[
  {
    "path": ".clang-format",
    "content": "# yaml-language-server: $schema=https://json.schemastore.org/clang-format.json\n---\nLanguage:        Cpp\nAccessModifierOffset: -2\nAlignAfterOpenBracket: Align\nAlignArrayOfStructures: None\nAlignConsecutiveAssignments:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveBitFields:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveDeclarations:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveMacros:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveShortCaseStatements:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCaseArrows: false\n  AlignCaseColons: false\nAlignConsecutiveTableGenBreakingDAGArgColons:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveTableGenCondOperatorColons:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignConsecutiveTableGenDefinitionColons:\n  Enabled:         false\n  AcrossEmptyLines: false\n  AcrossComments:  false\n  AlignCompound:   false\n  AlignFunctionPointers: false\n  PadOperators:    false\nAlignEscapedNewlines: DontAlign\nAlignOperands:   Align\nAlignTrailingComments:\n  Kind:            Always\n  OverEmptyLines:  0\nAllowAllArgumentsOnNextLine: true\nAllowAllParametersOfDeclarationOnNextLine: true\nAllowBreakBeforeNoexceptSpecifier: Never\nAllowShortBlocksOnASingleLine: Never\nAllowShortCaseExpressionOnASingleLine: true\nAllowShortCaseLabelsOnASingleLine: false\nAllowShortCompoundRequirementOnASingleLine: true\nAllowShortEnumsOnASingleLine: true\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: Never\nAllowShortLambdasOnASingleLine: All\nAllowShortLoopsOnASingleLine: false\nAlwaysBreakAfterDefinitionReturnType: None\nAlwaysBreakBeforeMultilineStrings: false\nAttributeMacros:\n  - __capability\nBinPackArguments: false\nBinPackParameters: false\nBitFieldColonSpacing: Both\nBraceWrapping:\n  AfterCaseLabel:  false\n  AfterClass:      true\n  AfterControlStatement: Never\n  AfterEnum:       false\n  AfterExternBlock: false\n  AfterFunction:   false\n  AfterNamespace:  false\n  AfterObjCDeclaration: false\n  AfterStruct:     true\n  AfterUnion:      false\n  BeforeCatch:     false\n  BeforeElse:      false\n  BeforeLambdaBody: false\n  BeforeWhile:     false\n  IndentBraces:    false\n  SplitEmptyFunction: false\n  SplitEmptyRecord: false\n  SplitEmptyNamespace: false\nBreakAdjacentStringLiterals: true\nBreakAfterAttributes: Leave\nBreakAfterJavaFieldAnnotations: false\nBreakAfterReturnType: AllDefinitions\nBreakArrays:     true\nBreakBeforeBinaryOperators: All\nBreakBeforeConceptDeclarations: Always\nBreakBeforeBraces: Custom\nBreakBeforeInlineASMColon: OnlyMultiline\nBreakBeforeTernaryOperators: true\nBreakConstructorInitializers: BeforeComma\nBreakFunctionDefinitionParameters: false\nBreakInheritanceList: BeforeColon\nBreakStringLiterals: true\nBreakTemplateDeclarations: Yes\nColumnLimit:     100\nCommentPragmas:  '^ IWYU pragma:'\nCompactNamespaces: false\nConstructorInitializerIndentWidth: 2\nContinuationIndentWidth: 2\nCpp11BracedListStyle: true\nDerivePointerAlignment: true\nDisableFormat:   false\nEmptyLineAfterAccessModifier: Never\nEmptyLineBeforeAccessModifier: LogicalBlock\nExperimentalAutoDetectBinPacking: false\nFixNamespaceComments: true\nForEachMacros:\n  - forever\n  - foreach\n  - Q_FOREACH\n  - BOOST_FOREACH\nIfMacros:\n  - KJ_IF_MAYBE\nIncludeBlocks:   Preserve\nIncludeCategories:\n  - Regex:           '^<Q.*'\n    Priority:        200\n    SortPriority:    200\n    CaseSensitive:   true\nIncludeIsMainRegex: '(Test)?$'\nIncludeIsMainSourceRegex: ''\nIndentAccessModifiers: false\nIndentCaseBlocks: false\nIndentCaseLabels: false\nIndentExternBlock: AfterExternBlock\nIndentGotoLabels: true\nIndentPPDirectives: None\nIndentRequiresClause: true\nIndentWidth:     2\nIndentWrappedFunctionNames: false\nInsertBraces:    false\nInsertNewlineAtEOF: false\nInsertTrailingCommas: None\nIntegerLiteralSeparator:\n  Binary:          0\n  BinaryMinDigits: 0\n  Decimal:         0\n  DecimalMinDigits: 0\n  Hex:             0\n  HexMinDigits:    0\nJavaScriptQuotes: Leave\nJavaScriptWrapImports: true\nKeepEmptyLines:\n  AtEndOfFile:     false\n  AtStartOfBlock:  false\n  AtStartOfFile:   false\nLambdaBodyIndentation: Signature\nLineEnding:      DeriveLF\nMacroBlockBegin: ''\nMacroBlockEnd:   ''\nMainIncludeChar: Quote\nMaxEmptyLinesToKeep: 1\nNamespaceIndentation: None\nObjCBinPackProtocolList: Auto\nObjCBlockIndentWidth: 2\nObjCBreakBeforeNestedBlockParam: true\nObjCSpaceAfterProperty: false\nObjCSpaceBeforeProtocolList: true\nPackConstructorInitializers: BinPack\nPenaltyBreakAssignment: 150\nPenaltyBreakBeforeFirstCallParameter: 300\nPenaltyBreakComment: 500\nPenaltyBreakFirstLessLess: 400\nPenaltyBreakOpenParenthesis: 0\nPenaltyBreakScopeResolution: 500\nPenaltyBreakString: 600\nPenaltyBreakTemplateDeclaration: 10\nPenaltyExcessCharacter: 50\nPenaltyIndentedWhitespace: 0\nPenaltyReturnTypeOnItsOwnLine: 300\nPointerAlignment: Right\nPPIndentWidth:   -1\nQualifierAlignment: Leave\nReferenceAlignment: Pointer\nReflowComments:  false\nRemoveBracesLLVM: false\nRemoveParentheses: Leave\nRemoveSemicolon: false\nRequiresClausePosition: OwnLine\nRequiresExpressionIndentation: OuterScope\nSeparateDefinitionBlocks: Leave\nShortNamespaceLines: 1\nSkipMacroDefinitionBody: false\nSortIncludes:    CaseSensitive\nSortJavaStaticImport: Before\nSortUsingDeclarations: Lexicographic\nSpaceAfterCStyleCast: true\nSpaceAfterLogicalNot: false\nSpaceAfterTemplateKeyword: false\nSpaceAroundPointerQualifiers: Default\nSpaceBeforeAssignmentOperators: true\nSpaceBeforeCaseColon: false\nSpaceBeforeCpp11BracedList: false\nSpaceBeforeCtorInitializerColon: true\nSpaceBeforeInheritanceColon: true\nSpaceBeforeJsonColon: false\nSpaceBeforeParens: ControlStatements\nSpaceBeforeParensOptions:\n  AfterControlStatements: true\n  AfterForeachMacros: true\n  AfterFunctionDefinitionName: false\n  AfterFunctionDeclarationName: false\n  AfterIfMacros:   true\n  AfterOverloadedOperator: false\n  AfterPlacementOperator: true\n  AfterRequiresInClause: false\n  AfterRequiresInExpression: false\n  BeforeNonEmptyParentheses: false\nSpaceBeforeRangeBasedForLoopColon: true\nSpaceBeforeSquareBrackets: false\nSpaceInEmptyBlock: false\nSpacesBeforeTrailingComments: 1\nSpacesInAngles:  Never\nSpacesInContainerLiterals: false\nSpacesInLineCommentPrefix:\n  Minimum:         1\n  Maximum:         -1\nSpacesInParens:  Never\nSpacesInParensOptions:\n  ExceptDoubleParentheses: false\n  InCStyleCasts:   false\n  InConditionalStatements: false\n  InEmptyParentheses: false\n  Other:           false\nSpacesInSquareBrackets: false\nStandard:        Auto\nStatementAttributeLikeMacros:\n  - Q_EMIT\n  - emit\nStatementMacros:\n  - Q_UNUSED\n  - QT_REQUIRE_VERSION\n  - Q_CLASSINFO\n  - Q_ENUM\n  - Q_ENUM_NS\n  - Q_FLAG\n  - Q_FLAG_NS\n  - Q_GADGET\n  - Q_GADGET_EXPORT\n  - Q_INTERFACES\n  - Q_LOGGING_CATEGORY\n  - Q_MOC_INCLUDE\n  - Q_NAMESPACE\n  - Q_NAMESPACE_EXPORT\n  - Q_OBJECT\n  - Q_PROPERTY\n  - Q_REVISION\n  - Q_DISABLE_COPY\n  - Q_SET_OBJECT_NAME\n  - QT_BEGIN_NAMESPACE\n  - QT_END_NAMESPACE\n  - QML_ADDED_IN_MINOR_VERSION\n  - QML_ANONYMOUS\n  - QML_ATTACHED\n  - QML_DECLARE_TYPE\n  - QML_DECLARE_TYPEINFO\n  - QML_ELEMENT\n  - QML_EXTENDED\n  - QML_EXTENDED_NAMESPACE\n  - QML_EXTRA_VERSION\n  - QML_FOREIGN\n  - QML_FOREIGN_NAMESPACE\n  - QML_IMPLEMENTS_INTERFACES\n  - QML_INTERFACE\n  - QML_NAMED_ELEMENT\n  - QML_REMOVED_IN_MINOR_VERSION\n  - QML_SINGLETON\n  - QML_UNAVAILABLE\n  - QML_UNCREATABLE\n  - QML_VALUE_TYPE\n#TableGenBreakInsideDAGArg: DontBreak\nTabWidth:        2\nUseTab:          Never\nVerilogBreakBetweenInstancePorts: true\nWhitespaceSensitiveMacros:\n  - BOOST_PP_STRINGIZE\n  - CF_SWIFT_NAME\n  - NS_SWIFT_NAME\n  - PP_STRINGIZE\n  - STRINGIZE\n...\n\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\nthanks_dev: # Replace with a single thanks.dev username\ncustom: ['https://paypal.me/dm3mat'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/communication-issue.md",
    "content": "---\nname: Communication issue\nabout: If qdmr/dmrconf fails to detect a device\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\nIf you cannot detect or read a device, please include your report\n - [ ] Model name and firmware version\n - [ ] qdmr version and source (e.g., self-build or installed as a package).\n - [ ] Output of `groups`\n - [ ] Output of `lsusb` or output of `dmesg` after plugging the device in.\n - [ ] Output of `dmrconf --verbose detect`\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/decoding-issue.md",
    "content": "---\nname: Decoding issue\nabout: If qdmr fails to read/decode a codeplug from a device.\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\nFirst, check if qdmr/dmrconf can read the device. That is, read the binary codeplug one the command-line using \n```\n > dmrcond read --verbose DEVICE_codeplug.dfu\n```\nIf this succeeds, qdmr/dmrconf can talk to the device just fine. To help with identifying the decoding issue, consider adding this binary codeplug to the issue. You may need to zip it first. \n\nAlso report your qdmr version, the device firmware version and the output of\n```\n > dmrconf decode --verbose --radio=YOUR_DEVICE DFU_FILE_NAME decoded.yaml\n```\n"
  },
  {
    "path": ".github/workflows/clang.yml",
    "content": "name: build-linux-clang-14\n\non:\n  push:\n    branches: [ \"master\" ]\n  pull_request:\n    branches: [ \"master\" ]\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n  CC: /usr/bin/clang-14\n  CXX: /usr/bin/clang++-14\n\njobs:\n  build:\n    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.\n    # You can convert this to a matrix build if you need cross-platform coverage.\n    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Update APT\n      run: sudo apt-get update\n      \n    - name: Install Dependencies\n      run: sudo apt-get install clang-14 qt6-tools-dev qt6-base-dev qt6-base-dev-tools qt6-serialport-dev qt6-positioning-dev qt6-multimedia-dev libusb-1.0-0-dev libyaml-cpp-dev librsvg2-bin\n      \n    - name: Configure CMake\n      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.\n      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}\n\n    - name: Build\n      # Build your program with the given configuration\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n\n"
  },
  {
    "path": ".github/workflows/flatpak.yml",
    "content": "on:\n  release:\n    types: [published]\n    \nname: Flatpak\n\njobs:\n  flatpak:\n    name: \"Flatpak\"\n    runs-on: ubuntu-latest\n    container:\n      image: ghcr.io/flathub-infra/flatpak-github-actions:kde-6.9\n      options: --privileged\n    steps:\n    - uses: actions/checkout@v4\n    - uses: flatpak/flatpak-github-actions/flatpak-builder@v6\n      with:\n        bundle: qdmr.flatpak\n        manifest-path: de.darc.dm3mat.qdmr.yaml\n        cache-key: flatpak-builder-${{ github.sha }}\n    - name: upload linux artifact\n      uses: softprops/action-gh-release@v1\n      if: ${{startsWith(github.ref, 'refs/tags/') }}\n      with:\n          files: qdmr-x86_64.flatpak.zip \n"
  },
  {
    "path": ".github/workflows/unittests.yml",
    "content": "name: unittest-linux-gcc\n\non:\n  push:\n    branches: [ \"master\", \"devel\" ]\n  pull_request:\n    branches: [ \"master\", \"devel\" ]\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  build:\n    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.\n    # You can convert this to a matrix build if you need cross-platform coverage.\n    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n\n    - name: Update APT\n      run: sudo apt-get update\n      \n    - name: Install Dependencies\n      run: sudo apt-get install qt6-tools-dev qt6-base-dev qt6-base-dev-tools qt6-serialport-dev qt6-positioning-dev qt6-multimedia-dev libusb-1.0-0-dev libyaml-cpp-dev librsvg2-bin\n      \n    - name: Configure CMake\n      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.\n      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=On\n\n    - name: Build\n      # Build your program with the given configuration\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n    - name: Test\n      working-directory: ${{github.workspace}}/build\n      # Execute tests defined by the CMake configuration.\n      # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n      run: ctest -C ${{env.BUILD_TYPE}}\n\n"
  },
  {
    "path": ".gitignore",
    "content": "/doc/html/\n/doc/latex/\n/doc/reveng/cotre/*.pcapng.gz\n/doc/reveng/gd77/*.pcapng.gz\n/doc/manual/manual.pdf\n/doc/manual/manual_combined.xml\n/doc/manual/intro/fig/*.aux\n/doc/manual/intro/fig/*.log\n/doc/manual/intro/fig/*.png\n/doc/manual/html/*.html\n/doc/manual/html/manual.xml\n/doc/manual/html/docbook.css\n/doc/manual/html/fig\n/parts/\n/prime/\n/snap/\n/stage/\nCMakeLists.txt.user\n/build/\n"
  },
  {
    "path": ".gitmodules",
    "content": ""
  },
  {
    "path": "BUILD.md",
    "content": "# Building `qdmr`\n\n## Overview\n\n`qdmr` is a Qt6-based application. This document provides instructions to build the project and details the required dependencies for Ubuntu/Debian, Fedora, Arch Linux, and macOS (with Homebrew).\n\n---\n\n## Dependencies\n\nThe following dependencies are required:\n\n- `qt6-base-dev`\n- `qt6-tools-dev`\n- `qt6-tools-dev-tools`\n- `qt6-positioning-dev`\n- `qt6-serialport-dev`\n- `qt6-multimedia-dev`\n- `libyaml-cpp-dev`\n- `librsvg2-bin`\n- `libusb-1.0-0-dev`\n\nEquivalent packages are listed for Fedora, Arch Linux, and macOS.\n\n---\n\n## 1. Ubuntu / Debian\n\n### **Install Dependencies**\n\n```sh\nsudo apt update\nsudo apt install -y \\\n  qt6-base-dev qt6-tools-dev qt6-tools-dev-tools \\\n  qt6-positioning-dev qt6-serialport-dev qt6-multimedia-dev \\\n  libyaml-cpp-dev librsvg2-bin libusb-1.0-0-dev \\\n  build-essential cmake git\n```\n\n---\n\n## 2. Fedora\n\n### **Install Dependencies**\n\n```sh\nsudo dnf install -y \\\n  qt6-qtbase-devel qt6-qttools-devel qt6-qtpositioning-devel qt6-qtserialport-devel qt6-multimedia-devel\\\n  yaml-cpp-devel librsvg2-tools libusb1-devel \\\n  make gcc-c++ cmake git\n```\n\n---\n\n## 3. Arch Linux\n\n### **Install Dependencies**\n\n```sh\nsudo pacman -S --needed \\\n  qt6-base qt6-tools qt6-positioning qt6-serialport qt6-multimedia \\\n  yaml-cpp librsvg libusb cmake git base-devel\n```\n\n---\n\n## 4. macOS (Homebrew)\n\n### **Install Dependencies**\n\n```sh\nbrew install \\\n  qt@6 yaml-cpp librsvg libusb cmake git\n```\n\n> **Note:** You may need to set environment variables for Qt6 tools if Homebrew does not symlink them into your PATH:\n>\n> ```sh\n> export PATH=\"/opt/homebrew/opt/qt@6/bin:$PATH\"\n> export LDFLAGS=\"-L/opt/homebrew/opt/qt@6/lib\"\n> export CPPFLAGS=\"-I/opt/homebrew/opt/qt@6/include\"\n> ```\n\n---\n\n## Build Instructions (All Platforms)\n\n1. **Clone the repository**\n\n   ```sh\n   git clone https://github.com/hmatuschek/qdmr.git\n   cd qdmr\n   ```\n\n2. **Create a build directory**\n\n   ```sh\n   mkdir build\n   cd build\n   ```\n\n3. **Configure the project**\n\n   On some systems, you may need to specify the location of your Qt6 installation:\n   ```sh\n   cmake .. -DQt6_ROOT=/path/to/qt6\n   ```\n   The `Qt6_ROOT` specifies the base-path for searching Qt6 related dependencies.\n\n   > On macOS, you may need to help CMake find Qt6:\n   > ```sh\n   > cmake .. -DQt6_ROOT=$(brew --prefix qt@6)\n   > ```\n\n   If you want to specify an install prefix , run e.g. \n   ```sh\n   cmake .. -DCMAKE_INSTALL_PREFIX=~/.local/\n   ```\n   The latter will prepare an user-local installation.\n\n4. **Build the project**\n\n   ```sh\n   cmake --build .\n   ```\n\n5. **(Optional) Run the application**\n\n   ```sh\n   ./src/qdmr\n   ```\n\n6. **(Optional) Install**\n\n   ```sh \n   cmake --install .\n   ```\n\n   This installs everything under the aforementioned install prefix.\n\n---\n\n## Troubleshooting\n\n- **Missing Packages:** Ensure all dependencies are installed as per your OS section.\n- **CMake Can't Find Qt6:** Double-check your `PATH`, `Qt6_ROOT`, and Qt6 installation.\n- **Permission Errors:** Try using `sudo` if needed for installation steps, but not for building the project.\n\n---\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.25.0)\nproject(qdmr VERSION 0.15.0)\n\nset(RELEASE_SUFFIX \"\")\n\ncmake_policy(SET CMP0100 NEW)\ncmake_policy(SET CMP0140 NEW)\n\nset(CMAKE_C_STANDARD 99)\nset(CMAKE_CXX_STANDARD 14)\nset(CMAKE_CXX_STANDARD_REQUIRED ON)\n\n\nset(DOCBOOK2MAN_XSLT_DEFAULT \"docbook_man.debian.xsl\")\nif(UNIX AND APPLE)\n  set(DOCBOOK2MAN_XSLT_DEFAULT \"docbook_man.macports.xsl\")\nendif(UNIX AND APPLE)\n\noption(BUILD_TESTS \"Build test programs\" OFF)\noption(BUILD_DOCS  \"Build API documentation\" OFF)\noption(BUILD_MAN   \"Build man page for dmrconf\" OFF)\noption(INSTALL_UDEV_RULES \"Install udev rules file.\" ON)\noption(INSTALL_UDEV_PATH \"Install path of udev rules file.\" \"/etc/udev/rules.d\")\noption(INSTALL_APPSTREAM_DATA \"Install AppStream metainfo file.\" ON)\noption(INSTALL_BUNDLE \"Installs QDMR as an AppBundle under MacOS X\" OFF)\noption(BUNDLE_PATH \"Where to install the MacOS X application bundle.\" \"~/Applications\")\noption(DOCBOOK2MAN_XSLT \"Man page style sheet.\" ${DOCBOOK2MAN_XSLT_DEFAULT})\n\nset(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake)\n\ninclude(GNUInstallDirs)\ninclude(GenerateIcons)\n\nfind_package(Qt6 REQUIRED COMPONENTS Core Widgets UiTools Network SerialPort Positioning\n  Concurrent LinguistTools Multimedia)\nqt6_standard_project_setup()\n\nfind_package(LIBUSB_1 REQUIRED)\nfind_package(YAMLCPP REQUIRED)\nfind_program(CONVERT_CMD rsvg-convert DOC \"SVG conversion tool (librsvg).\" REQUIRED)\n\nset(CMAKE_AUTOMOC ON)\nset(CMAKE_AUTOUIC ON)\nset(CMAKE_AUTORCC ON)\nset(USE_FOLDERS ON)\n\n\nif (BUILD_MAN)\n  find_program(XSLTPROC_EXECUTABLE NAMES xsltproc)\nendif()\n\nif (BUILD_DOCS)\n  find_package(Doxygen REQUIRED dot)\nendif()\n\nif (${BUILD_TESTS})\n  find_package(Qt6 REQUIRED COMPONENTS Test)\n  enable_testing(true)\nendif(${BUILD_TESTS})\n\nIF (UNIX AND APPLE)\n  SET(ADDITIONAL_LIBS ${ADDITIONAL_LIBS} \"-framework IOKit -framework CoreFoundation\")\nENDIF(UNIX AND APPLE)\n\nINCLUDE_DIRECTORIES(${LIBUSB_1_INCLUDE_DIRS})\nINCLUDE_DIRECTORIES(${YAMLCPP_INCLUDE_DIRS})\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src)\nINCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/lib)\nINCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}/lib)\n\nLINK_DIRECTORIES(${PROJECT_BINARY_DIR}/src)\n\n\nset(PROJECT_VERSION_STRING \"\\\"${PROJECT_VERSION}\\\"\")\nmessage(STATUS \"Build version ${PROJECT_VERSION}\")\n\n# Set compiler flags\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wall -Wsign-compare\")\nset(CMAKE_CXX_FLAGS_DEBUG   \"${CMAKE_CXX_FLAGS_DEBUG}  -O0 -ggdb -fstack-protector -Wextra\")\nset(CMAKE_CXX_FLAGS_RELEASE \"${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wextra\")\n\n\n# Get default install directories under Linux\nIF(UNIX AND NOT APPLE)\n INCLUDE(GNUInstallDirs)\nENDIF(UNIX AND NOT APPLE)\nIF(UNIX AND APPLE)\n SET(CMAKE_INSTALL_LIBDIR \"lib\")\n SET(CMAKE_INSTALL_FULL_LIBDIR \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}\")\n SET(CMAKE_INSTALL_INCLUDEDIR \"include\")\n SET(CMAKE_INSTALL_FULL_INCLUDEDIR \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}\")\n # Set RPATH under MacOS\n SET(CMAKE_SKIP_RPATH FALSE)\n SET(CMAKE_SKIP_BUILD_RPATH  FALSE)\n SET(CMAKE_SKIP_INSTALL_RPATH FALSE)\n SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)\n SET(CMAKE_INSTALL_RPATH \"${CMAKE_INSTALL_PREFIX}/lib\")\n SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)\n message(STATUS \"RPATH: ${CMAKE_INSTALL_RPATH}\")\nENDIF(UNIX AND APPLE)\n\n\n# Sources...\nadd_subdirectory(lib)\nadd_subdirectory(cli)\nadd_subdirectory(src)\nadd_subdirectory(doc)\nadd_subdirectory(dist)\n\nif(BUILD_TESTS)\n enable_testing()\n add_subdirectory(test)\nendif(BUILD_TESTS)\n\n# Source distribution packages:\nset(CPACK_SOURCE_GENERATOR \"TGZ\")\nset(CPACK_SOURCE_PACKAGE_FILE_NAME\n  \"${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}${RELEASE_SUFFIX}\")\nset(CPACK_SOURCE_IGNORE_FILES\n  \"/build/;/doc/html;/doc/latex;/doc/reveng;/doc/dmr-intro;/examples/*.dfu;/.git/;~$;.qm;*.user$;${CPACK_SOURCE_IGNORE_FILES}\")\n\ninclude(CPack)\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "# QDMR, a GUI application and command line tool to program DMR radios\n<a href=\"https://translate.codeberg.org/projects/qdmr/\">\n <img src=\"https://translate.codeberg.org/widgets/qdmr/-/graphical-user-interface/svg-badge.svg\"\n      alt=\"Translation status\">\n</a>\n\n![qdmr channel editor](https://raw.githubusercontent.com/hmatuschek/qdmr/master/doc/fig/qdmr-channels.png \"The qdmr CPS software.\")\n\n*qdmr* is a graphical user interface (GUI) application that allows to program several types of DMR radios.\nTo this end, it aims at being a more universal codeplug programming software (CPS) compared to the\ndevice and even revision specific CPSs provided by the manufacturers. The goal of this project is to\nprovide a **single**, **comfortable**, [**well-documented**](https://static.dm3mat.de/qdmr/manual/) \nand **platform-independent** CPS for several types of (mainly Chinese) DMR radios.\n\n## Supported Radios \nCurrently, there are only few radios that are supported\n\n  * OpenGD77, OpenRD5R, etc. firmware (since version 0.4.0)\n  * OpenUV390, OpenDM1701, etc. firmware (since version 0.13.0)\n  * Radioddity GD77 (since version 0.8.1)\n  * Radioddity GD73 (since version 0.12.0)\n  * Baofeng/Radioddity RD-5R & RD-5R+ (since version 0.2.0)\n  * TYT MD-390 / Retevis RT8 (since version 0.9.0)\n  * TYT MD-UV380 (since version 0.9.0)\n  * TYT MD-UV390 / Retevis RT3S (since version 0.3.0)\n  * TYT MD-2017 / Retevis RT82 (since version 0.9.0)\n  * Anytone AT-D878UV (since version 0.5.0)\n  * Anytone AT-D868UVE (since version 0.7.0)\n  * Anytone AT-D878UVII (since version 0.8.0)\n  * Anytone AT-D578UV (since version 0.8.0)\n  * Anytone AT-D578UV II (since version 0.13.0)\n  * BTECH DM-1701 / Retevis RT84 (since version 0.10.0)\n  * BTECH BF-1801A6 (since version 0.12.0)\n  * BTECH DMR-6x2 (since version 0.11.0)\n  * BTECH DMR-6x2 PRO (since version 0.13.0)\n  * Baofeng DM-32UV (since version 0.14.0)\n\nA more [detailed list](https://dm3mat.de/software/qdmr) is also available. \n\n\n## Questions?\n\n  * Have a look at the [discussions](https://github.com/hmatuschek/qdmr/discussions), maybe your questions has already been answered.\n  * There is a *Matrix* chat at [#qdmr:darc.de](https://matrix.to/#/#qdmr:darc.de).\n  * You can also follow me at [mastodon](https://mastodon.radio/@dm3mat), where I usually announce new releases.\n\n\n## Want to help?\n\n  * 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). \n  * If you want to help translating qdmr in your language, checkout [qdmr's weblate project](https://translate.codeberg.org/projects/qdmr/graphical-user-interface/).\n\n\n## Ecosystem\nAs *qdmr* gets more and more popular with Linux HAMs, the ecosystem around it grows too. These are \ntools, that make your day-to-day usage of *qdmr* easier, by providing features, not covered in \nqdmr or dmrconf.\n\n  * **[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.\n  * **[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/).\n  \n\n## Releases\n<a href=\"https://repology.org/project/qdmr/versions\">\n <img src=\"https://repology.org/badge/vertical-allrepos/qdmr.svg\" alt=\"Packaging status\" align=\"right\">\n</a>\n\n * **[Version 0.14.1](https://github.com/hmatuschek/qdmr/releases/tag/v0.14.0)** -- Bugfix release.\n * **[Version 0.14.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.14.0)** -- Added support for Baofeng DM-32UV, added AM channels.\n * **[Version 0.13.3](https://github.com/hmatuschek/qdmr/releases/tag/v0.13.3)** -- Added support BTech DMR6X2PRO and OpenUV390.\n * **[Version 0.12.3](https://github.com/hmatuschek/qdmr/releases/tag/v0.12.3)** -- Added support BTech DR-1801UV (A6, still unstable) and Radioddity GD-73.\n * **[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.\n * **[Version 0.10.4](https://github.com/hmatuschek/qdmr/releases/tag/v0.10.4)** -- Added support for BTech DM1701, some bugfixes.\n * **[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.\n * **[Version 0.8.1](https://github.com/hmatuschek/qdmr/releases/tag/v0.8.1)** -- Fixed Radioddity GD-77 support (callsign db still buggy).\n * **[Version 0.7.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.7.0)** -- Added AT-D868UVE support and many bugfixes.\n * **[Version 0.6.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.6.4)** -- Added APRS & roaming for AT-D878UV.\n * **[Version 0.5.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.5.0)** -- Added support for Anytone AT-D878UV.\n * **[Version 0.4.0](https://github.com/hmatuschek/qdmr/releases/tag/v0.4.0)** -- Added Open GD77 support.\n * **[Version 0.2.1](https://github.com/hmatuschek/qdmr/releases/tag/v0.2.1)** -- First public release.\n\n\n## Install\nThere 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.de/software/qdmr/install). Some distributions (see badge above) already added qdmr, thus easing the install using the system package manager.\n\n## License\nqdmr - A GUI application and command-line-tool to program DMR radios.\nCopyright (C) 2019-2026 Hannes Matuschek, DM3MAT\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License along\nwith this program; if not, write to the Free Software Foundation, Inc.,\n51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n"
  },
  {
    "path": "cli/CMakeLists.txt",
    "content": "qt_add_executable(dmrconf WIN32\n  main.cc\n  printprogress.cc printprogress.hh\n  detect.cc detect.hh\n  verify.cc verify.hh\n  readcodeplug.cc readcodeplug.hh\n  writecodeplug.cc writecodeplug.hh\n  encodecodeplug.cc encodecodeplug.hh\n  decodecodeplug.cc decodecodeplug.hh\n  infofile.cc infofile.hh\n  writecallsigndb.cc writecallsigndb.hh\n  encodecallsigndb.cc encodecallsigndb.hh\n  progressbar.cc progressbar.hh\n  autodetect.cc autodetect.hh)\n\ntarget_link_libraries(\n  dmrconf PRIVATE Qt6::Core Qt6::SerialPort Qt6::Network Qt6::Positioning\n                  ${LIBUSB_1_LIBRARIES} ${YAMLCPP_LIBRARIES} libdmrconf)\n\ninstall(TARGETS dmrconf DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})\n"
  },
  {
    "path": "cli/autodetect.cc",
    "content": "#include \"autodetect.hh\"\n\n#include \"logger.hh\"\n#include \"radioinfo.hh\"\n#include \"usbdevice.hh\"\n\nQVariant\nparseDeviceHandle(const QString &device) {\n  QRegularExpression pattern(\"([0-9]+):([0-9]+)\");\n  auto match = pattern.match(device.simplified());\n  if (match.hasMatch()) {\n    return QVariant::fromValue(USBDeviceHandle(match.captured(1).toUInt(), match.captured(2).toUInt()));\n  }\n  return QVariant(device.simplified());\n}\n\nvoid\nprintDevices(QTextStream &out, const QList<USBDeviceDescriptor> &devices) {\n  foreach (USBDeviceDescriptor device, devices) {\n    if (USBDeviceInfo::Class::None == device.interfaceClass())\n      continue;\n    out << \"Device '\";\n    if (USBDeviceInfo::Class::Serial == device.interfaceClass()) {\n      out << device.device().toString() << \"'\\n\";\n    } else if (USBDeviceInfo::Class::DFU == device.interfaceClass()) {\n      USBDeviceHandle addr = device.device().value<USBDeviceHandle>();\n      out << QString(\"%1:%2\").arg(addr.bus).arg(addr.device) << \"'\\n\";\n    } else if (USBDeviceInfo::Class::HID == device.interfaceClass()) {\n      USBDeviceHandle addr = device.device().value<USBDeviceHandle>();\n      out << QString(\"%1:%2\").arg(addr.bus).arg(addr.device) << \"'\\n\";\n    }\n    out << \" Type:        \" << device.description() << \"\\n\";\n    out << \" Description: \" << device.longDescription() << \"\\n\";\n  }\n}\n\nRadio *\nautoDetect(QCommandLineParser &parser, QCoreApplication &app, const ErrorStack &err) {\n  Q_UNUSED(app)\n\n  logDebug() << \"Autodetect radios.\";\n\n  QList<USBDeviceDescriptor> interfaces = USBDeviceDescriptor::detect();\n  if (interfaces.isEmpty())\n    interfaces = USBDeviceDescriptor::detect(false);\n\n  if (interfaces.isEmpty()) {\n    errMsg(err) << \"No matching USB devices are found. Check connection?\";\n    return nullptr;\n  }\n\n  logInfo() << \"Found \" << interfaces.count() << \" device(s):\";\n  foreach (USBDeviceDescriptor d, interfaces) {\n    logInfo() << \"  \" << d.description() << \".\";\n  }\n\n  USBDeviceDescriptor device;\n  if (parser.isSet(\"device\")) {\n    // If a device is passed by option, search for matching handle\n    QVariant devHandle = parseDeviceHandle(parser.value(\"device\"));\n    foreach (USBDeviceDescriptor dev, interfaces) {\n      if (dev.device() == devHandle) {\n        device = dev;\n        break;\n      }\n    }\n    if (! device.isValid()) {\n      ErrorStack::MessageStream msg(err, __FILE__, __LINE__);\n      msg << \"Device handle '\" << parser.value(\"device\") << \"' not found in:\\n\";\n      printDevices(msg, interfaces);\n      return nullptr;\n    }\n  } else if (1 != interfaces.size()) {\n    // If no device is specified, there should only be one interface\n    ErrorStack::MessageStream msg(err, __FILE__, __LINE__);\n    msg << \"Cannot auto-detect radio, more than one matching USB devices found:\"\n        << \" Use --device option to specify to which device to talk to. Devices found:\\n\";\n    printDevices(msg, interfaces);\n    return nullptr;\n  } else if (! interfaces.first().isSave()) {\n    ErrorStack::MessageStream msg(err, __FILE__, __LINE__);\n    msg << \"It is not save to assume that the device:\\n\";\n    printDevices(msg, interfaces);\n    msg << \"is a DMR radio. Please specify the device explicitly to verify correctness.\";\n    return nullptr;\n  } else {\n    // The first device is safe to use\n    device = interfaces.first();\n  }\n\n  logDebug() << \"Using device \" << device.deviceHandle() << \".\";\n\n  // Handle identifiability of radio\n  if (parser.isSet(\"radio\")) {\n    RadioInfo radio = RadioInfo::byKey(parser.value(\"radio\").toLower());\n    if (! radio.isValid()) {\n      errMsg(err) << \"Unknown radio '\" << parser.value(\"radio\").toLower() << \"'.\";\n      return nullptr;\n    }\n    Radio *rad = Radio::detect(device, radio, err);\n    if (nullptr == rad) {\n      logError() << \"Cannot detect radio.\";\n      return nullptr;\n    }\n    return rad;\n  } else if (! device.isSave()) {\n    // Collect all radio keys for the device\n    QStringList radios;\n    foreach (RadioInfo info, RadioInfo::allRadios(device)) {\n      radios.append(info.key());\n    }\n    errMsg(err) << \"It is not save or possible to identify the radio connected to the device '\"\n                << device.deviceHandle() << \". You have to specify which radio to use using \"\n                << \"the --radio option. Possible radios for this device are \"\n                << radios.join(\", \") << \".\";\n    return nullptr;\n  }\n\n  // Try auto-detect:\n  Radio *rad = Radio::detect(device, RadioInfo(), err);\n  if (nullptr == rad) {\n    errMsg(err) << \"Cannot auto-detect radio.\";\n    return nullptr;\n  }\n  return rad;\n}\n"
  },
  {
    "path": "cli/autodetect.hh",
    "content": "#ifndef AUTODETECT_HH\n#define AUTODETECT_HH\n\n#include \"radio.hh\"\n#include <QCommandLineParser>\n#include <QCoreApplication>\n\n\nQVariant parseDeviceHandle(const QString &device);\nvoid printDevices(QTextStream &out, const QList<USBDeviceDescriptor> &devices);\nRadio *autoDetect(QCommandLineParser &parser, QCoreApplication &app, const ErrorStack &err=ErrorStack());\n\n#endif // AUTODETECT_HH\n"
  },
  {
    "path": "cli/decodecodeplug.cc",
    "content": "#include \"decodecodeplug.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QFile>\n#include <QFileInfo>\n\n#include \"logger.hh\"\n#include \"config.hh\"\n#include \"radioinfo.hh\"\n#include \"dummyfilereader.hh\"\n#include \"md390_codeplug.hh\"\n#include \"md390_filereader.hh\"\n#include \"uv390_codeplug.hh\"\n#include \"uv390_filereader.hh\"\n#include \"md2017_codeplug.hh\"\n#include \"md2017_filereader.hh\"\n#include \"dm1701_codeplug.hh\"\n#include \"dm1701_filereader.hh\"\n#include \"rd5r_codeplug.hh\"\n#include \"rd5r_filereader.hh\"\n#include \"gd77_codeplug.hh\"\n#include \"gd77_filereader.hh\"\n#include \"gd73_codeplug.hh\"\n#include \"gd73_filereader.hh\"\n#include \"opengd77_codeplug.hh\"\n#include \"openuv380_codeplug.hh\"\n#include \"openrtx_codeplug.hh\"\n#include \"d868uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"dmr6x2uv2_codeplug.hh\"\n#include \"dr1801uv_codeplug.hh\"\n#include \"dr1801uv_filereader.hh\"\n#include \"dm32uv_codeplug.hh\"\n\n\ntemplate <class Cpl, class Rdr>\nbool decode(Config &config, const QString &filename, QCommandLineParser &parser, const ErrorStack &err=ErrorStack()) {\n  Cpl codeplug;\n  if (parser.isSet(\"manufacturer\")) {\n    if (! Rdr::read(filename, &codeplug, err)) {\n      errMsg(err) << \"Cannot decode manufacturer codeplug file '\" << filename << \"'.\";\n      return false;\n    }\n  } else if (! codeplug.read(filename, err)) {\n    errMsg(err) << \"Cannot decode binary codeplug file '\" << filename << \"'.\";\n    return false;\n  }\n  if (! codeplug.decode(&config, err)) {\n    errMsg(err) << \"Cannot decode binary codeplug file '\" << filename << \"'.\";\n    return false;\n  }\n  if (! codeplug.postprocess(&config, err)) {\n    logError() << \"Cannot post-process binary codeplug file '\" << filename << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nint\ndecodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {\n  Q_UNUSED(app);\n\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  QString filename = parser.positionalArguments().at(1);\n  ErrorStack err;\n\n  if (! parser.isSet(\"radio\")) {\n    logError() << \"No radio type is specified! Use the --radio option.\";\n    return -1;\n  }\n\n  if (! RadioInfo::hasRadioKey(parser.value(\"radio\").toLower())) {\n    QStringList radios;\n    foreach (RadioInfo info, RadioInfo::allRadios())\n      radios.append(info.key());\n    logError() << \"Unknown radio '\" << parser.value(\"radio\").toLower() << \".\";\n    logError() << \"Known radios \" << radios.join(\", \") << \".\";\n    return -1;\n  }\n\n  RadioInfo::Radio radio = RadioInfo::byKey(parser.value(\"radio\").toLower()).id();\n  Config config;\n\n  switch (radio) {\n  case RadioInfo::MD390:\n    if (! decode<MD390Codeplug, MD390FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::UV390:\n    if (! decode<UV390Codeplug, UV390FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::MD2017:\n    if (! decode<MD2017Codeplug, MD2017FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::DM1701:\n    if (! decode<DM1701Codeplug, DM1701FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::RD5R:\n    if (! decode<RD5RCodeplug, RD5RFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::GD73:\n    if (! decode<GD73Codeplug, GD73FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::GD77:\n    if (! decode<GD77Codeplug, GD77FileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::OpenGD77:\n    if (! decode<OpenGD77Codeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::OpenUV380:\n    if (! decode<OpenUV380Codeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::OpenRTX:\n    if (! decode<OpenRTXCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::D868UVE:\n    if (! decode<D868UVCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::D878UV:\n    if (! decode<D878UVCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::D878UVII:\n    if (! decode<D878UV2Codeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::D578UV:\n    if (! decode<D578UVCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::DMR6X2UV:\n    if (! decode<DMR6X2UVCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::DMR6X2UV2:\n    if (! decode<DMR6X2UV2Codeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::DR1801UV:\n    if (! decode<DR1801UVCodeplug, DR1801UVFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  case RadioInfo::DM32UV:\n    if (! decode<DM32UVCodeplug, DummyFileReader>(config, filename, parser, err)) {\n      logError() << \"Cannot decode codeplug '\" << filename << \"':\\n\" << err.format();\n      return -1;\n    } break;\n  default:\n    logError() << \"Decoding not implemented for \" << RadioInfo::byID(radio).name() << \".\";\n    return -1;\n  }\n\n  if (3 <= parser.positionalArguments().size()) {\n    QFileInfo info(parser.positionalArguments().at(2));\n    if ((\"conf\" == info.suffix()) || (\"csv\" == info.suffix()) || parser.isSet(\"csv\")) {\n      logError() << \"Export of the old table based format was disabled with 0.9.0. \"\n                    \"Import still works.\";\n      return -1;\n    } else if ((\"yaml\" == info.suffix()) || parser.isSet(\"yaml\")) {\n      QFile outfile(info.filePath());\n      if (! outfile.open(QIODevice::WriteOnly)) {\n        logError() << \"Cannot write CSV codeplug file '\" << outfile.fileName()\n                   << \"':\\n\" << outfile.errorString();\n        return -1;\n      }\n      QTextStream stream(&outfile);\n      if (! config.toYAML(stream, err)) {\n        logError() << \"Cannot serialize codeplug to YAML:\\n\"\n                   << err.format(\" \");\n      }\n      outfile.close();\n    } else {\n      logError() << \"Cannot determine codeplug output file format. Consider using --csv or --yaml.\";\n      return -1;\n    }\n  } else {\n    QTextStream stream(stdout);\n    if (parser.isSet(\"csv\")) {\n      logError() << \"Export of the old table based format was disabled with 0.9.0. \"\n                    \"Import still works.\";\n      return -1;\n    } else if (parser.isSet(\"yaml\")) {\n      if (! config.toYAML(stream, err)) {\n        logError() << \"Cannot serialize codeplug into YAML:\\n\"\n                   << err.format(\" \");\n        return -1;\n      }\n    } else {\n      logError() << \"Cannot determine codeplug output file format. Consider using --csv or --yaml.\";\n      return -1;\n    }\n  }\n\n  return 0;\n}\n\n"
  },
  {
    "path": "cli/decodecodeplug.hh",
    "content": "#ifndef DECODECODEPLUG_HH\n#define DECODECODEPLUG_HH\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint decodeCodeplug(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // DECODECODEPLUG_HH\n"
  },
  {
    "path": "cli/detect.cc",
    "content": "#include \"detect.hh\"\n#include <QCommandLineParser>\n#include <QCoreApplication>\n\n#include \"logger.hh\"\n#include \"radio.hh\"\n#include \"radiointerface.hh\"\n#include \"autodetect.hh\"\n\nint detect(QCommandLineParser &parser, QCoreApplication &app) {\n  // Try to detect a radio\n  ErrorStack err;\n  Radio *radio = autoDetect(parser, app, err);\n  if (nullptr == radio) {\n    logError() << \"Cannot detect radio: \\n\" << err.format(\"  \");\n    return -1;\n  }\n\n  QTextStream out(stdout);\n  out << \"Found: \" << radio->name() << Qt::endl;\n  delete radio;\n\n  return 0;\n}\n\n\n"
  },
  {
    "path": "cli/detect.hh",
    "content": "#ifndef DETECT_HH\n#define DETECT_HH\n\nclass QCommandLineParser;\nclass QCoreApplication;\n\nint detect(QCommandLineParser &parser, QCoreApplication &app);\n\n\n#endif // DETECT_HH\n"
  },
  {
    "path": "cli/encodecallsigndb.cc",
    "content": "#include \"encodecodeplug.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QFile>\n\n#include \"logger.hh\"\n#include \"config.hh\"\n#include \"radioinfo.hh\"\n#include \"dm1701_callsigndb.hh\"\n#include \"uv390_callsigndb.hh\"\n#include \"md2017_callsigndb.hh\"\n#include \"opengd77_callsigndb.hh\"\n#include \"openuv380_callsigndb.hh\"\n#include \"gd77_callsigndb.hh\"\n#include \"d868uv_callsigndb.hh\"\n#include \"d878uv2_callsigndb.hh\"\n\n\nint encodeCallsignDB(QCommandLineParser &parser, QCoreApplication &app) {\n  Q_UNUSED(app);\n\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  UserDatabase userdb(false);\n  if (parser.isSet(\"database\")) {\n    if (! userdb.load(parser.value(\"database\"))) {\n      logError() << \"Cannot load user-db from '\" << parser.value(\"database\") << \"'.\";\n      return -1;\n    }\n  } else if (! userdb.ready()) {\n    logInfo() << \"Downloading call-sign DB...\";\n    // Wait for download to finish\n    QEventLoop loop;\n    QObject::connect(&userdb, SIGNAL(loaded()), &loop, SLOT(quit()));\n    QObject::connect(&userdb, SIGNAL(error(QString)), &loop, SLOT(quit()));\n    loop.exec();\n    // Check if call-sign DB has been loaded\n    if (0 == userdb.count()) {\n      logError() << \"Could not download/load call-sign DB.\";\n      return -1;\n    }\n  }\n\n  if (parser.isSet(\"id\")) {\n    QStringList prefixes_text = parser.value(\"id\").split(\",\");\n    QSet<unsigned> prefixes;\n    foreach (QString prefix_text, prefixes_text) {\n      bool ok=true; uint32_t prefix = prefix_text.toUInt(&ok);\n      if (ok)\n        prefixes.insert(prefix);\n    }\n\n    if (prefixes.isEmpty()) {\n      logError() << \"Please specify a valid DMR ID or a list of DMR prefixes for --id option.\";\n      return -1;\n    }\n    prefixes_text.clear();\n    foreach (unsigned prefix, prefixes) {\n      prefixes_text.append(QString::number(prefix));\n    }\n    logDebug() << \"Sort call-sign DB w.r.t. DMR ID(s) {\" << prefixes_text.join(\", \") << \"}.\";\n    userdb.sortUsers(prefixes);\n  } else {\n    logWarn() << \"No ID is specified, a more or less random set of call-signs will be used \"\n              << \"if the radio cannot hold the entire call-sign DB of \" << userdb.count()\n              << \" entries. Specify your DMR ID with --id=YOUR_DMR_ID. dmrconf will then \"\n              << \"select those entries 'closest' to you. I.e., DMR IDs with the same prefix.\";\n  }\n\n  CallsignDB::Flags selection;\n  if (parser.isSet(\"limit\")) {\n    bool ok=true;\n    selection.setCountLimit(parser.value(\"limit\").toUInt(&ok));\n    if (! ok) {\n      logError() << \"Please specify a valid limit for the number of callsign db entries using the -n/--limit option.\";\n      return -1;\n    }\n  }\n\n  if (! parser.isSet(\"radio\")) {\n    logError() << \"You have to specify the radio using the --radio option.\";\n    parser.showHelp(-1);\n    return -1;\n  }\n\n  if (! RadioInfo::hasRadioKey(parser.value(\"radio\").toLower())) {\n    QStringList radios;\n    foreach (RadioInfo info, RadioInfo::allRadios())\n      radios.append(info.key());\n    logError() << \"Unknown radio '\" << parser.value(\"radio\").toLower() << \".\";\n    logError() << \"Known radios \" << radios.join(\", \") << \".\";\n    return -1;\n  }\n\n  RadioInfo::Radio radio = RadioInfo::byKey(parser.value(\"radio\").toLower()).id();\n  ErrorStack err;\n\n  if (RadioInfo::UV390 == radio) {\n    UV390CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if (RadioInfo::MD2017 == radio) {\n    MD2017CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if (RadioInfo::DM1701 == radio) {\n    DM1701CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if (RadioInfo::OpenGD77 == radio) {\n    OpenGD77CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if (RadioInfo::OpenUV380 == radio) {\n    OpenUV380CallsignDB db(false);\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if (RadioInfo::GD77 == radio) {\n    GD77CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if ((RadioInfo::D868UVE == radio) || (RadioInfo::D878UV == radio) || (RadioInfo::DMR6X2UV == radio)){\n    D868UVCallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else if ((RadioInfo::D878UVII == radio) || (RadioInfo::D578UV == radio) || (RadioInfo::DMR6X2UV2 == radio)){\n    D878UV2CallsignDB db;\n    if (! db.encode(&userdb, selection, err)) {\n      logError() << \"Cannot encode call-sign DB: \" << err.format();\n      return -1;\n    }\n    if (! db.write(parser.positionalArguments().at(1), err)) {\n      logError() << \"Cannot write output call-sign DB file '\" << parser.positionalArguments().at(1)\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else {\n    logError() << \"Cannot encode calls-sign DB: Not implemented for '\" << parser.value(\"radio\") << \"'.\";\n    return -1;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "cli/encodecallsigndb.hh",
    "content": "#ifndef ENCODECALLSIGNDB_HH\n#define ENCODECALLSIGNDB_HH\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint encodeCallsignDB(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // ENCODECALLSIGNDB_HH\n"
  },
  {
    "path": "cli/encodecodeplug.cc",
    "content": "#include \"encodecodeplug.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QFile>\n#include <QFileInfo>\n\n#include \"logger.hh\"\n#include \"config.hh\"\n#include \"radioinfo.hh\"\n#include \"rd5r_codeplug.hh\"\n#include \"gd73_codeplug.hh\"\n#include \"gd77_codeplug.hh\"\n#include \"opengd77_codeplug.hh\"\n#include \"openuv380_codeplug.hh\"\n#include \"openrtx_codeplug.hh\"\n#include \"md390_codeplug.hh\"\n#include \"uv390_codeplug.hh\"\n#include \"md2017_codeplug.hh\"\n#include \"d868uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"d168uv_codeplug.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"dmr6x2uv2_codeplug.hh\"\n#include \"dr1801uv_codeplug.hh\"\n#include \"dm32uv_codeplug.hh\"\n\n\n\ntemplate <class T>\nbool encode(Config &config, Codeplug::Flags flags, QCommandLineParser &parser) {\n  ErrorStack err;\n  T codeplug;\n\n  Config *intermediate = codeplug.preprocess(&config, err);\n  if (nullptr == intermediate) {\n    logError() << \"Cannot pre-process codeplug: \" << err.format();\n    return false;\n  }\n\n  if (! codeplug.encode(intermediate, flags, err)) {\n    logError() << \"Cannot encode codeplug: \" << err.format();\n    delete intermediate;\n    return false;\n  }\n  delete intermediate;\n\n  codeplug.image(0).sort();\n  if (! codeplug.write(parser.positionalArguments().at(2), err)) {\n    logError() << \"Cannot write output codeplug file '\" << parser.positionalArguments().at(1)\n               << \"': \" << err.format();\n    return false;\n  }\n\n  return true;\n}\n\n\nint encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {\n  Q_UNUSED(app);\n\n  if (3 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  QFileInfo fileinfo(parser.positionalArguments().at(1));\n\n  if (! parser.isSet(\"radio\")) {\n    logError() << \"You have to specify the radio using the --radio option.\";\n    parser.showHelp(-1);\n    return -1;\n  }\n\n  if (! RadioInfo::hasRadioKey(parser.value(\"radio\").toLower())) {\n    QStringList radios;\n    foreach (RadioInfo info, RadioInfo::allRadios())\n      radios.append(info.key());\n    logError() << \"Unknown radio '\" << parser.value(\"radio\").toLower() << \".\";\n    logError() << \"Known radios \" << radios.join(\", \") << \".\";\n    return -1;\n  }\n\n  RadioInfo::Radio radio = RadioInfo::byKey(parser.value(\"radio\").toLower()).id();\n\n  Codeplug::Flags flags;\n  flags.setUpdateCodeplug(false);\n  if (parser.isSet(\"auto-enable-gps\"))\n    flags.setAutoEnableGPS(true);\n  if (parser.isSet(\"auto-enable-roaming\"))\n    flags.setAutoEnableRoaming(true);\n\n  Config config;\n  ErrorStack err;\n  if (parser.isSet(\"csv\") || (\"conf\" == fileinfo.suffix()) || (\"csv\" == fileinfo.suffix())) {\n    QString errorMessage;\n    QFile infile(fileinfo.canonicalFilePath());\n    if (! infile.open(QIODevice::ReadOnly)) {\n      logError() << \"Cannot encode CSV codeplug file '\" << fileinfo.fileName() << \"': \" << infile.errorString();\n      return -1;\n    }\n    QTextStream stream(&infile);\n    if (! config.readCSV(stream, errorMessage)) {\n      logError() << \"Cannot parse CSV codeplug '\" << infile.fileName() << \"': \" << errorMessage;\n      return -1;\n    }\n  } else if (parser.isSet(\"yaml\") || (\"yaml\" == fileinfo.suffix()) || (\"yml\" == fileinfo.suffix())) {\n    if (! config.readYAML(fileinfo.canonicalFilePath(), err)) {\n      logError() << \"Cannot parse YAML codeplug '\" << fileinfo.fileName()\n                 << \"':\\n\" << err.format(\" \");\n      return -1;\n    }\n  } else {\n    logError() << \"Cannot determine input file type, consider using --csv or --yaml.\";\n    return -1;\n  }\n\n  switch (radio) {\n  case RadioInfo::MD390:\n    if (! encode<MD390Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::UV390:\n    if (! encode<UV390Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::MD2017:\n    if (! encode<MD2017Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::DM1701:\n    if (! encode<DM1701Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::RD5R:\n    if (! encode<RD5RCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::GD73:\n    if (! encode<GD73Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::GD77:\n    if (! encode<GD77Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::OpenGD77:\n    if (! encode<OpenGD77Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::OpenUV380:\n    if (! encode<OpenUV380Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::OpenRTX:\n    if (! encode<OpenRTXCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::D868UVE:\n    if (! encode<D868UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::D878UV:\n    if (! encode<D878UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::D878UVII:\n    if (! encode<D878UV2Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::D578UV:\n    if (! encode<D578UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::D168UV:\n    if (! encode<D168UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::DMR6X2UV:\n    if (! encode<DMR6X2UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::DMR6X2UV2:\n    if (! encode<DMR6X2UV2Codeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::DR1801UV:\n    if (! encode<DR1801UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  case RadioInfo::DM32UV:\n    if (! encode<DM32UVCodeplug>(config, flags, parser))\n      return -1;\n    break;\n  default:\n    logError() << \"Cannot encode codeplug: Unknown radio '\" << parser.value(\"radio\") << \"'.\";\n    return -1;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "cli/encodecodeplug.hh",
    "content": "#ifndef ENCODECODEPLUG_HH\n#define ENCODECODEPLUG_HH\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint encodeCodeplug(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // ENCODECODEPLUG_HH\n"
  },
  {
    "path": "cli/infofile.cc",
    "content": "#include \"infofile.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QTextStream>\n\n#include \"logger.hh\"\n#include \"dfufile.hh\"\n\n\nint infoFile(QCommandLineParser &parser, QCoreApplication &app) {\n  Q_UNUSED(app)\n\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  QString filename = parser.positionalArguments().at(1);\n  DFUFile file;\n  ErrorStack err;\n  if (! file.read(filename, err)) {\n    logError() << \"Cannot read codeplug file '\" << filename\n               << \"': \" << err.format();\n    return -1;\n  }\n\n  QTextStream out(stdout);\n  file.dump(out);\n  return 0;\n}\n"
  },
  {
    "path": "cli/infofile.hh",
    "content": "#ifndef INFOFILE_HH\n#define INFOFILE_HH\n\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint infoFile(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // INFOFILE_HH\n"
  },
  {
    "path": "cli/main.cc",
    "content": "#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <iostream>\n\n#include \"logger.hh\"\n#include \"config.h\"\n#include \"detect.hh\"\n#include \"verify.hh\"\n#include \"radioinfo.hh\"\n#include \"readcodeplug.hh\"\n#include \"writecodeplug.hh\"\n#include \"writecallsigndb.hh\"\n#include \"encodecodeplug.hh\"\n#include \"encodecallsigndb.hh\"\n#include \"decodecodeplug.hh\"\n#include \"infofile.hh\"\n\n#include \"uv390_codeplug.hh\"\n\nint main(int argc, char *argv[])\n{\n  // Install log handler to stderr.\n  QTextStream out(stderr);\n  StreamLogHandler *handler = new StreamLogHandler(out, LogMessage::WARNING, true);\n  Logger::get().addHandler(handler);\n\n  // Instantiate core application\n  QCoreApplication app(argc, argv);\n  app.setApplicationName(\"dmrconf\");\n  app.setOrganizationName(\"DM3MAT\");\n  app.setOrganizationDomain(\"dm3mat.darc.de\");\n  app.setApplicationVersion(VERSION_STRING);\n\n  QCommandLineParser parser;\n  parser.setApplicationDescription(\n        QCoreApplication::translate(\n          \"main\", \"Up- and download codeplugs for cheap Chinese DMR radios.\"));\n\n  parser.addHelpOption();\n  parser.addVersionOption();\n  parser.addOption({\n                     {\"V\",\"verbose\"},\n                     QCoreApplication::translate(\"main\", \"Verbose output.\")\n                   });\n  parser.addOption({\n                     {\"c\", \"csv\"},\n                     QCoreApplication::translate(\"main\", \"Up- and download codeplugs in CSV format.\")\n                   });\n  parser.addOption({\n                     {\"y\", \"yaml\"},\n                     QCoreApplication::translate(\"main\", \"Up- and download codeplugs in extensible YAML format.\")\n                   });\n  parser.addOption({\n                     {\"b\", \"bin\"},\n                     QCoreApplication::translate(\"main\", \"Up- and download codeplugs in binary format.\")\n                   });\n  parser.addOption({\n                     {\"m\", \"manufacturer\"},\n                     QCoreApplication::translate(\"main\", \"Given file is manufacturer codeplug file. \"\n                     \" Can be used with 'decode'.\")\n                   });\n  parser.addOption({\n                     {\"D\",\"device\"},\n                     QCoreApplication::translate(\"main\", \"Specifies the device to use to talk to \"\n                     \"the radio. If not specified, the dmrconf will try to detect the radio \"\n                     \"automatically. Please note, that for some radios the device must be specified.\"),\n                     QCoreApplication::translate(\"main\", \"DEVICE\")\n                   });\n  parser.addOption({\n                     {\"R\", \"radio\"},\n                     QCoreApplication::translate(\"main\", \"Specifies the radio. This option can also \"\n                     \"be used to override the auto-detection of radios. Be careful using this \"\n                     \"option when writing to the device. A incompatible code-plug might be written.\"),\n                     QCoreApplication::translate(\"main\", \"RADIO\")\n                   });\n  parser.addOption({\n                     {\"i\", \"id\"},\n                     QCoreApplication::translate(\"main\", \"Specifies the DMR id.\"),\n                     QCoreApplication::translate(\"main\", \"ID\")\n                   });\n  parser.addOption({\n                     {\"n\", \"limit\"},\n                     QCoreApplication::translate(\"main\", \"Limits several amounts, depending on the \"\n                     \"context. When encoding/writing the callsign db, this option specifies the \"\n                     \"maximum number of callsigns to encode.\"),\n                     QCoreApplication::translate(\"main\", \"N\")\n                   });\n  parser.addOption({\n                     {\"B\",\"database\"},\n                     QCoreApplication::translate(\"main\", \"Specifies the user DB json file when \"\n                     \"writing the callsign db.\"),\n                     \"FILENAME\"\n                   });\n  parser.addOption(QCommandLineOption(\n                     \"init-codeplug\",\n                     QCoreApplication::translate(\n                       \"main\", \"Initializes the code-plug in the radio. If not present (default) \"\n                               \"the code-plug gets updated, maintaining all settings made earlier.\")));\n  parser.addOption(QCommandLineOption(\n                     \"update-device-clock\",\n                     QCoreApplication::translate(\n                       \"main\", \"If present, the device clock gets set to the system time on any \"\n                       \"transfer to the radio.\")));\n  parser.addOption(QCommandLineOption(\n                     \"auto-enable-gps\",\n                     QCoreApplication::translate(\n                       \"main\", \"Automatically enables GPS if there is a GPS/APRS system used by any \"\n                               \"channel.\")));\n  parser.addOption(QCommandLineOption(\n                     \"auto-enable-roaming\",\n                     QCoreApplication::translate(\"main\", \"Automatically enables roaming if there is a \"\n                                                         \"roaming zone used by any channel.\")));\n  parser.addOption(QCommandLineOption(\n                     \"ignore-limits\",\n                     QCoreApplication::translate(\"main\", \"Disables some limit checks.\")));\n  parser.addOption(QCommandLineOption(\n                     \"list-radios\",\n                     QCoreApplication::translate(\"main\", \"Lists all supported radios including the \"\n                                                 \"keys to be used with the --radio option.\")));\n  parser.addPositionalArgument(\n        \"command\", QCoreApplication::translate(\n          \"main\", \"Specifies the command to perform. Either detect, verify, read, write, \"\n          \"write-db, encode, encode-db, decode or info. Consult the man-page of dmrconf for a \"\n          \"detailed description of these commands.\"),\n        QCoreApplication::translate(\"main\", \"[command]\"));\n\n  parser.addPositionalArgument(\n        \"file\", QCoreApplication::translate(\n          \"main\", \"The code-plug file. Either binary (extension .dfu), text/csv (extension .conf \"\n          \"or .csv) or YAML format (extension .yaml). The format can be forced using the --csv, \"\n          \"--yaml or --binary options.\"),\n        QCoreApplication::translate(\"main\", \"[filename]\"));\n\n  parser.process(app);\n\n  if (parser.isSet(\"list-radios\")) {\n    QList<RadioInfo> radios = RadioInfo::allRadios();\n    QTextStream out(stdout);\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << \" Key\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"| \";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << \"Name\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"| \";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(60); out << \"Manufacturer\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"\\n\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"+-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"+-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(60); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"\\n\";\n    foreach (RadioInfo radio, radios) {\n      out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << (\" \" +radio.key());\n      out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"| \";\n      out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(18); out << radio.name();\n      out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"| \";\n      out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << radio.manufacturer() << \"\\n\";\n    }\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"+-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(18); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"+-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar('-'); out.setFieldWidth(60); out << \"-\";\n    out.setFieldAlignment(QTextStream::AlignLeft); out.setPadChar(' '); out.setFieldWidth(0); out << \"\\n\";\n    out.flush();\n    return 0;\n  }\n\n  if (1 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  if (parser.isSet(\"verbose\"))\n    handler->setMinLevel(LogMessage::DEBUG);\n\n  int res = -1;\n  QString command = parser.positionalArguments().at(0);\n\n  if (\"detect\" == command)\n    res = detect(parser, app);\n  else if (\"verify\" == command)\n    res = verify(parser, app);\n  else if (\"read\" == command)\n    res = readCodeplug(parser, app);\n  else if (\"write\" == command)\n    res = writeCodeplug(parser, app);\n  else if (\"write-db\" == command)\n    res = writeCallsignDB(parser, app);\n  else if (\"encode\" == command)\n    res = encodeCodeplug(parser, app);\n  else if (\"encode-db\" == command)\n    res = encodeCallsignDB(parser, app);\n  else if (\"decode\" == command)\n    res = decodeCodeplug(parser, app);\n  else if (\"info\" == command)\n    res = infoFile(parser, app);\n  else\n    parser.showHelp(-1);\n\n  // Allow some pending events to be processed (e.g., deleteLater())\n  QEventLoop loop;\n  while(loop.processEvents()) {}\n\n  return res;\n}\n"
  },
  {
    "path": "cli/printprogress.cc",
    "content": "#include \"printprogress.hh\"\n\n#include <QTextStream>\n\n\nvoid print_progress(int prog) {\n  QTextStream out(stdout);\n  out << \"\\r\";\n  out.setFieldWidth(2);\n  out << prog;\n  out.setFieldWidth(0);\n  out << \"%\";\n  prog = (prog*20)/100;\n  out << \"[\";\n  for (int i=0; i<prog; i++)\n    out << \"*\";\n  for (int i=prog; i<20; i++)\n    out << \" \";\n  out << \"]\";\n}\n\n"
  },
  {
    "path": "cli/printprogress.hh",
    "content": "#ifndef PRINTPROGRESS_HH\n#define PRINTPROGRESS_HH\n\n\nvoid print_progress(int prog);\n\n#endif // PRINTPROGRESS_HH\n"
  },
  {
    "path": "cli/progressbar.cc",
    "content": "#include \"progressbar.hh\"\n\nvoid showProgress(unsigned percent) {\n  std::cerr << \"[\";\n  for (unsigned i=0; i<50; i++) {\n    if (percent/2 > i)\n      std::cerr << \"=\";\n    else\n      std::cerr << \" \";\n  }\n  std::cerr << \"] \" << percent <<\"%\" << std::endl;\n}\n\nvoid updateProgress(unsigned percent) {\n  std::cerr << \"\\033[1A\\033[K\";\n  showProgress(percent);\n}\n"
  },
  {
    "path": "cli/progressbar.hh",
    "content": "#ifndef PROGRESSBAR_HH\n#define PROGRESSBAR_HH\n\n#include <iostream>\n\nvoid showProgress(unsigned percent=0);\nvoid updateProgress(unsigned percent);\n\n#endif // PROGRESSBAR_HH\n"
  },
  {
    "path": "cli/readcodeplug.cc",
    "content": "#include \"readcodeplug.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QFile>\n\n#include \"logger.hh\"\n#include \"radio.hh\"\n#include \"printprogress.hh\"\n#include \"config.hh\"\n#include \"codeplug.hh\"\n#include \"progressbar.hh\"\n#include \"autodetect.hh\"\n\n\nint readCodeplug(QCommandLineParser &parser, QCoreApplication &app)\n{\n  Q_UNUSED(app);\n\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  ErrorStack err;\n  Radio *radio = autoDetect(parser, app, err);\n  if (nullptr == radio) {\n    logError() << \"Cannot detect radio: \" << err.format();\n    return -1;\n  }\n\n  QString filename = parser.positionalArguments().at(1);\n\n  if (! parser.isSet(\"verbose\")) {\n    showProgress();\n    QObject::connect(radio, &Radio::downloadProgress, updateProgress);\n  }\n\n  TransferFlags flags;\n  flags.setBlocking(true);\n\n  Config config;\n  if (! radio->startDownload(flags, err)) {\n    logError() << \"Codeplug download error: \" << err.format();\n    return -1;\n  }\n\n  if (Radio::StatusError == radio->status()) {\n    logError() << \"Codeplug download error: \" << err.format();\n    return -1;\n  }\n\n  logDebug() << \"Save codeplug at '\" << filename << \"'.\";\n  // If output is CSV -> decode code-plug\n  if (parser.isSet(\"csv\") || (filename.endsWith(\".conf\") || filename.endsWith(\".csv\"))) {\n    logError() << \"Export of the old table based format was disabled with 0.9.0. \"\n                  \"Import still works.\";\n    return -1;\n  } else if (parser.isSet(\"yaml\") || filename.endsWith(\".yaml\")) {\n    // decode codeplug\n    if (! radio->codeplug().decode(&config, err)) {\n      logError() << \"Cannot decode codeplug: \" << err.format();\n      return -1;\n    }\n    // post-process decoded codeplug\n    if (! radio->codeplug().postprocess(&config, err)) {\n      logError() << \"Cannot post-process codeplug: \" << err.format();\n      return -1;\n    }\n\n    // try to write YAML file\n    QFile file(filename);\n    if (! file.open(QIODevice::WriteOnly)) {\n      logError() << \"Cannot write YAML file '\" << filename << \"': \" << file.errorString();\n      return -1;\n    }\n\n    QTextStream stream(&file);\n    if (! config.toYAML(stream)) {\n      logError() << \"Cannot serialize config to YAML file '\" << filename << \"'.\";\n      return -1;\n    }\n    stream.flush();\n    file.close();\n  } else if (parser.isSet(\"bin\") || filename.endsWith(\".bin\") || filename.endsWith(\".dfu\")) {\n    // otherwise write binary code-plug\n    if (! radio->codeplug().write(filename, err)) {\n      logError() << \"Cannot dump codeplug into file '\" << filename << \"': \" << err.format();\n      return -1;\n    }\n  } else {\n    logError() << \"Cannot determine file output type from '\" << filename << \"'. \"\n               << \"Consider using --csv, --yaml or --bin.\";\n    return -1;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "cli/readcodeplug.hh",
    "content": "#ifndef READCODEPLUG_HH\n#define READCODEPLUG_HH\n\nclass QCommandLineParser;\nclass QCoreApplication;\n\nint readCodeplug(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // READCODEPLUG_HH\n"
  },
  {
    "path": "cli/verify.cc",
    "content": "#include \"verify.hh\"\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QString>\n#include <QFile>\n#include <QTextStream>\n#include <iostream>\n\n#include \"logger.hh\"\n#include \"config.hh\"\n#include \"csvreader.hh\"\n#include \"dfufile.hh\"\n#include \"radiolimits.hh\"\n#include \"rd5r.hh\"\n#include \"uv390.hh\"\n#include \"md2017.hh\"\n#include \"dm1701.hh\"\n#include \"gd73.hh\"\n#include \"gd77.hh\"\n#include \"opengd77.hh\"\n#include \"openuv380.hh\"\n#include \"d868uv.hh\"\n#include \"d878uv.hh\"\n#include \"d878uv2.hh\"\n#include \"d578uv.hh\"\n\n\nint verify(QCommandLineParser &parser, QCoreApplication &app)\n{\n  Q_UNUSED(app);\n\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  QString filename = parser.positionalArguments().at(1);\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    logError() << \"Cannot open file\" << filename;\n    return -1;\n  }\n\n  Config config;\n\n  // Determine type by ending or flag\n  if (parser.isSet(\"csv\") || (filename.endsWith(\".conf\") || filename.endsWith(\".csv\"))) {\n    QTextStream stream(&file);\n    QString errorMessage;\n    if (! CSVReader::read(&config, stream, errorMessage)) {\n      logError() << \"Cannot read config file '\" << filename << \"': \" << errorMessage;\n      return -1;\n    }\n    logInfo() << \"Verify '\" << filename << \"': No syntax issues found.\";\n  } else if (parser.isSet(\"bin\") || (filename.endsWith(\".bin\") || filename.endsWith(\".dfu\"))) {\n    logError() << \"Verification of binary code-plugs makes no sense.\";\n    return -1;\n  } else if (parser.isSet(\"yaml\") || (filename.endsWith(\".yaml\") || filename.endsWith(\".yml\"))) {\n    ErrorStack err;\n    if (! config.readYAML(filename,err)) {\n      logError() << \"Cannot read codeplug file '\" << filename\n                 << \"': \" << err.format();\n      return -1;\n    }\n  } else {\n    logError() << \"Cannot determine filetype from filename '\" << filename\n               << \"': Consider using --csv.\";\n    return -1;\n  }\n\n  if (! parser.isSet(\"radio\")) {\n    logInfo() << \"To verify the codeplug against a specific radio, conser using the --radio=RADIO option.\";\n    return 0;\n  }\n\n\n  RadioLimitContext ctx;\n  RadioInfo::Radio radio = RadioInfo::byKey(parser.value(\"radio\").toLower()).id();\n  switch (radio) {\n  case RadioInfo::RD5R: {\n    RD5R radio; ErrorStack err;\n    Config *intermediate = radio.codeplug().preprocess(&config, err);\n    if (nullptr == intermediate) {\n      logError() << \"Cannot pre-process codeplug: \" << err.format();\n      return -1;\n    }\n    radio.limits().verifyConfig(intermediate, ctx);\n    delete intermediate;\n    } break;\n  case RadioInfo::UV390: {\n      UV390 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::MD2017: {\n      MD2017 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::DM1701: {\n      DM1701 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::GD73: {\n      GD73 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::GD77: {\n      GD77 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::OpenGD77: {\n      OpenGD77 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::OpenUV380: {\n      OpenUV380 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::D868UV: {\n      D868UV radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::D878UV: {\n      D878UV radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::D878UVII: {\n      D878UV2 radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  case RadioInfo::D578UV: {\n      D578UV radio; ErrorStack err;\n      Config *intermediate = radio.codeplug().preprocess(&config, err);\n      if (nullptr == intermediate) {\n        logError() << \"Cannot pre-process codeplug: \" << err.format();\n        return -1;\n      }\n      radio.limits().verifyConfig(intermediate, ctx);\n      delete intermediate;\n    } break;\n  default:\n    logError() << \"Cannot verify code-plug against unknown radio '\" << radio << \"'.\";\n    return -1;\n  }\n\n  bool valid = true;\n  for (int i=0; i<ctx.count(); i++) {\n    switch (ctx.message(i).severity()) {\n    case RadioLimitIssue::Silent:\n      logDebug() << ctx.message(i).format();\n      break;\n    case RadioLimitIssue::Hint:\n      logInfo() << ctx.message(i).format();\n      break;\n    case RadioLimitIssue::Warning:\n      logWarn() << ctx.message(i).format();\n      break;\n    case RadioLimitIssue::Critical:\n      logError() << ctx.message(i).format();\n      valid = false;\n      break;\n    }\n  }\n\n  return (valid ? 0 : -1);\n}\n"
  },
  {
    "path": "cli/verify.hh",
    "content": "#ifndef VERIFY_HH\n#define VERIFY_HH\n\nclass QCommandLineParser;\nclass QCoreApplication;\n\nint verify(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // VERIFY_HH\n"
  },
  {
    "path": "cli/writecallsigndb.cc",
    "content": "#include \"writecallsigndb.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n\n#include \"logger.hh\"\n#include \"radio.hh\"\n#include \"userdatabase.hh\"\n#include \"progressbar.hh\"\n#include \"callsigndb.hh\"\n#include \"autodetect.hh\"\n\n\nint writeCallsignDB(QCommandLineParser &parser, QCoreApplication &app) {\n  UserDatabase userdb(false);\n\n  if (parser.isSet(\"database\")) {\n    if (! userdb.load(parser.value(\"database\"))) {\n      logError() << \"Cannot load user-db from '\" << parser.value(\"database\") << \"'.\";\n      return -1;\n    }\n  } else if (! userdb.ready()) {\n    logInfo() << \"Downloading call-sign DB...\";\n    // Wait for download to finish\n    QEventLoop loop;\n    QObject::connect(&userdb, SIGNAL(loaded()), &loop, SLOT(quit()));\n    QObject::connect(&userdb, SIGNAL(error(QString)), &loop, SLOT(quit()));\n    loop.exec();\n    // Check if call-sign DB has been loaded\n    if (0 == userdb.count()) {\n      logError() << \"Could not download/load call-sign DB.\";\n      return -1;\n    }\n  }\n\n  if (parser.isSet(\"id\")) {\n    QStringList prefixes_text = parser.value(\"id\").split(\",\");\n    QSet<unsigned> prefixes;\n    foreach (QString prefix_text, prefixes_text) {\n      bool ok=true; uint32_t prefix = prefix_text.toUInt(&ok);\n      if (ok)\n        prefixes.insert(prefix);\n    }\n\n    if (prefixes.isEmpty()) {\n      logError() << \"Please specify a valid DMR ID or a list of DMR prefixes for --id option.\";\n      return -1;\n    }\n    prefixes_text.clear();\n    foreach (unsigned prefix, prefixes) {\n      prefixes_text.append(QString::number(prefix));\n    }\n    logDebug() << \"Sort call-sign DB w.r.t. DMR ID(s) {\" << prefixes_text.join(\", \") << \"}.\";\n    userdb.sortUsers(prefixes);\n  } else {\n    logWarn() << \"No ID is specified, a more or less random set of call-signs will be used \"\n              << \"if the radio cannot hold the entire call-sign DB of \" << userdb.count()\n              << \" entries. Specify your DMR ID with --id=YOUR_DMR_ID. dmrconf will then \"\n              << \"select those entries 'closest' to you. I.e., DMR IDs with the same prefix.\";\n  }\n\n  CallsignDB::Flags selection;\n  selection.setUpdateDeviceClock(parser.isSet(\"update-device-clock\"));\n  if (parser.isSet(\"limit\")) {\n    bool ok=true;\n    selection.setCountLimit(parser.value(\"limit\").toUInt(&ok));\n    if (! ok) {\n      logError() << \"Please specify a valid limit for the number of callsign db entries using the -n/--limit option.\";\n      return -1;\n    }\n  }\n\n  ErrorStack err;\n  Radio *radio = autoDetect(parser, app, err);\n  if (nullptr == radio) {\n    logError() << \"Could not detect radio: \" << err.format();\n    return -1;\n  }\n\n  if (! parser.isSet(\"verbose\")) {\n    showProgress();\n    QObject::connect(radio, &Radio::downloadProgress, updateProgress);\n  }\n\n  selection.setBlocking(true);\n  if (! radio->startUploadCallsignDB(&userdb, selection, err)) {\n    logError() << \"Could not upload call-sign DB to radio: \" << err.format();\n    return -1;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "cli/writecallsigndb.hh",
    "content": "#ifndef WRITECALLSIGNDB_HH\n#define WRITECALLSIGNDB_HH\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint writeCallsignDB(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // WRITECALLSIGNDB_HH\n"
  },
  {
    "path": "cli/writecodeplug.cc",
    "content": "#include \"writecodeplug.hh\"\n\n#include <QCoreApplication>\n#include <QCommandLineParser>\n#include <QFileInfo>\n\n#include \"logger.hh\"\n#include \"radio.hh\"\n#include \"config.hh\"\n#include \"progressbar.hh\"\n#include \"autodetect.hh\"\n#include \"radiolimits.hh\"\n\n\nint writeCodeplug(QCommandLineParser &parser, QCoreApplication &app) {\n  if (2 > parser.positionalArguments().size())\n    parser.showHelp(-1);\n\n  QString filename = parser.positionalArguments().at(1);\n  QFileInfo fileinfo(filename);\n\n  QString errorMessage;\n  Config config;\n  if (parser.isSet(\"csv\") || (\"csv\" == fileinfo.suffix()) || (\"conf\"==fileinfo.suffix())) {\n    if (! config.readCSV(filename, errorMessage)) {\n      logError() << \"Cannot read CSV file '\" << filename << \"': \" << errorMessage;\n      return -1;\n    }\n  } else if (parser.isSet(\"yaml\") || (\"yaml\" == fileinfo.suffix()) || (\"yml\" == fileinfo.suffix())) {\n    ErrorStack err;\n    if (! config.readYAML(fileinfo.canonicalFilePath(), err)) {\n      logError() << \"Cannot parse YAML codeplug '\" << fileinfo.fileName() << \"': \" << err.format();\n      return -1;\n    }\n  }\n  logDebug() << \"Read codeplug from '\" << filename << \"'.\";\n\n  ErrorStack err;\n  Radio *radio = autoDetect(parser, app, err);\n  if (nullptr == radio) {\n    logError() << \"Cannot detect radio:\" << err.format();\n    return -1;\n  }\n\n  RadioLimitContext ctx(parser.isSet(\"ignore-limits\"));\n\n  Config *intermediate = radio->codeplug().preprocess(&config, err);\n  if (nullptr == intermediate) {\n    logError() << \"Cannot pre-process codeplug: \" << err.format();\n    return -1;\n  }\n\n  bool verified = true;\n  radio->limits().verifyConfig(intermediate, ctx);\n\n  // Only print warnings\n  for (int i=0; i<ctx.count(); i++) {\n    switch (ctx.message(i).severity()) {\n    case RadioLimitIssue::Warning:\n      logWarn() << \"Verification Issue: \" << ctx.message(i).format();\n      break;\n    case RadioLimitIssue::Critical:\n      logError() << \"Verification Issue: \" << ctx.message(i).format();\n      break;\n    default:\n      break;\n    }\n  }\n\n  if (! verified) {\n    logError() << \"Cannot upload codeplug to device: Codeplug cannot be verified with radio.\";\n    delete intermediate;\n    return -1;\n  }\n\n  if (! parser.isSet(\"verbose\")) {\n    showProgress();\n    QObject::connect(radio, &Radio::downloadProgress, updateProgress);\n  }\n\n  Codeplug::Flags flags;\n  flags.setBlocking(true);\n  flags.setUpdateDeviceClock(parser.isSet(\"update-device-clock\"));\n  flags.setUpdateCodeplug(! parser.isSet(\"init-codeplug\"));\n  flags.setAutoEnableGPS(parser.isSet(\"auto-enable-gps\"));\n  flags.setAutoEnableRoaming(parser.isSet(\"auto-enable-roaming\"));\n\n  logDebug() << \"Start upload to \" << radio->name() << \".\";\n  if (! radio->startUpload(intermediate, flags, err)) {\n    logError() << \"Codeplug upload error: \" << err.format();\n    return -1;\n  }\n\n  if (Radio::StatusError == radio->status()) {\n    logError() << \"Codeplug upload error: \" << err.format();\n    return -1;\n  }\n\n  logDebug() << \"Upload completed.\";\n  return 0;\n}\n"
  },
  {
    "path": "cli/writecodeplug.hh",
    "content": "#ifndef WRITECODEPLUG_HH\n#define WRITECODEPLUG_HH\n\nclass QCoreApplication;\nclass QCommandLineParser;\n\nint writeCodeplug(QCommandLineParser &parser, QCoreApplication &app);\n\n#endif // WRITECODEPLUG_HH\n"
  },
  {
    "path": "cmake/FindLIBUSB_1.cmake",
    "content": "# - Try to find libusb-1.0\n# Once done this will define\n#\n# LIBUSB_1_FOUND - system has libusb\n# LIBUSB_1_INCLUDE_DIRS - the libusb include directory\n# LIBUSB_1_LIBRARIES - Link these to use libusb\n# LIBUSB_1_DEFINITIONS - Compiler switches required for using libusb\n#\n# Adapted from cmake-modules Google Code project\n#\n# Copyright (c) 2006 Andreas Schneider <mail@cynapses.org>\n#\n# (Changes for libusb) Copyright (c) 2008 Kyle Machulis <kyle@nonpolynomial.com>\n#\n# Redistribution and use is allowed according to the terms of the New BSD license.\n#\n# CMake-Modules Project New BSD License\n#\n# Redistribution and use in source and binary forms, with or without\n# modification, are permitted provided that the following conditions are met:\n#\n# * Redistributions of source code must retain the above copyright notice, this\n# list of conditions and the following disclaimer.\n#\n# * Redistributions in binary form must reproduce the above copyright notice,\n# this list of conditions and the following disclaimer in the\n# documentation and/or other materials provided with the distribution.\n#\n# * Neither the name of the CMake-Modules Project nor the names of its\n# contributors may be used to endorse or promote products derived from this\n# software without specific prior written permission.\n#\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\n# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\n# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n#\n\n\nif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)\n  # in cache already\n  set(LIBUSB_FOUND TRUE)\nelse (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)\n  find_path(LIBUSB_1_INCLUDE_DIR\n    NAMES\nlibusb.h\n    PATHS\n      /usr/include\n      /usr/local/include\n      /sw/include\nPATH_SUFFIXES\nlibusb-1.0\n  )\n\n  find_library(LIBUSB_1_LIBRARY\n    NAMES\n      usb-1.0 usb\n    PATHS\n      /usr/lib\n      /usr/local/lib\n      /sw/lib\n  )\n\n  set(LIBUSB_1_INCLUDE_DIRS\n    ${LIBUSB_1_INCLUDE_DIR}\n  )\n  set(LIBUSB_1_LIBRARIES\n    ${LIBUSB_1_LIBRARY}\n)\n\n  if (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)\n     set(LIBUSB_1_FOUND TRUE)\n  endif (LIBUSB_1_INCLUDE_DIRS AND LIBUSB_1_LIBRARIES)\n\n  if (LIBUSB_1_FOUND)\n    if (NOT libusb_1_FIND_QUIETLY)\n      message(STATUS \"Found libusb-1.0:\")\nmessage(STATUS \" - Includes: ${LIBUSB_1_INCLUDE_DIRS}\")\nmessage(STATUS \" - Libraries: ${LIBUSB_1_LIBRARIES}\")\n    endif (NOT libusb_1_FIND_QUIETLY)\n  else (LIBUSB_1_FOUND)\n    if (libusb_1_FIND_REQUIRED)\n      message(FATAL_ERROR \"Could not find libusb\")\n    endif (libusb_1_FIND_REQUIRED)\n  endif (LIBUSB_1_FOUND)\n\n  # show the LIBUSB_1_INCLUDE_DIRS and LIBUSB_1_LIBRARIES variables only in the advanced view\n  mark_as_advanced(LIBUSB_1_INCLUDE_DIRS LIBUSB_1_LIBRARIES)\n\nendif (LIBUSB_1_LIBRARIES AND LIBUSB_1_INCLUDE_DIRS)\n"
  },
  {
    "path": "cmake/FindYAMLCPP.cmake",
    "content": "# - Try to find yaml-cpp\n# Once done this will define\n#\n# YAMLCPP_FOUND - system has yaml-cpp\n# YAMLCPP_INCLUDE_DIRS - the yaml-cpp include directory\n# YAMLCPP_LIBRARIES - Link these to use yaml-cpp\n# YAMLCPP_DEFINITIONS - Compiler switches required for using yaml-cpp\n\nif (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS)\n  # in cache already\n  set(YAMLCPP_FOUND TRUE)\nelse (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS)\n  find_path(YAMLCPP_INCLUDE_DIR\n    NAMES\n    yaml-cpp/yaml.h\n    PATHS\n      /usr/include\n      /usr/local/include\n      /opt/local\n      /sw/include\n  )\n\n  find_library(YAMLCPP_LIBRARY\n    NAMES\n      yaml-cpp\n    PATHS\n      /usr/lib\n      /usr/local/lib\n      /opt/local\n      /sw/lib\n  )\n\n  set(YAMLCPP_INCLUDE_DIRS\n    ${YAMLCPP_INCLUDE_DIR}\n  )\n  set(YAMLCPP_LIBRARIES\n    ${YAMLCPP_LIBRARY}\n)\n\n  if (YAMLCPP_INCLUDE_DIRS AND YAMLCPP_LIBRARIES)\n     set(YAMLCPP_FOUND TRUE)\n  endif (YAMLCPP_INCLUDE_DIRS AND YAMLCPP_LIBRARIES)\n\n  if (YAMLCPP_FOUND)\n    if (NOT YAMLCPP_FIND_QUIETLY)\n      message(STATUS \"Found yaml-cpp:\")\n      message(STATUS \" - Includes: ${YAMLCPP_INCLUDE_DIRS}\")\n      message(STATUS \" - Libraries: ${YAMLCPP_LIBRARIES}\")\n    endif (NOT YAMLCPP_FIND_QUIETLY)\n  else (YAMLCPP_FOUND)\n    if (YAMLCPP_FIND_REQUIRED)\n      message(FATAL_ERROR \"Could not find yaml-cpp\")\n    endif (YAMLCPP_FIND_REQUIRED)\n  endif (YAMLCPP_FOUND)\n\n  # show the YAMLCPP_INCLUDE_DIRS and YAMLCPP_LIBRARIES variables only in the advanced view\n  mark_as_advanced(YAMLCPP_INCLUDE_DIRS YAMLCPP_LIBRARIES)\nendif (YAMLCPP_LIBRARIES AND YAMLCPP_INCLUDE_DIRS)\n"
  },
  {
    "path": "cmake/GenerateIcons.cmake",
    "content": "set(GENERATE_ICONS_OUTPUT_FILES \"\")\n\n\n\nfunction(_create_build_icon_command icon_name icon_source_dir icon_theme icon_context icon_size icon_output_dir)\n  cmake_path(APPEND icon_source_dir ${icon_theme} ${icon_context} \"${icon_name}.svg\"\n             OUTPUT_VARIABLE input_file)\n  cmake_path(APPEND icon_output_dir ${icon_theme} \"${icon_size}x${icon_size}\" ${icon_context} \"${icon_name}.png\"\n             OUTPUT_VARIABLE output_file)\n  list(APPEND GENERATE_ICONS_OUTPUT_FILES ${output_file})\n  add_custom_command(\n    OUTPUT ${output_file}\n    #COMMAND convert ARGS -background none -resize ${icon_size}x${icon_size} ${input_file} ${output_file}\n    COMMAND rsvg-convert ARGS -w ${icon_size} -h ${icon_size} -o ${output_file} ${input_file}\n    DEPENDS ${input_file}\n    COMMENT \"Generate ${output_file}\"\n    VERBATIM)\n  return(PROPAGATE GENERATE_ICONS_OUTPUT_FILES)\nendfunction()\n\n\n\nfunction(generate_icons)\n  cmake_parse_arguments(GENERATE_ICONS \"\" \"DIRECTORY;CONTEXT;\" \"THEMES;ICONS;SIZES\" ${ARGN})\n\n  cmake_path(APPEND CMAKE_CURRENT_BINARY_DIR \"icons\" OUTPUT_VARIABLE GENERATE_ICONS_OUTPUT_DIR)\n\n  foreach(ICON_THEME ${GENERATE_ICONS_THEMES})\n    file(MAKE_DIRECTORY \"${GENERATE_ICONS_OUTPUT_DIR}/${ICON_THEME}/scalable/${GENERATE_ICONS_CONTEXT}\")\n    foreach(ICON_NAME ${GENERATE_ICONS_ICONS})\n      cmake_path(APPEND GENERATE_ICONS_DIRECTORY ${ICON_THEME} ${GENERATE_ICONS_CONTEXT} \"${ICON_NAME}.svg\" OUTPUT_VARIABLE source_file)\n      cmake_path(APPEND GENERATE_ICONS_OUTPUT_DIR ${ICON_THEME} \"scalable\" ${GENERATE_ICONS_CONTEXT} OUTPUT_VARIABLE output_dir)\n      cmake_path(APPEND output_dir \"${ICON_NAME}.svg\" OUTPUT_VARIABLE output_file)\n      list(APPEND GENERATE_ICONS_OUTPUT_FILES ${output_file})\n      add_custom_command(\n        OUTPUT ${output_file}\n        COMMAND ${CMAKE_COMMAND} -E copy ${source_file} ${output_dir}\n        DEPENDS ${source_file}\n        COMMENT \"Copy ${output_file}\"\n        VERBATIM)\n    endforeach()\n\n    foreach(ICON_SIZE ${GENERATE_ICONS_SIZES})\n      cmake_path(APPEND GENERATE_ICONS_OUTPUT_DIR ${ICON_THEME} \"${ICON_SIZE}x${ICON_SIZE}\" ${GENERATE_ICONS_CONTEXT}\n                        OUTPUT_VARIABLE SCALED_ICONS_OUTPUT_DIR)\n      file(MAKE_DIRECTORY ${SCALED_ICONS_OUTPUT_DIR})\n      foreach(ICON_NAME ${GENERATE_ICONS_ICONS})\n        cmake_path(APPEND GENERATE_ICONS_DIRECTORY ${ICON_THEME} ${GENERATE_ICONS_CONTEXT} \"${ICON_NAME}.svg\"\n                          OUTPUT_VARIABLE source_file)\n        _create_build_icon_command(${ICON_NAME} ${GENERATE_ICONS_DIRECTORY} ${ICON_THEME} ${GENERATE_ICONS_CONTEXT} ${ICON_SIZE} ${GENERATE_ICONS_OUTPUT_DIR})\n      endforeach()\n    endforeach()\n  endforeach()\n  return(PROPAGATE GENERATE_ICONS_OUTPUT_FILES)\nendfunction()\n\n"
  },
  {
    "path": "de.darc.dm3mat.qdmr.yaml",
    "content": "app-id: de.darc.dm3mat.qdmr\nruntime: org.kde.Platform\nruntime-version: '6.9'\nsdk: org.kde.Sdk\ncommand: qdmr\nrename-desktop-file: qdmr.desktop\nrename-icon: qdmr\nfinish-args:\n  - --device=all\n  - --share=network\n  - --share=ipc\n  - --socket=fallback-x11\n  - --socket=wayland\n  - --env=\"QT_SERIALPORT_SKIP_UDEV_LOOKUP=1\"\n\ncleanup:\n  - /include\n  - /lib/pkgconfig\n  - /share/aclocal\n  - /share/doc\n  - /share/man\n  - /man\n  - \"*.a\"\n  - \"*.la\"\n\nmodules:\n  - name: yaml-cpp\n    buildsystem: cmake-ninja\n    config-opts:\n      - -DBUILD_SHARED_LIBS=ON\n    sources:\n      - type: git\n        url: https://github.com/jbeder/yaml-cpp\n        commit: f7320141120f720aecc4c32be25586e7da9eb978 # tag 0.8.0\n\n  - name: libusb1\n    buildsystem: autotools\n    config-opts:\n      - --disable-static\n    sources:\n      - type: git\n        url: https://github.com/libusb/libusb.git\n        tag: v1.0.29\n        commit: 15a7ebb4d426c5ce196684347d2b7cafad862626\n\n  - name: qdmr\n    buildsystem: cmake-ninja\n    sources:\n      - type: dir\n        path: .\n"
  },
  {
    "path": "dist/99-qdmr.rules",
    "content": "# Radioddity GD-73\nSUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1206\", ATTRS{idProduct}==\"0227\", MODE=\"660\", GROUP=\"dialout\"\n\n# TYT MD-UV380\nSUBSYSTEM==\"usb\", ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"df11\", MODE=\"660\", GROUP=\"dialout\"\n\n# Baofeng RD-5R, TD-5R\n# Radioddity GD-77\nSUBSYSTEM==\"usb\", ATTRS{idVendor}==\"15a2\", ATTRS{idProduct}==\"0073\", MODE=\"660\", GROUP=\"dialout\"\n\n# Ignore this device in Modem Manager\n# Anytone AT-D868UV, AT-D878UV, AT-D578UV (II) (GD32)\nATTRS{idVendor}==\"28e9\" ATTRS{idProduct}==\"018a\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n# Anytone AT-D578UV (II) (STM32)\nATTRS{idVendor}==\"2e3c\" ATTRS{idProduct}==\"5740\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n# also for DR-1801UV\nATTRS{idVendor}==\"067b\" ATTRS{idProduct}==\"23a3\", ENV{ID_MM_DEVICE_IGNORE}=\"1\"\n"
  },
  {
    "path": "dist/CMakeLists.txt",
    "content": "if (UNIX AND NOT APPLE)\n  configure_file(\"qdmr.desktop.in\" \"qdmr.desktop\")\n  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/qdmr.desktop DESTINATION share/applications/)\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr32.png DESTINATION share/icons/hicolor/32x32/apps/ RENAME qdmr.png)\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr48.png DESTINATION share/icons/hicolor/48x48/apps/ RENAME qdmr.png)\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr64.png DESTINATION share/icons/hicolor/64x64/apps/ RENAME qdmr.png)\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr72.png DESTINATION share/icons/hicolor/72x72/apps/ RENAME qdmr.png)\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/qdmr96.png DESTINATION share/icons/hicolor/96x96/apps/ RENAME qdmr.png)\nendif(UNIX AND NOT APPLE)\n\nif (UNIX AND NOT APPLE AND ${INSTALL_APPSTREAM_DATA})\n  install(FILES ${PROJECT_SOURCE_DIR}/dist/de.darc.dm3mat.qdmr.metainfo.xml DESTINATION share/metainfo/)\nendif(UNIX AND NOT APPLE AND ${INSTALL_APPSTREAM_DATA})\n"
  },
  {
    "path": "dist/de.darc.dm3mat.qdmr.metainfo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!-- Copyright (C) 2022-2025 Hannes Matuschek <dm3mat@darc.de> -->\n\n<component type=\"desktop-application\">\n  <id>de.darc.dm3mat.qdmr</id>\n  <launchable type=\"desktop-id\">qdmr.desktop</launchable>\n  <name>QDMR</name>\n  <developer id=\"de.darc.dm3mat\">\n    <name>Hannes Matuschek</name>\n  </developer>\n  <project_license>GPL-3.0+</project_license>\n  <metadata_license>CC-BY-SA-3.0</metadata_license>\n  <summary>Codeplug programming software for DMR radios</summary>\n\n  <description>\n    <p>\n      QDMR is a Qt application to program DMR radios. DMR is a digital modulation\n      standard used in amateur and commercial radio. To this end, QDMR is an\n      alternative codeplug programming software (CPS) that supports several radios\n      of several manufacturers. Including AnyTone AT-D868UVE, AT-D878UV, AT-878UVII,\n      AT-D578UV, TyT MD-390, MD-UV380, MD-UV390, MD-2017, Retevis RT8, RT3S, RT82, RT84,\n      Radioddity RD5R, GD-77, GD-73, Baofeng DM-1701, DR-1801-A6, DMR-6X2(PRO), DM-32UV\n      and radios running the OpenGD77 firmware.\n    </p>\n  </description>\n\n  <screenshots>\n    <screenshot type=\"default\">\n      <image>https://raw.githubusercontent.com/hmatuschek/qdmr/master/doc/fig/qdmr-channels.png</image>\n    </screenshot>\n  </screenshots>\n\n  <url type=\"homepage\">https://dm3mat.darc.de/qdmr/</url>\n  <url type=\"bugtracker\">https://github.com/hmatuschek/qdmr/issues</url>\n\n  <content_rating type=\"oars-1.1\"/>\n</component>\n\n  \n"
  },
  {
    "path": "dist/fedora/Dockerfile",
    "content": "FROM fedora:rawhide\n\nRUN dnf -y install fedora-packager rpmdevtools wget \\\n        cmake docbook5-style-xsl xsltproc gcc-c++ hicolor-icon-theme librsvg2-tools \\\n        pkgconfig qt6-qtbase-devel qt6-qtconnectivity-devel qt6-qtpositioning-devel \\\n        qt6-qtserialport-devel qt6-qtbase-devel libusb1-devel yaml-cpp-devel qt6-qttools-devel\n\nRUN rpmdev-setuptree\n\nCMD /bin/bash"
  },
  {
    "path": "dist/fedora/docker-build.sh",
    "content": "#!/bin/bash \n\ncp /mnt/qdmr.spec /root/rpmbuild/SPECS\nVERSION=\"0.13.2\"\nTARBALL=\"v${VERSION}.tar.gz\"\n\n# Download sources\ncd /root/rpmbuild/SOURCES \nif  [ ! -f $TARBALL ]; then\n    wget https://github.com/hmatuschek/qdmr/archive/refs/tags/$TARBALL\nfi\n\n# Copy specs and build RPM\ncd /root/rpmbuild/SPECS\nrpmbuild -ba qdmr.spec\n\n\n# Extract \n# cp /root/rpmbuild/RPMS/x86_64/*.rpm /mnt/\n"
  },
  {
    "path": "dist/fedora/qdmr.spec",
    "content": "#\n# spec file for package qdmr\n#\n# Copyright (c) 2021-2025, Martin Hauke <mardnh@gmx.de>\n#\n# All modifications and additions to the file contributed by third parties\n# remain the property of their copyright owners, unless otherwise agreed\n# upon. The license for this file, and modifications and additions to the\n# file, is the same license as for the pristine package itself (unless the\n# license for the pristine package is not an Open Source License, in which\n# case the license is the MIT License). An \"Open Source License\" is a\n# license that conforms to the Open Source Definition (Version 1.9)\n# published by the Open Source Initiative.\n\n\n%define ext_man .gz\n%define sover   0\n%define realver 0.14.0\nName:           qdmr\nVersion:        %{realver}\nRelease:        0fedora\nSummary:        Graphical code-plug programming tool for DMR radios\nLicense:        GPL-3.0-or-later\nGroup:          Productivity/Hamradio/Other\nURL:            https://dm3mat.darc.de/qdmr/\n#Git-Clone:     https://github.com/hmatuschek/qdmr.git\nSource:         https://github.com/hmatuschek/qdmr/archive/refs/tags/v%{realver}.tar.gz\nBuildRequires:  cmake\nBuildRequires:  docbook5-style-xsl\nBuildRequires:  xsltproc\nBuildRequires:  gcc-c++\nBuildRequires:  hicolor-icon-theme\nBuildRequires:  librsvg2-tools\nBuildRequires:  pkgconfig\nBuildRequires:  pkgconfig(Qt6Core)\nBuildRequires:  pkgconfig(Qt6Network)\nBuildRequires:  pkgconfig(Qt6Positioning)\nBuildRequires:  pkgconfig(Qt6SerialPort)\nBuildRequires:  pkgconfig(Qt6Test)\nBuildRequires:  pkgconfig(Qt6UiTools)\nBuildRequires:  pkgconfig(Qt6Widgets)\nBuildRequires:  pkgconfig(libusb-1.0)\nBuildRequires:  pkgconfig(yaml-cpp)\n\n\n%description\nqdmr is a Qt application to program DMR radios. DMR is a digital modulation\nstandard used in amateur and commercial radio. To this end, qdmr is an\nalternative codeplug programming software (CPS) that supports several radios\nacross several manufacturers.\n\nCurrently supported devices are:\n * Radioddity/Baofeng RD-5R, GD-73, GD-77\n * TYT MD-380, MD-390, MD-UV380, MD-UV390, MD-2017\n * Retevis RT8, RT3S, RT82\n * Open GD77 firmware (GD77, RD-5R, DM-1801, MD-UV390, RT-3S & DM1701)\n * AnyTone AT-D868UVE, AT-D878UV, AT-878UVII, AT-D578UV, AT-D578UVII\n * BTech DMR-6X2, DMR-6X2PRO, DM-1701, DR-1801UVA6, DM-32UV\n\n%package -n libdmrconf%{sover}\nSummary:        Graphical code-plug programming tool for DMR radios\nGroup:          System/Libraries\n\n%description -n libdmrconf%{sover}\nqDMR is a simple to use and feature-rich code-plug programming software\n(CPS) for cheap DMR radios.\n\nThis subpackage contains shared library part of libdmrconf.\n\n%package devel\nSummary:        Development files for dmrconf\nGroup:          Development/Libraries/C and C++\nRequires:       libdmrconf%{sover} = %{version}\n\n%description devel\nqDMR is a simple to use and feature-rich code-plug programming software\n(CPS) for cheap DMR radios.\n\nThis subpackage contains libraries and header files for developing\napplications that want to make use of libdmrconf.\n\n%prep\n%setup -q -n %{name}-%{realver}\n\n%build\n%cmake -DBUILD_MAN=ON -DDOCBOOK2MAN_XSLT=docbook_man.fedora.xsl -DINSTALL_UDEV_PATH=%{_udevrulesdir}\n%cmake_build\n\n%install\n%cmake_install\n\n%post -n libdmrconf%{sover} -p /sbin/ldconfig\n%postun -n libdmrconf%{sover} -p /sbin/ldconfig\n\n%files\n%license LICENSE\n%doc README.md\n%{_bindir}/dmrconf\n%{_bindir}/qdmr\n%{_udevrulesdir}/99-qdmr.rules\n%{_datadir}/applications/qdmr.desktop\n%{_metainfodir}/de.darc.dm3mat.qdmr.metainfo.xml\n%{_datadir}/icons/hicolor/*/apps/qdmr.png\n%{_mandir}/man1/dmrconf.1%{?ext_man}\n%{_mandir}/man1/qdmr.1%{?ext_man}\n\n%files -n libdmrconf%{sover}\n%{_libdir}/libdmrconf.so.*\n\n%files devel\n%{_includedir}/libdmrconf/\n%{_libdir}/libdmrconf.so\n\n%changelog\n* Sat Mar 21 2026 Hannes Matuschek <dm3mat@darc.de>\n  - Updated to 0.14.0\n* Sun Nov 30 2025 Hannes Matuschek <dm3mat@darc.de>\n  - Updated to 0.13.2\n* Tue Oct 21 2025 Hannes Matuschek <dm3mat@darc.de>\n  - Updated to 0.13.1\n* Sun Oct 19 2025 Hannes Matuschek <dm3mat@darc.de>\n  - Updated to 0.13.0\n* Thu Jul 25 2024 Hannes Matuschek <dm3mat@darc.de>\n  - Updated to 0.12.0\n  - added translations\n  - updated description\n  - fixed so-version\n  - adapted from openSUSE spec file\n"
  },
  {
    "path": "dist/macosx/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleExecutable</key>\n\t<string>qdmr</string>\n\n\t<key>CFBundleGetInfoString</key>\n\t<string>QDMR 0.4.1</string>\n\t\n\t<key>CFBundleIdentifier</key>\n\t<string>com.github.hmatuschek.qdmr</string>\n\t\n\t<key>CFBundleIconFile</key>\n\t<string>qdmr.icns</string>\n\t\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>1.0</string>\n\t\n\t<key>CFBundleName</key>\n\t<string>QDMR</string>\n\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\n\t<key>CFBundleShortVersionString</key>\n\t<string>0.4</string>\n\n\t<key>CFBundleVersion</key>\n\t<string>0.4.1</string>\n\n\t<key>LSMinimumSystemVersion</key>\n\t<string>10.9.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "dist/qdmr.desktop.in",
    "content": "[Desktop Entry]\nName=QDMR\nGenericName=Codeplug programming software\nComment=A universal codeplug programming software for DMR radios\nExec=qdmr\nIcon=qdmr\nType=Application\nCategories=Network;HamRadio;\nTerminal=false\nX-Desktop-File-Install-Version=0.24\n"
  },
  {
    "path": "doc/CMakeLists.txt",
    "content": "#\n# man-page generation\n#\nmessage(STATUS \"Using xsltproc: ${XSLTPROC_EXECUTABLE}\")\nconfigure_file(\"dmrconf.in.xml\" \"dmrconf.xml\")\nconfigure_file(\"qdmr.in.xml\" \"qdmr.xml\")\n\nadd_custom_command(\n  OUTPUT dmrconf.1\n  COMMAND ${XSLTPROC_EXECUTABLE}\n          -o ${CMAKE_CURRENT_BINARY_DIR}/dmrconf.1\n          ${CMAKE_CURRENT_SOURCE_DIR}/${DOCBOOK2MAN_XSLT}\n          ${CMAKE_CURRENT_BINARY_DIR}/dmrconf.xml\n  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dmrconf.xml\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  COMMENT \"Generating man-page for dmrconf.\"\n  VERBATIM)\n\nadd_custom_command(\n  OUTPUT qdmr.1\n  COMMAND ${XSLTPROC_EXECUTABLE}\n          -o ${CMAKE_CURRENT_BINARY_DIR}/qdmr.1\n          ${CMAKE_CURRENT_SOURCE_DIR}/${DOCBOOK2MAN_XSLT}\n          ${CMAKE_CURRENT_BINARY_DIR}/qdmr.xml\n  DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/qdmr.xml\n  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}\n  COMMENT \"Generating man-page for qdmr.\"\n  VERBATIM)\n\nif (${BUILD_MAN})\n  # Create man page from docbook\n  add_custom_target(dmrconf_manpage ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dmrconf.1)\n  add_custom_target(qdmr_manpage ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/qdmr.1)\n  # install man page on unix systems\n  if (UNIX)\n    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dmrconf.1\n                  ${CMAKE_CURRENT_BINARY_DIR}/qdmr.1\n      DESTINATION ${CMAKE_INSTALL_FULL_MANDIR}/man1/)\n  endif (UNIX)\nendif(${BUILD_MAN})\n\n#\n# optional API docs\n#\nif (${BUILD_DOCS})\n  set(DOXYGEN_PROJECT_NAME \"libdrmconf\")\n  set(DOXYGEN_PROJECT_BRIEF \"A library to program DMR radios.\")\n  set(DOXYGEN_BRIEF_MEMBER_DESC YES)\n  set(DOXYGEN_JAVADOC_AUTOBRIEF YES)\n  set(DOXYGEN_EXAMPLE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/code\")\n  set(DOXYGEN_DOTFILE_DIRS \"${CMAKE_CURRENT_SOURCE_DIR}/fig\")\n  set(DOXYGEN_ENABLE_PREPROCESSING YES)\n  set(DOXYGEN_MACRO_EXPANSION YES)\n  set(DOXYGEN_EXPAND_ONLY_PREDEF YES)\n  set(DOXYGEN_PREDEFINED __attribute__(x) =)\n  set(DOXYGEN_IMAGE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/fig\")\n  doxygen_add_docs(apidocs\n    ALL\n    ${CMAKE_SOURCE_DIR}/lib\n    COMMENT \"Build API documentation\")\n  install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html\n          DESTINATION ${CMAKE_INSTALL_DOCDIR}/libdmrconf/)\nendif(${BUILD_DOCS})\n"
  },
  {
    "path": "doc/code/anytone_2tonefunction.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Frequency 1 in 0.1Hz, little endian                           | Frequency 2 in 0.1Hz, little endian                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Response                      | Name, up to 7 ASCII chars                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Pad byte 0x00                 | Unused set to 0x00                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Response: 0=None, 1=Tone, 2=ToneRespond\n"
  },
  {
    "path": "doc/code/anytone_2toneid.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Frequency 1 in 0.1Hz, little endian                           | Frequency 2 in 0.1Hz, little endian                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unused set to 0x00                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Name, up to 7 ASCII chars                                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                            | Pad byte 0x00                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_2tonesettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused set to 0x00                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                            | Tone 1 duration in 100ms      | Tone 2 duration in 100ms      | Long tone duration in 100ms   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Gap duration in 10ms          | Auto reset time in 10s        | Sidetone enable               | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_5tonefunction.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Function                      | Response                      | ID length                     | ID up to 24 BCD digits     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                            | Name up to 7 ASCII chars   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                            | Pad set to 0x00               | Unused set to 0x00         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Function: Specifies the function to perform. 0=OpenSquelch, 1=CallAll, 2=EmergencyAlarm, 3=RemoteKill, 4=RemoteStun,\n   5=RemoteWakeup, 6=GroupCall\n - Response: 0=None, 1=Tone, 2=ToneRespond\n"
  },
  {
    "path": "doc/code/anytone_5tonefunctionlist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Five-tone function 0                                                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Five-tone function 1                                                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n01e0 | Five-tone function 15                                                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n01fc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_5toneid.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused set to 0x00            | Standard                      | ID length                     | Tone duration in ms           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | ID up to 40 BCD digits                                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Name, up to 7 ASCII chars                                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Pad byte 0x00                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Standard: 0=ZVEI1, 1=ZVEI2, 2=ZVEI3, 3=PZVEI, 4=DZVEI, 5=PDZVEI, 6=CCIR1, 7=CCIR2, 8=PCCIR, 9=EEA, 10=EuroSignal, 11=NATEL,\n   12=MODAT, 13=CCITT, 14=EIA\n\n"
  },
  {
    "path": "doc/code/anytone_5toneidlist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Five-tone ID 0                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Five-tone ID 1                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c60 | Five-tone ID 99                                                                                                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c7c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_5tonesettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown settings                                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Unused set to 0x00            | Decoding response             | Decoding standard             | Radio ID length               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Decoding tone duration in ms  | Radio ID up to 14 BCD digits                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Post encode delay in 10ms     | PTT ID 0=off [5,75]           | Auto reset time in 10s        | First delay in 10ms           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Sidetone enable               | Unknown                       | Stop code [0,15]              | Stop time in 10ms             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Decode time in 10ms           | Delay after stop in 10ms      | Pre time in 10ms              | Unused set to 0x00                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Unused set to 0x00            | BOT standard                  | BOT ID length                 | BOT tone duration in ms       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | BOT ID, up to 24 BCD digits                                                                                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Unused set to 0x00                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | Unused set to 0x00            | EOT standard                  | EOT ID length                 | EOT tone duration in ms       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | EOT ID up to 24 BCD digits                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Unused set to 0x00                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Decoding response: 0=None, 1=Tone, 2=ToneRespond\n - Decoding/BOT/EOT standard: 0=ZVEI1, 1=ZVEI2, 2=ZVEI3, 3=PZVEI, 4=DZVEI, 5=PDZVEI, 6=CCIR1, 7=CCIR2, 8=PCCIR, 9=EEA, 10=EuroSignal,\n   11=NATEL, 12=MODAT, 13=CCITT, 14=EIA\n\n"
  },
  {
    "path": "doc/code/anytone_alarmsetting.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Analog alarm settings                                                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                                                            | Digital alarm settings                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 |                                                               | Unused set to 0x00                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_analogalarm.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused 0x00                   | Standard                      | ID length                     | Tone duration in ms           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | ID up to 40 BCD encoded digits                                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Name, max 7 ASCII chars                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Pad byte 0x00                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField details:\n - Standard: Specifies the 5-tone standard. 0=ZVEI1, 1=ZVEI2, 2=ZVEI3, 3=PZVEI, 4=DZVEI, 5=PDZVEI, 6=CCIR1, 7=CCIR2, 8=PCCIR, 9=EEA,\n   10=EURO SIGNAL, 11=NATEL, 12=MODAT, 13=CCITT, 14=EIA.\n"
  },
  {
    "path": "doc/code/anytone_analogquickcall.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Type                      | Analog contact index, 0xff=none   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nSome field details:\n - Type: 0=none, 1=DTMF, 2=2Tone, 3=5Tone\n"
  },
  {
    "path": "doc/code/anytone_analogquickcalls.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Quick call 0                                                  | Quick call 1                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Quick call 2                                                  | Quick call 3                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Unused, filled with 0x00                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00fc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_bootsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Intro line 1, 16 x ASCII, 0-terminated and padded                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Intro line 2, 16 x ASCII, 0-terminated and padded                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Boot password, 8 x ASCII, only number chars, 0-terminated and padded                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Unused, filled with 0x00                                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit code, little endian                              | DCS receive code, little endian                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS frequency in 0.1Hz, little endian                | 2-tone decode index, 0-based, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0 |SquelchMode| 0   0   0   0 | 0   0 |OptSig | 0   0 | TxPer | Scan list index               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|RGP|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Pad byte set to 0x00          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Device specific settings                                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset.\n - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz).\n - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo.\n - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX.\n - TAr: Enable talkaround.\n - CaC: Enable call confirm.\n - RXO: Enable RX only.\n - CTR: Enable CTCSS phase reversal.\n - TDC: Enable TX DCS code.\n - TCT: Enable TX CTCSS tone.\n - RDC: Enable RX DCS code.\n - RCT: Enable RX CTCSS tone.\n - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling,\n   4 = CTCSS/DCS or Optional Signaling\n - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone\n - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free.\n - LWK: Enable lone worker.\n - EEE: Enable enhanced encryption\n - RGP: Enable RX GPS:\n - EAT: Enable adative TDMA\n - EST: Enable simplex TDMA,\n - SMC: SMS confirmation,\n - TSL: Time slot where 0=TS1, 1=TS2\n"
  },
  {
    "path": "doc/code/anytone_contact.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Type                          | Name 16 x ASCII chars                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                            | 18 unused bytes set to 0                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                            | ID 8 digits BCD encoded    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...  big endian                                                                                | Call alert                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | 60 unused bytes, set to 0                                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_contactmapentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID and group call flag, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - DMR ID and group call flag: The ID is encoded in BCD litte-endian, shifted to the left by one bit. Bit 0 is then the group-call\n   flag. The combined id and flag is then stored as a 32bit little-endian.\n"
  },
  {
    "path": "doc/code/anytone_digitalalarm.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Action, 0=none                | Alarm duration in seconds     | TX duration in seconds        | RX duration in seconds        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Channel index, 0-based, little endian                         | Channel select 1=current      | Alarm repeat 0=continuous     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Voice switch BC dur in min+1  | Area switch BC dur in min+1   | VOX enable                    | RX alarm enable               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField details:\n - Action: Specifies the alarm action. 0=none, 1=TX and background, 2=TX and non-local alarm, 3=TX and local alarm\n - Channel select: If 0 use specified channel index else current channel.\n - Alarm repeat: Number of alarm repetitions, 0=continuous.\n - Voice switch broadcast duration: Specifies the time in minutes +1. That is 0=1min, 1=2min, ...\n - Area switch broadcast duration: Specifies the time in minutes +1. That is 0=1min, 1=2min, ...\n"
  },
  {
    "path": "doc/code/anytone_digitalalarmextension.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Call type                     | Unused set to 0x00                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                            | Destination ID             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ... 8 digit BCD, big endian                                                                    | Unused set to 0x00         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField details:\n - Call type: 0=private, 1=group, 2=all\n"
  },
  {
    "path": "doc/code/anytone_dmraprssettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Manual TX interval in sec.    | Auto TX interval in sec.      | Enable fixed location beacon  | Latitude degrees              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Latitude minutes              | Latitude seconds              | South flag (0=north, 1=south) | Longitude degrees             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Longitude minutes             | Longitude seconds             | West flag (0=east, 1=west)    | Transmit power                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Revert channel 0 index, 0-based, little endian.               | Revert channel 1 index, 0-based, little endian.               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Revert channel 6 index, 0-based, little endian.               | Revert channel 7 index, 0-based, little endian.               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Target DMR ID, 8 digit BCD, big endian                                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Call type                     | Timeslot override             | Unused, set to 0x00                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nSome field details:\n - Transmit power: 0=low, 1=mid, 2=high, 3=turbo.\n - Revert channel indices: Global index of channel, 0x0fa0=VFO A, 0x0fa1=VFO B, 0x0fa2=selected, 0xffff=none.\n - Call type: 0=private, 1=group, 2=all call.\n - Timeslot override: 0=same as channel, 1=TS1, 2=TS2\n"
  },
  {
    "path": "doc/code/anytone_dtmfcontact.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n 0 | Number, 14 digits BCD encoded, big-endian, 0-padded                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                            | Number of digits              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n 8 | Name, 15 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                            | 0x00 pad                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_dtmfidlist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | DTMF number 0                                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | DTMF number 1                                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00f0 | DTMF number 15                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00fc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_dtmfsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Interval symbol [0,15]        | Group code [0, 15]            | Response                      | Pre time in 10ms              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | First digit duration in 10ms  | Auto reset time in 10s        | Radio ID, 3 digits?                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                            | Post enc. delay in 10ms       | PTT ID pause in seconds       | PTT ID enable                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | D-code pause in seconds       | Side tone enable              | Unused set to 0x00                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | BOT ID up to 16 digits, 0xff terminated/padded                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | EOT ID up to 16 digits, 0xff terminated/padded                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Remote kill ID, up to 16 digits, 0xff terminated/padded                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Remote stun ID, up to 16 digits, 0xff terminated/padded                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Response: 0=None, 1=Tone, 2=ToneRespond\n"
  },
  {
    "path": "doc/code/anytone_generalsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enable key tone               | Display mode                  | Enable automatic key lock     | Automatic shutdown time       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unknown                       | Unknown                       | Boot display                  | Enable boot password          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unknown                       | Squelch level VFO A           | Squelch level VFO B           | Power save mode               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | VOX sensitivity               | VOX delay in 100+500*n ms     | VFO scan type                 | MIC gain                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | PF1 short press function      | PF2 short press function      | PF3 short press function      | P1 short press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | P2 short press function       | Work mode A                   | Work mode B                   | Unknown                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Work mode MEM zone A          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Work mode MEM zone B          | Unknown                       | Enable recording              | Unknown                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                            | Display brightness            | Backlight duration            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Enable GPS                    | Enable SMS alert              | Unknown                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Work mode main channel set    | Enable sub channel            | Unknown                       | Enable call alert             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | GPS Time zone                 | Talk permit tone              | Digital call reset tone       | VOX source                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unknown                                                       | Idle channel tone             | Menu exit time                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Unknown                       | Startup tone                  | Enable call end prompt        | Max volume                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unknown                                                                                       | GPS RX positions              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Unknown                       | PF1 long press function       | PF2 long press function       | PF3 long press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | P1 long press function        | P2 long press function        | Long press duration           | Enable Volume change prompt   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | Auto repeater A direction     | Unknown                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                            | Display later caller          | Unknown                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Unknown                       | Display clock                 | Max head phone volume         |  Unknown                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 |                                                                                               | Enable enhanced audio         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | VFO Scan UHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | VFO Scan UHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | VFO Scan VHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | VFO Scan VHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Auto rep. offset index UHF    | Auto rep. offset index VHF    | Unknown                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70  ...                                                            | Call tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | Call tone frequency 2 in Hz, little endian                    | Call tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Call tone frequency 4 in Hz, little endian                    | Call tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | Call tone duration 1 in ms, little endian                     | Call tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Call tone duration 3 in ms, little endian                     | Call tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Call tone duration 5 in ms, little endian                     | Idle tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Idle tone frequency 2 in Hz, little endian                    | Idle tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | Idle tone frequency 4 in Hz, little endian                    | Idle tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Idle tone duration 1 in ms, little endian                     | Idle tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Idle tone duration 3 in ms, little endian                     | Idle tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Idle tone duration 5 in ms, little endian                     | Reset tone frequency 1 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Reset tone frequency 2 in Hz, little endian                   | Reset tone frequency 3 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Reset tone frequency 4 in Hz, little endian                   | Reset tone frequency 5 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4 | Reset tone duration 1 in ms, little endian                    | Reset tone duration 2 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 | Reset tone duration 3 in ms, little endian                    | Reset tone duration 4 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Reset tone duration 5 in ms, little endian                    | Record delay in 200ms         | Call display mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | Device specific settings                                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ncc  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n - GPS time zone: 00h=UTC-12, ..., 0Ch=UTC, ..., 19h=UTC+13\n"
  },
  {
    "path": "doc/code/anytone_grouplist.txt",
    "content": "      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\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00  | 64 indices of group calls, 32bit each, little endian, default 0xffffffff                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc   ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n100 | Name, 16 x ASCII, 0-padded                                                                                                 ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10c  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n110 | 16 unused bytes set to 0                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n11c  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_hotkey.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Type (0=call, 1=menu)         | Menu item                     | Call type                     | Digital call type             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Contact index, 0-based, little endian, 0xffffffff=none                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Message index, 0-based        | Unused filled with 0x00                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField details:\n - Menu item: 1=Show SMS nenu, 2=Show new SMS menu, 3=Show hot text menu, 4=Show SMS inbox, 5=Show SMS outbox, 6=Show contacts,\n   7=Show manual dial menu.\n - Call type: 0=analog, 1=digital (only when Type=call).\n - Digital call type: 0xff = off, 0=Group call, 1=Private call, 2=All call, 3=Hot text, 4=Call tip (?), 5=Status message.\n - Contact index: May be analog quick call index (if Type=call, and Call type=analog) or contact index (if Type=call and\n   Call type=digital). 0xffffffff = none.\n - Message index: May be SMS message index (if Digital call type=Hot text) or status message index\n   (if Digital call type=Status message). 0xff = none.\n"
  },
  {
    "path": "doc/code/anytone_hotkeysettings.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Hotkey setting 0                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030 | Hotkey setting 1                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n005c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0330 | Hotkey setting 17                                                                                                          ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n035c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_message.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Message text, 99 ASCII chars, 0-padded                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                            |  157 unused bytes, 0x00    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_messagelist.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused 0x00                   | Unused 0x00                   | Next index, 0xff=EOL          | Current index, 0xff=none      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 12 unused bytes set to 0x00                                                                                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_radioid.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Radio ID encoded as 8 BCD digits, big endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unused, set to 0x00           | Name, 16 x ASCII, 0-padded                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                            | 11 unused bytes, set to 0x00                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_repeateroffsetfrequencies.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Offset frequency 0                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Offset frequency 1                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n03e4 | Offset frequency 249                                                                                                          |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n03e8 | Unused, filled with 0                                                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n03ec  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_scanlist.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0x00           | Priority Channel Select       | Primary channel index 1 +1, little endian, 0=selected         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Secondary channel index 2 +1, little endian, 0=selected       | Look back time A, in x10sec                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Look back time B, in x10sec                                   | Dropout delay, in x10sec                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Dwell time, in x10sec                                         | Revert channel type           | Name, 16 ASCII, 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | 50 x 16bit channel indices, little endian, 0xffff=empty                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | 12 unused bytes , set to 0x00                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_statusmessages.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Status message 0, 32 x ASCII, 0 terminated and padded                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Status message 0, 32 x ASCII, 0 terminated and padded                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n03e0 | Status message 31, 32 x ASCII, 0 terminated and padded                                                                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n03fc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_wfmchannellist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Channel frequency 0, 8 digit BCD, big endian                                                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Channel frequency 1, 8 digit BCD, big endian                                                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n018c | Channel frequency 99, 8 digit BCD, big endian                                                                                 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0190 | Unused, filled with 0                                                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n019c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/anytone_zonechannellist.txt",
    "content": "      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\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000 | Zone 0, channel index A little endian                         |  Zone 1, channel index A little endian                        |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n010 | Zone 2, channel index A little endian                         |                                                            ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1f0  ...                                                            | Zone 249, channel index A little endian                       |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1f4 | 12 pad bytes set to 0x00                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1fc  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n200 | Zone 0, channel index B little endian                         |  Zone 1, channel index B little endian                        |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n210 | Zone 2, channel index B little endian                         |                                                            ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3f0  ...                                                            | Zone 249, channel index B little endian                       |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3f4 | 12 pad bytes set to 0x00                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3fc  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d578uv_airbandchannel.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Frequency, 8 digit BCD, big-endian                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Name 16 x ASCII, 0-padded and terminated                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Unused, filled with 0x00                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d578uv_airbandchannellist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Air band channel 0                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Air band channel 1                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c60 | Air band channel 99                                                                                                        ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c7c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d578uv_aprssettingext.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown settings                                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                            | Fixed altitude in feet, little endian                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 |RST|RNF|RWX|RMS|RIT|ROB|RME|RPO| 0   0   0   0   0   0   0 |ROF| Unknown settings                                              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | FM APRS Frequency 0, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | FM APRS Frequency 1, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | FM APRS Frequency 2, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | FM APRS Frequency 3, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | FM APRS Frequency 4, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | FM APRS Frequency 5, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | FM APRS Frequency 6, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n26 | FM APRS Frequency 7, 8 digit BCD, big-endian                                                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Unknown                                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RPO: Report position flag.\n - RME: Report MIC-E flag.\n - ROB: Report object flag.\n - RIT: Report item flag.\n - RMS: Report message flag.\n - RWX: Report weather flag.\n - RNF: Report NEMA flag.\n - RST: Report status flag.\n - ROF: Report other flag.\n"
  },
  {
    "path": "doc/code/d578uv_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit code, little endian                              | DCS receive code, little endian                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS frequency in 0.1Hz, little endian                | 2-tone decode index, 0-based, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0 |SquelchMode| 0   0 |PTT-ID | 0   0 |OptSig | 0   0 | TxPer | Scan list index               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|RGP|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0   0   0   0 |EXR|BTE|ETM|RNG| 0   0   0   0   0   0 |APRSRep| Analog APRS PTT               | Digital APRS PTT              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | GPS System Index              | Freq. corr. signed in 10Hz    | Analog crambler enable        | 0   0   0   0   0 |SMF|RnK|Muk|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unused set to 0x00            | 0   0   0   0 |DAD| 0   0   0 | Unused set to 0x00                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset.\n - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz).\n - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo.\n - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX.\n - TAr: Enable talkaround.\n - CaC: Enable call confirm.\n - RXO: Enable RX only.\n - CTR: Enable CTCSS phase reversal.\n - TDC: Enable TX DCS code.\n - TCT: Enable TX CTCSS tone.\n - RDC: Enable RX DCS code.\n - RCT: Enable RX CTCSS tone.\n - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling,\n   4 = CTCSS/DCS or Optional Signaling\n - PTT-ID: When to send the PTT-ID, where 0=off, 1=start, 2=end, 3=both.\n - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone\n - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free.\n - LWK: Enable lone worker.\n - EEE: Enable enhanced encryption\n - RGP: Enable RX digital (DMR) APRS\n - EAT: Enable adative TDMA\n - EST: Enable simplex TDMA,\n - SMC: SMS confirmation,\n - TSL: Time slot where 0=TS1, 1=TS2\n - XFR: Exclude from roaming\n - DAD: Data ACK disable.\n - ETM: Enable through mode\n - BTE: BlueTooth hands free enabled\n - EXR: Exclude from roaming\n - APRSRep: Enable APRS/GPS report,\n - MuK: Multiple keys\n - RnK: Random key\n - SMF: SMS forbid.\n"
  },
  {
    "path": "doc/code/d578uv_generalsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Key tones                     | Display mode                  | Auto key-lock enable          | Auto shut-down delay          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Transmit timeout              | Language                      | Boot display mode             | Boot password enable          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | VFO Frequency step            | Squelch level A               | Squelch level B               | VFO scan type                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DMR mic. gain                 | Work mode A                   | Work mode B                   | STE Type                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | STE when no signal            | Group call hang time          | Private call hang time        | Pre-wave delay                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Wake head period              | WFM channel index             | WFM Mode                      | Work mode MEM zone idx A      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Work mode MEM zone idx B      | Airband/WFM enable            | Record enable                 | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Unknown                       | Display brightness            | Unknown                       | GPS enable                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | SMS alert tone enable         | WFM monitor enable            | Unknown                       | Main channel B                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Sub channel enable            | TBST frequency                | Call alert tone enable        | GPS time zone                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Talk permit tones             | DMR call reset tone enable    | DMR idle channel tone         | Menu exit time                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Filter own ID in missed calls | Boot tone enable              | Show call end prompt          | Max. volume                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Remote stun & kill enable     | Remote monitor enable         | Get GPS position              | Long key-press duration       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Show volume bar               | Auto repeater mode A          | DMR monitor mode              | DMR monitor match CC          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | DMR monitor match ID          | DMR monitor slot hold         | Last caller display mode      | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Analog call hang time         | Show clock                    | Enable GPS template info      | Enhance sound                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | UHF VFO scan minimum frequency in 10Hz, 32bit uint, little-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | UHF VFO scan maximum frequency in 10Hz, 32bit uint, little-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | VHF VFO scan minimum frequency in 10Hz, 32bit uint, little-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | VHF VFO scan maximum frequency in 10Hz, 32bit uint, little-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Auto-repeater VHF min frequency in 10Hz, 32bit uint, litte-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Auto-repeater VHF max frequency in 10Hz, 32bit uint, litte-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | Auto-repeater UHF min frequency in 10Hz, 32bit uint, litte-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | Auto-repeater UHF max frequency in 10Hz, 32bit uint, litte-endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | Talk permit tone 1 frequency                                  | Talk permit tone 2 frequency                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | Talk permit tone 3 frequency                                  | Talk permit tone 4 frequency                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Talk permit tone 5 frequency                                  | Talk permit tone 1 duration in 10ms, 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | Talk permit tone 2 duration in 10ms, 16bit uint little endian | Talk permit tone 3 duration in 10ms, 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Talk permit tone 4 duration in 10ms, 16bit uint little endian | Talk permit tone 5 duration in 10ms, 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | Idle channel tone 1 freuency in Hz, 16bit uint little endian  | Idle channel tone 2 freuency in Hz, 16bit uint little endian  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Idle channel tone 3 freuency in Hz, 16bit uint little endian  | Idle channel tone 4 freuency in Hz, 16bit uint little endian  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | Idle channel tone 5 freuency in Hz, 16bit uint little endian  | Idle channel tone 1 duration in 10ms 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Idle channel tone 2 duration in 10ms 16bit uint little endian | Idle channel tone 3 duration in 10ms 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Idle channel tone 4 duration in 10ms 16bit uint little endian | Idle channel tone 5 duration in 10ms 16bit uint little endian |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Call reset tone 1 freuency in Hz, 16bit uint little endian    | Call reset tone 2 freuency in Hz, 16bit uint little endian    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | Call reset tone 3 freuency in Hz, 16bit uint little endian    | Call reset tone 4 freuency in Hz, 16bit uint little endian    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Call reset tone 5 freuency in Hz, 16bit uint little endian    | Call reset tone 1 duration in 10ms, 16bit uint little endian  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Call reset tone 2 duration in 10ms, 16bit uint little endian  | Call reset tone 3 duration in 10ms, 16bit uint little endian  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Call reset tone 4 duration in 10ms, 16bit uint little endian  | Call reset tone 5 duration in 10ms, 16bit uint little endian  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Auto rep. offset index UHF    | Auto rep. offset index VHF    | Unknown                       | Priority zone A index         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Priority zone B index         | Unknown                       | Caller display mode           | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4 | Bluetooth enable              | BT + int microphone           | BT + int. speaker             | Plug-in recoding tone enable  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 | GPS Ranging interval          | BT mic gain [0,4]             | BT speaker gain [0,4]         | Channel number mode           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Show current contact          | Auto-roaming interval         | Call sign color               | GPS units                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 |           |FKL|SkL| 0 |KbL|KnL| Auto-roaming wait             | Standby text color            | Standby background image      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb4 | Show last call on boot        | SMS format                    | Auto repeater mode B          | Send addr book with own code  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb8 | Boot channel enable           | Boot zone A index             | Boot zone B index             | Boot channel index A          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nbc | Unknown                       | Roaming zone index            | Repeater check enable         | Repeater check interval       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 | Repeater check recon. count   | Auto-roaming start condition  | Unknown                       | Show display separator        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc4 | Channel switch keep last call | Channel A name color          | Repeater out of range notif.  | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc8 | Auto roaming enable           | P1 key short press            | P2 key short press            | P3 key short press            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ncc | P4 key short press            | P5 key short press            | P6 key short press            | PA key short press            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd0 | PB key short press            | PC key short press            | PD key short press            | P1 key long press             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd4 | P2 key long press             | P3 key long press             | P4 key long                   | P5 key long press             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd8 | P6 key long press             | PA key long press             | PB key long press             | PC key long press             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndc | PD key long press             | Unknown                       | Out of range notify count     | Transmit timeout rekey        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne0 | Unknown                                                       | BT hold time                  | BT RX delay                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne4 | Unknown                                                                                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne8 | Unknown                                                                                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nec | Unknown                                                                                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  Bluetooth settings:\n    - BT hold time in seconds, 0=off (default), [off, 1, ..., 30, infinity].\n    - BT RX delay stored in multiples of 0.5s, 0=0.5, 1=1.0s, 2=1.5s (default), ..., 10=5.5s.\n  STE (squelch tail):\n    - STE Type: 0=Off (default), 1=Silent, 2=120deg, 3=180deg, 4=240deg, 5=55Hz\n    - STE when no signal: 0=Off (default), 1=55.2Hz, 2=259.2Hz\n  WFM/Air band:\n    - Air band/WFM enable: 0=Off (both, default), 1=WFM, 2=Air-band A, 3=Air-band B\n    - WFM mode: 0=Channel (default), 1=VFO\n    - Air band mode: 0=Channel (default), 1=VFO\n    - Air band TX offset direction: 0=positive (default), 1=negative\n    - Air band squelch: 0=Off (default), [1,5]\n  Power save:\n    - Auto shut-down delay: 0=off, 1=10m, 2=30min, 3=60min, 4=120min\n  Key settings:\n    - KnL: Knob lock enable.\n    - KbL: Key-board lock enable.\n    - SkL: Side-key lock enable.\n    - FKL: Forced key lock enable.\n    - Long key-press duration: 0=1s, 1=2s, ..., 4=5s\n  Other:\n    - Send addr book with own code: Sends ANI analog ID together with selected addr book ID.\n    - Transmit timeout in multiples of 30s. 0=Off, 1=30s, ..., 8=240s\n    - Transmit timeout rekey in seconds. 0=Off, 1=1s, ..., 255=255s.\n    - VFO Frequency step: 0=2.5k, 1=5k, 2=6.25k, 3=8.33l, 4=10k, 5=12.5k, 6=20k, 7=25k, 8=30k, 9=50k.\n    - Squelch level A/B: 0=Off, 1=1, ..., 5=5.\n    - TBST frequency: 0=1000kHz, 1=1450kHz, 2=1750kHz, 3=2100kHz.\n    - Analog call hang time in seconds, [0,30]\n    - Priority zone A/B index 0xff=Off.\n  DMR settings:\n    - Group/private call hang time in seconds 1=1s, 2=2s, ..., 30=30s, 31=30min, 32=infinity.\n    - Pre-wave delay in multiples of 20ms 0=0ms (default), 1=20ms, ..., 50=1000ms\n    - Wake head period in multiples of 20ms 0=0ms (default), 1=20ms, ..., 60=1200ms\n    - DMR monitor mode 0=Off, 1=Single slot, 2=Dual-slot.\n    - SMS format: 0=Motorola (default), 1=Hytera, 2=DMR Standard\n  Boot settings:\n    - Boot display mode: 0=Default, 1=Custom text, 2=Custom image\n    - Boot channel index A/B: Index within zone, 0xff = VFO\n  Tone settings:\n    - Talk permit tones: 0=None, 1=DMR, 2=FM, 3=Both\n    - Key tones: 0=Off, 1=1, ..., 8=8, 9=With RX tone A, 10=With RX tone B\n    - DMR idle channel tone: 0=Off, 1=Type 1, 2=Type 2, 3=Type 3.\n  Display settings:\n    - Display brightness 0=1, ..., 4=5\n    - Menu exit time in multiples of 5s: 0=5s, 1=10s, ..., 11=60s\n    - Last caller display mode: 0=Off, 1=Show ID, 2=Show Call, 3=Show both\n    - Caller display mode: 0=Show name, 1=Show call\n    - Callsign/Channel A/B name/ color: 0=Orange (default), 1=Red, 2=Yellow, 3=Green, 4=Turquoise, 5=Blue, 6=White\n    - Standby text color: 0=White, 1=Black, 2=Orange, 3=Red, 4=Yellow, 5=Green, 6=Turquoise, 7=Blue\n    - Channel number mode: 0=Channel number, 1=Channel index within zone.\n    - Standby background image: 0=Default, 1=Custom image 1, 2=Custom image 2\n  GPS settings:\n    - GPS time zone: 00h=UTC-12:00, ..., 09h=UTC-3:30, 0Ah=UTC-3:00, ..., 0Dh=UTC, ..., 11h=UTC+3:30,\n      12h=UTC+4:00, 13h=UTC+4:30, 14h=UTC+5:00, 15h=UTC+5:30, 16h=UTC+5:45, ..., 1Ah=UTC+8:30, ...,\n      1Fh=UTC+13:00\n    - GPS Ranging interval in seconds [5,255]\n    - GPS units: 0=metric, 1=imperial\n  Scan settings:\n    - VFO scan type: 0=TO (fixed time, default), 1=CO (carrier), 2=SE (stop)\n  Auto-repeater settings:\n    - Auto repeater mode A/B: 0=Off, 1=positive, 2=negative\n    - Auto rep. offset index UHF/VHF: index, 0xff=Off.\n    - Repeater check interval in multiples of 5s: 0=5s, 1=10s, ..., 9=50\n    - Repeater check recon. count 0=1, 1=2, 2=3\n    - Repeater out of range notif.: 0=None, 1=Bell, 2=Voice\n    - Out of range notify count: 0=1, 1=2, ..., 9=10.\n    - Auto-roaming start condition: 0=time, 1=repeater out of range\n    - Auto-roaming interval in minutes. 0=1min, 1=2min, ..., 255=256min.\n    - Auto-roaming wait interval in seconds, 0=off, 1=1s, ...,\n  Audio settings:\n    - Max. volume: 0=Indoors (default), 1=1, ..., 8=8.\n    - DMR mic. gain: 0=1 (default), ..., 4=5\n"
  },
  {
    "path": "doc/code/d578uv_generalsettingsextension.txt",
    "content": "        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\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  | Unknown                                                       | Alias priority                | Alias encoding                |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020  | Weather alarm enable          | Cross-band repeater enable    | Unknown                       | Speaker mode                  |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0024  | Unknown                       | Mic-speaker path              | GPS mode                      | BT PTT hold enable            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0028  | PTT sleep time                | Fan control                   | Weather channel index         | Man dial group call hang time |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  | Man dial priv call hang time  | Knob short press              | Knob long press               | Channel B name color          |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030  | Encryption mode               | Pro-mode enable               | STE duration                  | HandOpMode                    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0034  | Zone name A color             | Zone name B color             | Auto shut-down mode           | Air band mode                 |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0038  | Air band channel index        | Air band TX offset direction  | Air band squelch              |                   |SCC|STS|SCT|\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  | FM channel idle tone enable   | Date format                   | Unknown                       | FM mic gain                   |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048  | Unknown                       | Transmit timeout predict      | GPS roaming enable            | Cross band rep. CC match mode |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c  | Cross band rep. Ch A TS       | Cross band rep. Ch B TS       | Unknown                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054  | BT handset VOX level          | BT handset VOX delay          | BT handset vol A              | BT handset vol B              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0058  | Call end tone frequency 1 in Hz, 16bit uint little endian     | Call end tone frequency 2 in Hz, 16bit uint little endian     |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n005c  | Call end tone frequency 3 in Hz, 16bit uint little endian     | Call end tone frequency 4 in Hz, 16bit uint little endian     |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0060  | Call end tone frequency 5 in Hz, 16bit uint little endian     | Call end tone 1 duration in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0064  | Call end tone 2 duration in 10ms, 16bit uint little endian    | Call end tone 3 duration in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0068  | Call end tone 4 duration in 10ms, 16bit uint little endian    | Call end tone 5 duration in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c  | All-call tone frequency 1 in Hz, 16bit uint little endian     | All-call tone frequency 2 in Hz, 16bit uint little endian     |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0070  | All-call tone frequency 3 in Hz, 16bit uint little endian     | All-call tone frequency 4 in Hz, 16bit uint little endian     |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0074  | All-call tone frequency 5 in Hz, 16bit uint little endian     | All-call tone duration 1 in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0078  | All-call tone duration 2 in 10ms, 16bit uint little endian    | All-call tone duration 3 in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n007c  | All-call tone duration 4 in 10ms, 16bit uint little endian    | All-call tone duration 5 in 10ms, 16bit uint little endian    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0080  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0084  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0088  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n008c  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0090  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0094  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0098  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n009c  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00a0  | Unknown                                                                                                                       |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00a4  | Unknown                                                                                                                    ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n01fc   ...                                                                                                                            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - PTT sleep time: in minutes [1,4], 0=infinite\n  - BT handset VOX level [0,8], default=0\n  - BT handset VOX delay in 0.5s. 0=0.5s, 1=1.0s, ..., 9=5.0s\n  - BT handset vol A/B [0,31]\n  - STE duration in ms. 0=10ms (default), 1=20ms, ..., 99=1000ms.\n  - Auto shut-down mode: 0=Reset timer on call, 1=no reset.\n  - Mic-speaker path: 0=Master, 1=Slave\n  - Fan control: 0=PTT, 1=Temperature, 2=Both\n  - Speaker mode: 0=Microphone, 1=Radio, 2=Both\n  - Encryption mode: 0=Common, 1=AES (default)\n  - HandOpMode: Select hand microphone type. 0=Serial port (common AnyTone hand mic), 1=Voltage Detect (simple PTT mic)\n  - Manual dial group/private call hang time in seconds 1=1s, 2=2s, ..., 30=30s, 31=30min, 32=infinity.\n  - Talk permit melody frequencies: 16bit uint little endian in Hz.\n  - Channel A/B name/Zone A/B name color: 0=Orange (default), 1=Red, 2=Yellow, 3=Green, 4=Turquoise, 5=Blue, 6=White\n  - SCT: Show channel type enable\n  - STS: Show time slot enable\n  - SCC: Show color code enable\n  - Date format: 0=yyyy/mm/dd, 1=dd/mm/yyyy\n  - GPS mode: 0=GPS, 1=Baidu, 2=Both\n  - Cross band rep. CC match mode: 0 = None, 1 = Match channel A CC, 2 = Match channel B CC\n  - Cross band rep. Ch A/B TS: 0=Off, 1=TS1, 2=TS2\n  - FM mic gain: 0=1, ..., 4=5.\n  - Alias priority: 0=Off, 1=contact, 2=over-the-air\n  - Alias encoding: 0=ISO8, 1=ISO7, 2=Unicode\n"
  },
  {
    "path": "doc/code/d578uv_hotkeysettings.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Hotkey setting 0                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030 | Hotkey setting 1                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n005c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0450 | Hotkey setting 23                                                                                                          ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n047c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uv_callsigndbentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Call type                     | DMR ID, 8-digit BCD, big-endian                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                            | 0   0   0 |FRD| 0   0 | Ring  | Body, up to 94 bytes, string list, 0-terminated        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n  - Call type: The call type of the entry, 0=Private, 1=Group and 2=All call.\n  - FRD: Friend flag.\n  - Ring: Ring tone settings, 0=Off, 1=Tone, 2=Online(?).\n  - Body: String list variable size. Each string in list 0-terminated. List is [Name, City, Call, State, Country, Comment].\n    Maximum length is 96 including 0 bytes.\n"
  },
  {
    "path": "doc/code/d868uv_callsigndblimit.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Number of db entries, little endian                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | End of database pointer, little endian                                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unused set to 0x00000000                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Unused set to 0x00000000                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uv_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit code, little endian                              | DCS receive code, little endian                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS frequency in 0.1Hz, little endian                | 2-tone decode index, 0-based, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0 |SquelchMode| 0   0   0   0 | 0   0 |OptSig | 0   0 | TxPer | Scan list index               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|RGP|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0   0   0   0   0 |DAD|ETM|RNG| 0   0   0   0   0   0   0 |TAP| DMR APRS IDX (0-based)        | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Unused set to 0x00            | Unused set to 0x00            | DMR encryption idx +1, 0=off  | 0   0   0   0   0 |SMF|RnK|Muk|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unused set to 0x00000000                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset.\n - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz).\n - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo.\n - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX.\n - TAr: Enable talkaround.\n - CaC: Enable call confirm.\n - RXO: Enable RX only.\n - CTR: Enable CTCSS phase reversal.\n - TDC: Enable TX DCS code.\n - TCT: Enable TX CTCSS tone.\n - RDC: Enable RX DCS code.\n - RCT: Enable RX CTCSS tone.\n - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling,\n   4 = CTCSS/DCS or Optional Signaling\n - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone\n - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free.\n - LWK: Enable lone worker.\n - EEE: Enable enhanced encryption\n - RGP: Enable RX GPS:\n - EAT: Enable adative TDMA\n - EST: Enable simplex TDMA,\n - SMC: SMS confirmation,\n - TSL: Time slot where 0=TS1, 1=TS2\n - DAD: Data ACK disable (inverted!)\n - ETM: Enable through mode\n - RNG: Ranging\n - APRSRep: Enable APRS/GPS report,\n - TAP: TX APRS enable\n - MuK: Multiple keys\n - RnK: Random key\n - SMF: SMS forbid.\n"
  },
  {
    "path": "doc/code/d868uv_generalsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enable key tone               | Display mode                  | Enable automatic key lock     | Automatic shutdown time       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unknown                       | Unknown                       | Boot display                  | Enable boot password          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unknown                       | Squelch level VFO A           | Squelch level VFO B           | Power save mode               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | VOX sensitivity               | VOX delay in 100+500*n ms     | VFO scan type                 | MIC gain                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | PF1 short press function      | PF2 short press function      | PF3 short press function      | P1 short press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | P2 short press function       | Work mode A                   | Work mode B                   | Unknown                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Work mode MEM zone A          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Work mode MEM zone B          | Unknown                       | Enable recording              | Unknown                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                            | Display brightness            | Backlight duration            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Enable GPS                    | Enable SMS alert              | Unknown                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Work mode main channel set    | Enable sub channel            | Unknown                       | Enable call alert             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | GPS Time zone                 | Talk permit tone              | Digital call reset tone       | VOX source                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unknown                                                       | Idle channel tone             | Menu exit time                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Unknown                       | Startup tone                  | Enable call end prompt        | Max volume                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unknown                                                                                       | GPS RX positions              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Unknown                       | PF1 long press function       | PF2 long press function       | PF3 long press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | P1 long press function        | P2 long press function        | Long press duration           | Enable Volume change prompt   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | Auto repeater A direction     | Unknown                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                            | Display later caller          | Unknown                                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Unknown                       | Display clock                 | Max head phone volume         |  Unknown                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 |                                                                                               | Enable enhanced audio         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | VFO Scan UHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | VFO Scan UHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | VFO Scan VHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | VFO Scan VHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Auto rep. offset index UHF    | Auto rep. offset index VHF    | Unknown                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70  ...                                                            | Call tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | Call tone frequency 2 in Hz, little endian                    | Call tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Call tone frequency 4 in Hz, little endian                    | Call tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | Call tone duration 1 in ms, little endian                     | Call tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Call tone duration 3 in ms, little endian                     | Call tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Call tone duration 5 in ms, little endian                     | Idle tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Idle tone frequency 2 in Hz, little endian                    | Idle tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | Idle tone frequency 4 in Hz, little endian                    | Idle tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Idle tone duration 1 in ms, little endian                     | Idle tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Idle tone duration 3 in ms, little endian                     | Idle tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Idle tone duration 5 in ms, little endian                     | Reset tone frequency 1 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Reset tone frequency 2 in Hz, little endian                   | Reset tone frequency 3 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Reset tone frequency 4 in Hz, little endian                   | Reset tone frequency 5 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4 | Reset tone duration 1 in ms, little endian                    | Reset tone duration 2 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 | Reset tone duration 3 in ms, little endian                    | Reset tone duration 4 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Reset tone duration 5 in ms, little endian                    | Record delay in 200ms         | Call display mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | Call display color            | GPS update period             | Show zone talkgroup           | Key tone level                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb4 | GPS units                     | 0 | 0 | 0 |EPK|ESK| 0 |EKB|ENK| Show last heard               | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb8 | Auto repeater VHF minimum frequency in 10Hz, little endian                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nbc | Auto repeater VHF maximum frequency in 10Hz, little endian                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 | Auto repeater UHF minimum frequency in 10Hz, little endian                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc4 | Auto repeater UHF maximum frequency in 10Hz, little endian                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc8 | Auto rep. direction           | Unknown                       | Enable default channel        | Default zone VFO A            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ncc | Default zone VFO B            | Default channel A             | Default channel B             | Keep last caller              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - EKN: Enable knob lock.\n - EKB: Enable keyboard lock.\n - ESK: Enable side key lock.\n - EPK: Enable \"professional key\" lock.\n"
  },
  {
    "path": "doc/code/d868uvanalogcontact.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Number, 14 digits BCD encoded, big-endian, 0-padded                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                            | Number of digits              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name, 15 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                            | 0x00 pad                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvchannel.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit                                                  | DCS receive                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS                                                  | 2-tone decode                 | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0   0   0 |SQM| 0   0 |PTT-ID | 0   0 |OptSig | 0   0 | TxPer | Scan list index               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|RGP|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0   0   0   0   0 |XFR|ETM|RNG| 0   0   0   0   0   0 |APRSRep| Analog APRS PTT               | Digital APRS PTT              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | GPS System Index              | Frequency correction signed   | DMR encryption                | 0   0   0   0   0 |MuK|RnK|SMF|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unused set to 0x00000000                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nwith\nRMode = Repater mode, BWd = Band width, PWR = Power, CMode = Channel mode, TAr=Talkaround, CaC = Call confirm, RXO = RX only,\nCTR = CTCSS phase reversal, TDC = TX DCS code, TCT = TX CTCSS tone, RDC = RX DCS code, RCT = RX CTCSS tone, SQM = Squelch mode,\nOptSig = Optional signalling, TxPer = TX permit, LWK = Lone worker, EEE = Enable enhanced encryption, RGP = Enable RX GPS,\nEAT = Enable adative TDMA, EST = Enable simplex TDMA, SMC = SMS confirmation, TSL = Time slot, XFR = Exclude from roaming,\nETM = Enable through mode, RNG = Ranging, APRSRep = Enable APRS/GPS report, MuK = Multiple keys, RnK = Random key, SMF = SMS forbid.\n"
  },
  {
    "path": "doc/code/d868uvcontact.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Type                          | Name 16 x ASCII chars                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                            | 18 unused bytes set to 0                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                            | ID 8 digits BCD encoded    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                            | Call alert                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | 60 unused bytes, set to 0                                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvgrouplist.txt",
    "content": "      0                               8                               16                              24\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00  | 64 indices of group calls, 32bit each, default 0xffffffff                                                                  ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc   ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n100 | Name, 16 x ASCII, 0-padded                                                                                                 ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10c  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n110 | 16 unused bytes set to 0                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n11c  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvmessage.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Message text, 99 ASCII chars, 0-padded                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                            |  157 unused bytes, 0x00    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvmessagelist.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused 0x00                   | Unused 0x00                   | Next index                    | Current index                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 12 unused bytes set to 0x00                                                                                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvradioid.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Radio ID encoded as 8 BCD digits                                                                                              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unused, set to 0x00           | Name, 16 x ASCII, 0-padded                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                            | 11 unused bytes, set to 0x00                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvscanlist.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0x00           | Priority Channel Select       | Priority channel index 1, 16bit index, little endian          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Priority channel index 2, 16bit index, little endian          | Look back time A                                              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Look back time B                                              | Dropout delay                                                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Dwell time                                                    | Revert channel type           | Name, 16 ASCII, 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                            | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | 50 x 16bit channel indices, little endian, 0xffff=empty                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | 12 unused bytes , set to 0x00                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d868uvzonechannels.txt",
    "content": "      0                               8                               16                              24\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000 | Zone 0, channel index A little endian                         |  Zone 1, channel index A little endian                        |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n010 | Zone 2, channel index A little endian                         |                                                            ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1f0  ...                                                            | Zone 249, channel index A little endian                       |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1f4 | 12 pad bytes set to 0x00                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1fc  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n200 | Zone 0, channel index A little endian                         |  Zone 1, channel index A little endian                        |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n210 | Zone 2, channel index A little endian                         |                                                            ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3f0  ...                                                            | Zone 249, channel index A little endian                       |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3f4 | 12 pad bytes set to 0x00                                                                                                   ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3fc  ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_aeskey.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Key index, 0xff=off           | Key data                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                            | Unused set to 0x00            | Unknown set to 0x40           | Unused set to 0x00         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_aprsrxentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enabled                       | Call up to 6 ASCII chars, 0x00 terminated and filled                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                                                            | SSID                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_aprssetting.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown, set to 0x000         | Unused                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                            | FM APRS TX delay in 20ms      | Signaling type                | CTCSS tone                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | DCS code, little-endian                                       | Manual TX interval in seconds | Auto TX interval in 30s       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | FM APRS Monitor enable        | Fixed location flag           | Latitude degrees              | Latitude minutes              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Latitude seconds              | South flag                    | Longitude degrees             | Longitude minutes             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Longitude seconds             | West flag                     | Destination call, 6 x ASCII, 0-padded                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Destination SSID              | Source call, 6 x ASCII, 0-padded                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                            | Source SSID                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Path, 20 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Unused set to 0x00            | ASCII APRS Symbol Table       | ASCII APRS Map Icon           | Transmit power                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Prewave delay in 10ms         | Unknown set to 0x01           | Unknown set to 0x03           | Unknown set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | DMR APRS Sys 0 channel index, uint16, litte-endian            | DMR APRS Sys 1 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | DMR APRS Sys 2 channel index, uint16, litte-endian            | DMR APRS Sys 3 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | DMR APRS Sys 4 channel index, uint16, litte-endian            | DMR APRS Sys 5 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | DMR APRS Sys 6 channel index, uint16, litte-endian            | DMR APRS Sys 7 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | DMR APRS Sys 0 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | DMR APRS Sys 1 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | DMR APRS Sys 2 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | DMR APRS Sys 3 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | DMR APRS Sys 4 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | DMR APRS Sys 5 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | DMR APRS Sys 6 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | DMR APRS Sys 7 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | DMR APRS Sys 0 call type      | DMR APRS Sys 1 call type      | DMR APRS Sys 2 call type      | DMR APRS Sys 3 call type      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | DMR APRS Sys 4 call type      | DMR APRS Sys 5 call type      | DMR APRS Sys 6 call type      | DMR APRS Sys 7 call type      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Enable roaming support        | DMR APRS Sys 0 time slot      | DMR APRS Sys 1 time slot      | DMR APRS Sys 2 time slot      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | DMR APRS Sys 3 time slot      | DMR APRS Sys 4 time slot      | DMR APRS Sys 5 time slot      | DMR APRS Sys 6 time slot      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | DMR APRS Sys 7 time slot      | Rep. activation delay         | APRS display time             | Unknown                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Unknown                                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4  ...                                                            | Fixed height in feet, uint16, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 |RST|RNF|RWX|RMS|RIT|ROB|RME|RPO| 0   0   0   0   0   0   0 |ROF| FM APRS width                 | Pass all enable               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | FM ARPS Frequency 0 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | FM ARPS Frequency 1 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb4 | FM ARPS Frequency 2 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb8 | FM ARPS Frequency 3 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nbc | FM ARPS Frequency 4 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 | FM ARPS Frequency 5 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc4 | FM ARPS Frequency 6 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc8 | FM ARPS Frequency 7 in 10Hz, 8 digit BCD, big-endian                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd0 | Unknown                                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - FM APRS TX delay: 0=0ms, 1=20ms, ..., 255=5100ms\n - Signaling type: 0=off, 1=CTCSS, 2=DCS, default=off\n - CTCSS Tone:  0= 61.2Hz,  1= 67.0Hz,  2= 69.3Hz,  3= 71.9Hz,  4= 74.4Hz,  5= 77.0Hz,  6= 79.7Hz,\n                7=82.5Hz,   8= 85.4Hz,  9= 88.5Hz, 10= 91.5Hz, 11= 94.8Hz, 12= 97.4Hz, 13=100.0Hz,\n               14=103.5Hz, 15=107.2Hz, 16=110.9Hz, 17=114.8Hz, 18=118.8Hz, 19=123.0Hz, 20=127.3Hz,\n               21=131.8Hz, 22=136.5Hz, 23=141.3Hz, 24=146.2Hz, 25=151.4Hz, 26=156.7Hz, 27=159.8Hz,\n               28=162.2Hz, 29=165.5Hz, 30=167.9Hz, 31=171.3Hz, 32=173.8Hz. 33=179.9Hz, 34=183.5Hz,\n               35=186.2Hz, 36=189.9Hz, 37=192.8Hz, 38=196.6Hz, 39=199.5Hz, 40=203.5Hz, 41=206.5Hz,\n               42=210.7Hz, 43=218.1Hz, 44=225.7Hz, 45=229.1Hz, 46=233.6Hz, 47=241.8Hz, 48=250.3Hz,\n               49=254.1Hz\n - Path: 20 x ASCII, 0-padded path string. Format is comma-separated CALL1-SSID,CALL2-SSID,...\n - DMR APRS Sys N channel index: uint16 channel index, 0-based, little-endian, [0,4000],\n      0x0fa0=VFO A, 0x0fa1=VFO B, 0x0fa2=Selected\n - DMR APRS Sys N time slot: 0 = Channel, 1 = Timeslot 1, 2 = Timeslot 2\n - Transmit power: 0=low, 1=mid, 2=high, 3=turbo.\n - Pewave delay: in multiples of 10ms [0,2550ms], default=0ms.\n - Manual TX interval: n+1 seconds. [0,255], default=0s\n - Auto TX interval: 0=Off, 1=30s, ..., 255=7650s, default=off\n - FM APRS Monitor enable: If enabled, the radio will monitor send FM APRS transmissions. default=off\n - Rep. activation delay: 0=Off, 1=100ms, ..., 10=1000ms.\n - APRS display time: 0=3s, 1=4s, ..., 12=15s, 13=infinite, default=3s\n - RPO: Report position flag.\n - RME: Report MIC-E flag.\n - ROB: Report object flag.\n - RIT: Report item flag.\n - RMS: Report message flag.\n - RWX: Report weather flag.\n - RNF: Report NEMA flag.\n - RST: Report status flag.\n - ROF: Report other flag.\n - FM APRS width: 0=narrow, 1=wide.\n - Pass all: 0=Off, 1=On, no idea.\n"
  },
  {
    "path": "doc/code/d878uv_aprssettingext.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown settings                                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                            | Fixed altitude in feet, little endian                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 |RST|RNF|RWX|RMS|RIT|ROB|RME|RPO| 0   0   0   0   0   0   0 |ROF| Unknown settings                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RPO: Report position flag.\n - RME: Report MIC-E flag.\n - ROB: Report object flag.\n - RIT: Report item flag.\n - RMS: Report message flag.\n - RWX: Report weather flag.\n - RNF: Report NEMA flag.\n - RST: Report status flag.\n - ROF: Report other flag.\n"
  },
  {
    "path": "doc/code/d878uv_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit code, little endian                              | DCS receive code, little endian                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS frequency in 0.1Hz, little endian                | 2-tone decode index, 0-based, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0 |SquelchMode| 0   0 |PTT-ID | 0   0 |OptSig | 0   0 | TxPer | Scan list index               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|RGP|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0   0   0   0 |DAD|XFR|ETM|RNG| 0   0   0   0   0   0 |APRSRep| Analog APRS PTT               | Digital APRS PTT              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | GPS System Index              | Freq. corr. signed in 10Hz    | DMR encryption idx +1, 0=off  | 0   0   0   0   0 |SMF|RnK|Muk|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | FM APRS Frequency index       | Unused set to 0x000000                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset.\n - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz).\n - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo.\n - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX.\n - TAr: Enable talkaround.\n - CaC: Enable call confirm.\n - RXO: Enable RX only.\n - CTR: Enable CTCSS phase reversal.\n - TDC: Enable TX DCS code.\n - TCT: Enable TX CTCSS tone.\n - RDC: Enable RX DCS code.\n - RCT: Enable RX CTCSS tone.\n - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling,\n   4 = CTCSS/DCS or Optional Signaling\n - PTT-ID: When to send the PTT-ID, where 0=off, 1=start, 2=end, 3=both.\n - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone\n - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free.\n - LWK: Enable lone worker.\n - EEE: Enable enhanced encryption\n - RGP: Enable RX digital (DMR) APRS\n - EAT: Enable adative TDMA\n - EST: Enable simplex TDMA,\n - SMC: SMS confirmation,\n - TSL: Time slot where 0=TS1, 1=TS2\n - XFR: Exclude from roaming\n - DAD: Data ACK disable.\n - ETM: Enable through mode\n - RNG: Ranging\n - APRSRep: Enable APRS/GPS report,\n - MuK: Multiple keys\n - RnK: Random key\n - SMF: SMS forbid.\n"
  },
  {
    "path": "doc/code/d878uv_dmraprssettings.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Channel index 0, uint16 little endian                         | Channel index 1, uint16 little endian                         |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Channel index 6, uint16 little endian                         | Channel index 7, uint16 little endian                         |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Destination contact index 0, uint32 little endian                                                                             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Destination contact index 1, uint32 little endian                                                                             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Destination contact index 7, uint32 little endian                                                                             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030 | Call type 0                   | Call type 1                   | Call type 2                   | Call type 3                   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0034 | Call type 4                   | Call type 5                   | Call type 6                   | Call type 7                   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0038 | Romaing enable                | Time slot 0                   | Time slot 1                   | Time slot 2                   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c | Time slot 3                   | Time slot 4                   | Time slot 5                   | Time slot 6                   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040 | Time slot 7                   | Repeater delay in 100ms       | Unused set to 0x00                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n005c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_dmraprssystems.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Digital channel index 0, 16bit little-endian                  |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                            | Digital channel index 7, 16bit little-endian                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Destination DMR ID 0, 32bit BCD big-endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Destination DMR ID 7, 32bit BCD big-endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Calltype channel 0            | Calltype channel 1            | Calltype channel 2            | Calltype channel 3            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Calltype channel 4            | Calltype channel 5            | Calltype channel 6            | Calltype channel 7            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Roaming support               | Timeslot channel 0            | Timeslot channel 1            | Timeslot channel 2            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Timeslot channel 3            | Timeslot channel 4            | Timeslot channel 5            | Timeslot channel 6            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Timeslot channel 7            | Rep.activation delay in 100ms | 30 unused bytes set to 0                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_generalsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enable key tone               | Display mode                  | Enable automatic key lock     | Automatic shutdown time       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Transmit timeout in 30s       | Language                      | Boot display                  | Enable boot password          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | VFO frequency step            | Squelch level VFO A           | Squelch level VFO B           | Power save mode               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | VOX sensitivity               | VOX delay                     | VFO scan type                 | DMR MIC gain                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | PF1 short press function      | PF2 short press function      | PF3 short press function      | P1 short press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | P2 short press function       | Work mode A                   | Work mode B                   | STE Type                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | STE freq. no signal           | Group call hang time in sec   | Private call hang time in sec | Prewave time in 20ms          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Wake head period in 20ms      | WFM channel index             | WFM VFO Mode enable           | Work mode MEM zone A index    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Work mode MEM zone B index    | Unused set to 0x00            | Enable recording              | DTMF duration                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Man down enable               | Unused set to 0x00            | Display brightness            | Backlight duration            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Enable GPS                    | Enable SMS alert              | Unused set to 0x00            | WFM monitor enable            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Work mode main channel set    | Enable sub channel            | TBST frequency                | Enable call alert             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | GPS Time zone                 | Talk permit tone              | Digital call reset tone       | VOX source                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Enable pro mode               | Unused set to 0x00            | Idle channel tone             | Menu exit time                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Filter own ID enable          | Startup tone enable           | Enable call end prompt        | Max speaker volume            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Remote stun/kill enable       | Unused set to 0x00            | Remote monitor enable         | GPS RX positions              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Unknown                       | PF1 long press function       | PF2 long press function       | PF3 long press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | P1 long press function        | P2 long press function        | Long press duration           | Enable Volume change prompt   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | Auto repeater A direction     | Digital monitor slot          | Digital monitor color code    | Digital monitor match ID      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Digital monitor hold slot     | Display later caller          | Unknown                       | Man down delay in sec         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Analog call hold in seconds   | Display clock                 | Max head phone volume         | Enable GPS message            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Unknown settings                                                                              | Enable enhanced audio         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | VFO Scan UHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | VFO Scan UHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | VFO Scan VHF minimum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | VFO Scan VHF maximum frequency in 10Hz, 32bit uint, little endian                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Auto rep. 1 offset index UHF  | Auto rep. 1 offset index VHF  | Unknown                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c |                                                               | Maintain call channel         | Priority zone A index         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Priority zone B index         | Unused set to 0x00            | Call tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | Call tone frequency 2 in Hz, little endian                    | Call tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Call tone frequency 4 in Hz, little endian                    | Call tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | Call tone duration 1 in ms, little endian                     | Call tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Call tone duration 3 in ms, little endian                     | Call tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Call tone duration 5 in ms, little endian                     | Idle tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Idle tone frequency 2 in Hz, little endian                    | Idle tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | Idle tone frequency 4 in Hz, little endian                    | Idle tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Idle tone duration 1 in ms, little endian                     | Idle tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Idle tone duration 3 in ms, little endian                     | Idle tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Idle tone duration 5 in ms, little endian                     | Reset tone frequency 1 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Reset tone frequency 2 in Hz, little endian                   | Reset tone frequency 3 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Reset tone frequency 4 in Hz, little endian                   | Reset tone frequency 5 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4 | Reset tone duration 1 in ms, little endian                    | Reset tone duration 2 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 | Reset tone duration 3 in ms, little endian                    | Reset tone duration 4 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Reset tone duration 5 in ms, little endian                    | Record delay in 200ms         | Call display mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | Unknown settings              | BT enable                     | BT and internal mic           | BT and internal speaker       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb4 | Plug-in rec tone enable       | GPS ranging interval in sec   | BT mic gain                   | BT speaker gain               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb8 | Display channel number        | Display contact               | Auto roaming period in min    | Key tone level                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nbc | Callsign color                | GPS unit                      | 0   0   0 |KLF|LSK| 0 |LKB|LKN| Auto roam delay in seconds    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 | Standby text color            | Standby background image      | Show last call on boot        | SMS format                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc4 | Auto repeater VHF 1 min frequency in 10Hz, 32bit uint, little endian                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc8 | Auto repeater VHF 1 max frequency in 10Hz, 32bit uint, little endian                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ncc | Auto repeater UHF 1 min frequency in 10Hz, 32bit uint, little endian                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd0 | Auto repeater UHF 1 max frequency in 10Hz, 32bit uint, little endian                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd4 | Auto rep. VFO B direction     | Addrbook is sent w own code   | Unused set to 0x00            | Boot channel enable           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd8 | VFO A default zone index      | VFO B default zone index      | VFO A default channel index   | VFO B default channel index   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndc | Default roaming zone index    | Repeater range check enable   | Repeater check interval in 5s | RepCheck reconnect count      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne0 | Roam start condition          | Backlight duration TX in sec  | Separate display              | Keep last caller              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne4 | Channel name A color          | RepCheck notification         | Backlight duration RX in sec  | Roaming enable                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne8 | Unused set to 0x00            | Mute delay in minutes-1       | RepCheck num notification     | Startup GPS test enable       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nec | Startup reset enable          | BT hold time                  | BT RX delay                   | Unknown                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n  - Work mode settings:\n    - Display mode: 0=Channel, 1=Frequency\n    - Work mode A/B: 0=Channel, 1=VFO\n    - Work mode main channel set: 0=A, 1=B\n  - VOX settings:\n    - VOX sensitivity: 0=Off, 1=1, ..., 3=3\n    - VOX delay in 100+500*n ms\n    - VOX source: 0=internal, 1=external, 2=both\n  - Bluetooth settings:\n    - BT mic gain: 0=1, ..., 3=4 (default), 4=5.\n    - BT speaker gain: 0=1, 1=2 (default), ..., 4=5\n    - BT hold time in seconds: 0=Off, ..., 10=10s (default), ..., 120=120s, 121=infinite.\n    - BT RX delay in 0.5s: 0=30ms, 1=1.0s, 2=1.5s, ... 10=5.5\n  - STE settings:\n    - STE type: 0=Off, 1=Silent, 2=120deg, 3=180deg, 4=240deg\n    - STE freq. no signal: 0=Off, 1=55.2Hz, 2=259.2Hz\n  - Power save settings:\n    - Automatic shutdown time: 0=Off (default), 1=10min, 2=30min, 3=60min, 4=120min\n    - Power save mode: 0=Off, 1=1:1 (50%), 2=2:1 (66%)\n  - Key settings:\n    - Key functions:\n    - Long press duration: 0=1s, 1=2s, ..., 4=5s\n    - KLF: Key lock forced enable\n    - LSK: Lock side key\n    - LKB: Keyboard lock\n    - LKN: Knob lock\n  - Other settings:\n    - Addrbook is sent w own code: Analog call send radio ID together with selected address book entry.\n    - Transmit timeout in multiples of 30s. 0=Off, 1=30s, ..., 8=240s\n    - Language: 0=English, 1=German\n    - VFO frequency step: 0=2.5kHz, 1=5kHz, 2=6.25kHz, 3=8.33kHz, 4=10kHz, 5=12.5kHz, 6=20kHz, 7=25kHz\n    - TBST frequency: 0=1000Hz, 1=1450Hz, 2=1750Hz, 3=2100Hz\n    - Analog call hold in seconds [0,...,30s]\n    - Maintain call channel: If enabled, allows to answer calls on sub-channel within 5s.\n    - Priority zone A/B index: Zone index 0-based, 0xff=Off.\n  - DMR settings:\n    - Group/private call hang time in seconds: 0=1s, ..., 29=30s, 30=30min, 31=infinite.\n    - Prewave time/wake head period in 20ms: 0=0ms, ..., 5=100ms (default), ..., 50=1000ms\n    - Digital monitor slot: 0=Off, 1=Single slot, 2=Dual slot\n    - SMS format: 0=Motorola, 1=Hytera, 2=DMR standard\n  - Boot settings:\n    - Boot display: 0=default, 1=custom text, 2=custom image\n    - VFO A/B default channel index: Channel index within default zone. 0xff = VFO.\n  - Tone settings:\n    - Talk permit tone: 0=Off, 1=DMR, 2=FM, 3=both\n    - Idle channel tone: 0=Off, 1=Type 1, 2=Type 2, 3=Type 3\n    - Key tone level: 0=adjustable, 1, ..., 15\n  - Display settings:\n    - Display brightness: 0=1, ..., 4=5\n    - Backlight duration: 0=Always, 1=5s, 2=10s, ..., 6=30s, 7=1m\n    - Backlight duration TX in sec: 0=Off, 1=1s, ..., 30=30s\n    - Menu exit time in 5sec: 0=5s, ..., 11=60s\n    - Display later caller: 0=Off, 1=ID, 2=Call, 3=Both\n    - Call display mode: 0=Off, 1=Call, 2=Name\n    - Callsign/channel name/ color: 0=Orange, 1=Red, 2=Yellow, 3=Green, 4=Turquoise, 5=Blue, 6=White\n    - Display channel number: 0=Channel number, 1=Index in zone\n    - Standby text color: 0=White, 1=Black, 2=Orange, 3=Red, 4=Yellow, 5=Green, 6=Turquoise, 7=Blue\n    - Standby background image: 0=Default, 1=Custom 1, 2=Custom 2\n    - Separate display: Draws a line to separate upper and lower half of the display.\n    - Keep last caller on channel switch\n    - Backlight duration RX in sec: 0=Always, 1=1s, ..., 30=30s.\n  - GPS settings:\n    - GPS time-zone: 00h=UTC-12:00, ..., 09h=UTC-3:30, 0Ah=UTC-3:00, ..., 0Dh=UTC, ..., 11h=UTC+3:30,\n                     12h=UTC+4:00, 13h=UTC+4:30, 14h=UTC+5:00, 15h=UTC+5:30, 16h=UTC+5:45, ...,\n                     1Ah=UTC+8:30, ..., 1Fh=UTC+13:00\n    - GPS ranging interval in sec [5,255]\n    - GPS units: 0=metric, 1=imperial\n  - Scan settings:\n    - VFO scan type: 0=fixed time, 1=carrier, 2=stop\n  - Auto repeater settings:\n    - Auto repeater A/B direction: 0=Off, 1=Positive, 2=Negative\n    - Auto rep. 1 offset index UHF/VHF. Offset frequency index 0-based, 0xff=Off.\n    - Repeater check interval in 5s: 0=5s, ..., 9=50s\n    - RepCheck reconnect count: 0=3, 1=4, 2=5\n    - RepCheck notification: 0=Off, 1=Bell, 2=Voice\n    - RepCheck num notification: 0=1, ..., 9=10.\n    - Roam start condition: 0=time based, 1=out-of-range\n    - Auto roaming period in min: 0=1min, ..., 255=256min.\n    - Auto roam delay in seconds: 0=None, 1=1s, ..., 30=30min\n  - Audio settings:\n    - Max speaker/head phone volume: 0=Indoors, 1=1, ..., 5=5 (default), ..., 8=8\n    - DMR MIC gain: 0=1 (default), ..., 4=5\n"
  },
  {
    "path": "doc/code/d878uv_generalsettingsextension.txt",
    "content": "        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\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000  | Enable send alias             | Unused set to 0x00                                                                            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018  | Unknown settings                                                                                                              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  | Unknown settings                                              | Talker alias priority         | Talker alias encoding         |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020  | Enable BT PTT latch           | Unknown                       | AutoRep UHF 2 offset index    | AutoRep VHF 2 offset index    |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0024  | AutoRep VHF 2 minimum frequency in 10Hz, 32 bit uint, little-endian.                                                          |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0028  | AutoRep VHF 2 maximum frequency in 10Hz, 32 bit uint, little-endian.                                                          |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  | AutoRep UHF 2 minimum frequency in 10Hz, 32 bit uint, little-endian.                                                          |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030  | AutoRep UHF 2 maximum frequency in 10Hz, 32 bit uint, little-endian.                                                          |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0034  | BT PTT sleep interval         | GPS mode                      | STE duration                  | Man dial group call hangtime  |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0038  | Man dial priv call hangtime   | Channel name B color          | Encryption type               | Enable TOT prediction         |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  | Enable TX power AGC           | Zone name A color             | Zone name B color             | Auto shutdown mode            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040  |                   |DCC|DSl|DCT| FM idle channel tone enable   | Date format                   | FM mic gain [0,4]             |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044  | GPS roaming enable            | Unknown setting               | Call end tone 1 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048  | Call end tone 2 in Hz, 16bit uint, little endian              | Call end tone 3 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c  | Call end tone 4 in Hz, 16bit uint, little endian              | Call end tone 5 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  | Call end tone 1 duration in 10ms, 16bit uint, little endian   | Call end tone 2 duration in 10ms, 16bit uint, little endian   |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054  | Call end tone 3 duration in 10ms, 16bit uint, little endian   | Call end tone 4 duration in 10ms, 16bit uint, little endian   |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0058  | Call end tone 5 duration in 10ms, 16bit uint, little endian   | All call tone 1 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n005c  | All call tone 2 in Hz, 16bit uint, little endian              | All call tone 3 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0060  | All call tone 4 in Hz, 16bit uint, little endian              | All call tone 5 in Hz, 16bit uint, little endian              |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0064  | All call tone 1 duration in 10ms, 16bit uint, little endian   | All call tone 2 duration in 10ms, 16bit uint, little endian   |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0068  | All call tone 3 duration in 10ms, 16bit uint, little endian   | All call tone 4 duration in 10ms, 16bit uint, little endian   |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c  | All call tone 5 duration in 10ms, 16bit uint, little endian   | Unknown settings                                           ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n01fc   ...                                                                                                                            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n  - Bluetooth settings:\n    - BT PTT sleep interval: 0=Infinite, 1=1min, ..., 4=4min.\n  - STE settings:\n    - STE duration in multiples of 10ms. 0=10ms, ..., 24=250ms (default), ..., 99=1000ms.\n  - Power save settings:\n    - Auto shutdown mode: 0=Auto shutdown timer is reset on call, 1=timer is not reset on call.\n  - Other settings:\n    - Encryption type: 0=AES, 1=DMR standard\n  - DMR settings:\n    - Man dial group/private call hangtime: 0=1s, ..., 29=30s, 30=30min, 31=infinite.\n  - Display settings:\n    - Channel name B; zone name A/B color: 0=Orange, 1=Red, 2=Yellow, 3=Green, 4=Turquoise, 5=Blue, 6=White\n    - DCT: Display channel type\n    - DSl: Display slot\n    - DCC: Display color code\n    - Date format: 0=yyyy/mm/dd, 1=dd/mm/yyyy\n  - GPS settings:\n    - GPS mode: 0=GPS, 1=Beidou, 2=GPS+Beidou, 3=GLONASS, 4=GPS+GLONASS, 5=Beidou+GLONASS, 6=All 3\n  - Auto repeater settings:\n    - AutoRep VHF/UHF 2 offset index: 0-based, 0xff = off.\n  - Audio settings:\n    - FM mic gain 0=1 (default), ..., 4=5\n  - Talker alias:\n    - Talker alias priority 0=Off, 1=Contacts, 2=over-the-air\n    - Talker alias encoding: 0=ISO-8, 1=ISO-7, 2=Unicode\n"
  },
  {
    "path": "doc/code/d878uv_gpsmessage.txt",
    "content": "      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\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00  | GPS Message, up to 32 ASCII chars                                                                                          ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c   ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  | Unknown settings block                                                                                                     ...\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c   ...                                                                                                                            |\n    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_radioinfo.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0x0000                                         | Enable full test              | Frequency range               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Enable international          | Unknown, set to 0x00          | Enable band select            | Unknown, set to 0x00          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unknown, set to 0x00          | Unknown, set to 0x01          | Unknown, set to 0x01          | Band select password       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ... 4 x ASCII, 0-terminated                                                                    | Unknown, set to 0xff          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Radio type, 7 x ASCII, 0-terminated                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                            | Unknown, set 0x01             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Unknown, set to 0x00          | Unknown, filled with 0xff                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Program password, 4 x ASCII, 0-terminated                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Area code, 4 x ASCII, 0-terminated                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Serial number, 16 x ASCII, 0-terminated                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Production date, 10 x ASCII, 0-terminated                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48  ...                                                            | Unused, set to 0x00                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Manufacture code, 8 x ASCII, 0-terminated                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | Unused, filled with 0x00                                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | Maintained date, 16 x ASCII, 0-terminated.                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Dealer code, 16 x ASCII, 0-terminated.                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Stock date, 16 x ASCII, 0-terminated.                                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Sell date, 16 x ASCII, 0-terminated.                                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Seller, 16 x ASCII, 0-terminated.                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | Maintained note, 128 x ASCII, 0-terminated.                                                                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nfc  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_roamingchannel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency in 10Hz, 32bit 8-digit BCD big-endian                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | RX Frequency in 10Hz, 32bit 8-digit BCD big-endian                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Color code                    | Time slot 0=TS1, 1=TS2        | Name 16b ASCII 0-padded                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                            | Unused filled with 0x00                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uv_roamingzone.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Roaming Channel Index 0       | Roaming Channel Index 1       | Roaming Channel Index 2       | Roaming Channel Index 3    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ... Roaming Channel Index 60   | Roaming Channel Index 61      | Roaming Channel Index 62      | Roaming Channel Index 63      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Channel name, 16b ASCII, 0x00 padded                                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | 48 bytes unused, set to 0x00                                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/d878uvgpssetting.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Digital channel index 0, 16bit little-endian                  |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                            | Digital channel index 7, 16bit little-endian                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Destination DMR ID 0, 32bit BCD big-endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Destination DMR ID 1, 32bit BCD big-endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Destination DMR ID 7, 32bit BCD big-endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Calltype channel 0            | Calltype channel 1            | Calltype channel 2            | Calltype channel 3            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Calltype channel 4            | Calltype channel 5            | Calltype channel 6            | Calltype channel 7            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Roaming support               | Timeslot channel 0            | Timeslot channel 1            | Timeslot channel 2            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Timeslot channel 3            | Timeslot channel 4            | Timeslot channel 5            | Timeslot channel 6            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Timeslot channel 7            | Repeater activation delay     | 30 unused bytes set to 0                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/detect_example.cc",
    "content": "#include \"libdmrconf/usbdevice.hh\"\n#include \"libdmrconf/anytone_radio.hh\"\n\nint main(void) {\n  // Example code to detect an AnytoneDevice\n\n  // First, search matching devices (only AnyTones)\n  QList<USBDeviceDescriptor> devices = AnytoneInterface::detect();\n  if (1 != devices.count()) {\n    // Either none or more than one device found...\n    return -1;\n  }\n\n  // A place to put error messages\n  ErrorStack err;\n  Radio *radio = AnytoneRadio::detect(devices.first(), RadioInfo(), err);\n  if (nullptr == radio) {\n    // There went something wrong, check err.\n    return -1;\n  }\n\n  // Read codeplug from device blocking.\n  if (! radio->startDownload(true, err)) {\n    // Some download error, check err.\n    delete radio;\n    return -1;\n  }\n\n  // Decode codeplug into genericCodeplug\n  Config genericCodeplug;\n  if (! radio->codeplug().decode(&genericCodeplug, err)) {\n    // Some decoding error, check err.\n    delete radio;\n    return -1;\n  }\n\n  // Do whatever you like with the codeplug.\n\n  return 0;\n}\n"
  },
  {
    "path": "doc/code/dm1701_buttonsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0                                              | Side button 1 short press     | Side button 1 long press      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Side button 2 short press     | Side button 2 long press      | Side button 3 short press     | Siden button 3 long press     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | P1 short press                | P1 long press                 | P2 short press                | P2 long press                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Unused set to 0x00000000                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Unknown set to 0x01           | Long press dur x250ms         | Unused set to 0xffff                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | One-touch setting 0                                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | One-touch setting 5                                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dm1701_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 |LWK| 1 |SQT|ASC| BandW | ChMod |   ColorCode   |TimeSlt|RXO|ALT|DCC|PCC| PRIV  |   PrivIdx     |DPD| 1 | 1   0 |EAA| 0 |RXFreqR|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 |AdimtCr|PWR|VOX| 0 |RVB|TXFreqR| Unknown set to 0xc3           | TX Contact name index + 1, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | 0   0 |      Tx Timeout       |    Tx Timeout Rekey Delay     | Emergency System index + 1    | Scan List index + 1           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | RX Group List index + 1       | GPS System index +1           | DTMF decode bitmap            | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Signaling System index +1  | TX Signaling System index +1  | Unused set to 0xff            | 1   1   1   1   1   1 |RXG|TXG|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name 16 x 16bit unicode characters                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description\n LWK = Lone Worker, default=0;\n SQT = Squelch type, 0=tight, 1=normal, default=1;\n ASC = Auto scan, default=0;\n BandW = Bandwidth, 0=12.5kHz, 2=25kHz, default = 0;\n ChMod = Channel Mode, 1=Analog or 2=Digital, default=1;\n ColorCode = ColorCode [0..15], default=1;\n TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1;\n RXO = RX Only, 0=off, 1=on, default=0;\n ALT = Allow Talkaround (inverted), default=1 (off);\n DCC = Data Call Confirmed, default=0;\n PCC = Private Call Confirmed, default=0;\n PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (off);\n PrivIdx = Privacy Index, [0,15], default=0.\n DPD = Display PTT ID (inverted), default = 1.\n EAA = Emergency Alarm Ack, default = 0;\n RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low);\n AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0.\n PWR = Specifies the power, 0=low, 1=high.\n VOX = VOX Enable, 0=Disable, 1=Enable, default = 0;\n RVB = Reverse burst 0=disable, 1=enable, default=1;\n TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0;\n TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3;\n InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0;\n Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0;\n Power = Power, 0=low, 1=middle, 2=high, default=high;\n ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1;\n RXG = Receive GSP info (inverted), default=1;\n TXG = Send GSP info (inverted), default=1;\n"
  },
  {
    "path": "doc/code/dm1701_settings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Intro Line 1, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Intro Line 2, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n26 | Reserved 24 bytes, set to 0xff                                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | 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 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | DMR ID 24bit, little endian                                                                   | Unused 0x00                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms  | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10]       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Unused, set 0x00              | Unused, set 0x00              | RX Low Bat. Intv N x 5s       | Call Alert Tone Dur N x 5s    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec  | Unused, set to 0x00           | Scan Dig. Hang Time N x 100ms |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | 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 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | Power-on password 8 x BCD numbers, 0x00000000=default                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | Radio prog. password 8 x BCD numbers, 0x00000000=disabled                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Unused set to 0xffffff                                                                        | Timezone,0=UTC-12 | 1 |PCM|GCM|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | Unused set to 0xffffffff                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Radio name 16 x 16bit unicode chars                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Channel hang time N x 100ms   | Unused, filled with 0xff                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\n\nField description:\n MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1;\n DAL = Disable all LEDs (inverted);\n TPD = Talk permit tone digital;\n TPA = Talk permit tone analog;\n PWE = Password and lock enable (inverted);\n CIT = Ch. free indication tone (inverted);\n DAT = Disable all tones (inverted);\n SMR = Save Mode Receive;\n SPR = Save Preamble;\n INP = Intro Picture;\n MSB = Mode select B (0=VFO, 1=MR);\n MSA = Mode select A (0=VFO, 1=MR);\n BcLTime = Backlight time, 0=Always, t=n*5s;\n Keypad Lock Time = 0xff=manual otherwise\n PCM = Private call match;\n GCM = Group call match;\n"
  },
  {
    "path": "doc/code/dm1701_zoneext.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Member Channel index+1 16 VFO A                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                            | Member Channel index+1 63 VFO A                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | Member Channel index+1 00 VFO B                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndc  ...                                                            | Member Channel index+1 63 VFO B                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dmr6x2uv_aprssetting.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown, set to 0x000         | TX frequency as 8 digit BDC, big-endian in 10Hz (unused)                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                            | FM APRS TX delay in 20ms      | Signaling type                | CTCSS tone                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | DCS code, little-endian                                       | Manual TX interval in seconds | Auto TX interval in 30s       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | FM APRS Monitor enable        | Fixed location flag           | Latitude degrees              | Latitude minutes              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Latitude seconds              | South flag                    | Longitude degrees             | Longitude minutes             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Longitude seconds             | West flag                     | Destination call, 6 x ASCII, 0-padded                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Destination SSID              | Source call, 6 x ASCII, 0-padded                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                            | Source SSID                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Path, 20 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Unused set to 0x00            | ASCII APRS Symbol Table       | ASCII APRS Map Icon           | Transmit power                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Prewave delay in 10ms         | Unknown set to 0x01           | Unknown set to 0x03           | Unknown set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | DMR APRS Sys 0 channel index, uint16, litte-endian            | DMR APRS Sys 1 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | DMR APRS Sys 2 channel index, uint16, litte-endian            | DMR APRS Sys 3 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | DMR APRS Sys 4 channel index, uint16, litte-endian            | DMR APRS Sys 5 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | DMR APRS Sys 6 channel index, uint16, litte-endian            | DMR APRS Sys 7 channel index, uint16, litte-endian            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | DMR APRS Sys 0 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | DMR APRS Sys 1 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | DMR APRS Sys 2 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | DMR APRS Sys 3 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | DMR APRS Sys 4 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | DMR APRS Sys 5 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | DMR APRS Sys 6 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | DMR APRS Sys 7 destination ID, 8-digit BCD, big-endian                                                                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | DMR APRS Sys 0 call type      | DMR APRS Sys 1 call type      | DMR APRS Sys 2 call type      | DMR APRS Sys 3 call type      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | DMR APRS Sys 4 call type      | DMR APRS Sys 5 call type      | DMR APRS Sys 6 call type      | DMR APRS Sys 7 call type      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Enable roaming support        | DMR APRS Sys 0 time slot      | DMR APRS Sys 1 time slot      | DMR APRS Sys 2 time slot      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | DMR APRS Sys 3 time slot      | DMR APRS Sys 4 time slot      | DMR APRS Sys 5 time slot      | DMR APRS Sys 6 time slot      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | DMR APRS Sys 7 time slot      | DMR APRS pre-wave delay       | Unknown                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - FM APRS TX Frequency 8 digit BCD big endian in 10Hz.\n - FM APRS TX delay: 0=0ms, 1=20ms, ..., 255=5100ms\n - Signaling type: 0=off, 1=CTCSS, 2=DCS, default=off\n - CTCSS Tone:  0= 61.2Hz,  1= 67.0Hz,  2= 69.3Hz,  3= 71.9Hz,  4= 74.4Hz,  5= 77.0Hz,  6= 79.7Hz,\n                7=82.5Hz,   8= 85.4Hz,  9= 88.5Hz, 10= 91.5Hz, 11= 94.8Hz, 12= 97.4Hz, 13=100.0Hz,\n               14=103.5Hz, 15=107.2Hz, 16=110.9Hz, 17=114.8Hz, 18=118.8Hz, 19=123.0Hz, 20=127.3Hz,\n               21=131.8Hz, 22=136.5Hz, 23=141.3Hz, 24=146.2Hz, 25=151.4Hz, 26=156.7Hz, 27=159.8Hz,\n               28=162.2Hz, 29=165.5Hz, 30=167.9Hz, 31=171.3Hz, 32=173.8Hz. 33=179.9Hz, 34=183.5Hz,\n               35=186.2Hz, 36=189.9Hz, 37=192.8Hz, 38=196.6Hz, 39=199.5Hz, 40=203.5Hz, 41=206.5Hz,\n               42=210.7Hz, 43=218.1Hz, 44=225.7Hz, 45=229.1Hz, 46=233.6Hz, 47=241.8Hz, 48=250.3Hz,\n               49=254.1Hz\n - Path: 20 x ASCII, 0-padded path string. Format is comma-separated CALL1-SSID,CALL2-SSID,...\n - DMR APRS Sys N channel index: uint16 channel index, 0-based, little-endian, [0,4000],\n      0x0fa0=VFO A, 0x0fa1=VFO B, 0x0fa2=Selected\n - DMR APRS Sys N time slot: 0 = Channel, 1 = Timeslot 1, 2 = Timeslot 2\n - Transmit power: 0=low, 1=mid, 2=high, 3=turbo.\n - FM prewave delay: in multiples of 10ms [0,2550ms], default=0ms.\n - Manual TX interval: n+1 seconds. [0,255], default=0s\n - Auto TX interval: 0=Off, 1=30s, ..., 255=7650s, default=off\n - FM APRS Monitor enable: If enabled, the radio will monitor send FM APRS transmissions. default=off\n - Rep. activation delay: 0=Off, 1=100ms, ..., 10=1000ms.\n - APRS display time: 0=3s, 1=4s, ..., 12=15s, 13=infinite, default=3s\n - RPO: Report position flag.\n - RME: Report MIC-E flag.\n - ROB: Report object flag.\n - RIT: Report item flag.\n - RMS: Report message flag.\n - RWX: Report weather flag.\n - RNF: Report NEMA flag.\n - RST: Report status flag.\n - ROF: Report other flag.\n - FM APRS width: 0=narrow, 1=wide.\n - Pass all: 0=Off, 1=On, no idea.\n"
  },
  {
    "path": "doc/code/dmr6x2uv_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | RX Frequency 32bit BCD encoded in big-endian as MMMkkkhh                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Tx Frequency Offset 32bit BCD encoded in big-endian as MMMkkkhh                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | RMode | 0 |BWd|  PWR  | CMode |TAr|CaC|RXO|CTR|TDC|TCT|RDC|RCT| CTCSS transmit                | CTCSS receive                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | DCS transmit code, little endian                              | DCS receive code, little endian                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Custom CTCSS frequency in 0.1Hz, little endian                | 2-tone decode index, 0-based, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Contact index 0-based, little endian                                                                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio ID table index.         | 0 |SquelchMode| 0   0   0   0 | 0   0 |OptSig | 0   0 | TxPer | 0   0   0   0   0 |XFR| 0 |RNG|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Group list index           | 2-tone ID                     | 5-tone ID                     | DTMF ID                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Color code                    |LWK|EEE|???|EAT| 0 |EST|SMC|TSL| AES Encryption key            | Name 16 x ASCII 0-padded   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                            | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0   0   0   0   0 |DAD|ETM| 0 | Unused, set to 0x00           | Scanlist idx 0, 0xff=unset    | Scanlist idx 1, 0xff=unset    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Scanlist idx 2, 0xff=unset    | Scanlist idx 3, 0xff=unset    | Scanlist idx 4, 0xff=unset    | Scanlist idx 5, 0xff=unset    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Scanlist idx 6, 0xff=unset    | Scanlist idx 7, 0xff=unset    | ARPS Report Channel Index     | 0   0 |RGP|DAP|APRSPTT|APRSTyp|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - RMode: Repater mode where 0=simplex, 1=positive TX offset, 2=negative TX offset.\n - BWd: Band width where 0=narrow (12.5kHz), 1=wide (25kHz).\n - PWR: Power where 0=low, 1=mid, 2=high, 3=turbo.\n - CMode: Channel mode, 0=analog, 1=digital, 2=analog + digi RX, 3=digital + analog RX.\n - TAr: Enable talkaround.\n - CaC: Enable call confirm.\n - RXO: Enable RX only.\n - CTR: Enable CTCSS phase reversal.\n - TDC: Enable TX DCS code.\n - TCT: Enable TX CTCSS tone.\n - RDC: Enable RX DCS code.\n - RCT: Enable RX CTCSS tone.\n - SquelchMode: Squelch mode 0=Carrier, 1=CTCSS/DCS, 2=Optional Signaling, 3=CTCSS/DCS and Optional Signaling,\n   4 = CTCSS/DCS or Optional Signaling\n - OptSig: Optional signalling where 0=off, 1=DTMF, 2=2-tone, 3=5-tone\n - TxPer: TX permit/admit criterion, 0=always, 1=colorcode, 2=channel free.\n - XFR: Exclude channel from roaming\n - LWK: Enable lone worker.\n - EEE: Enable enhanced encryption\n - EAT: Enable adaptive TDMA\n - EST: Enable simplex TDMA\n - SMC: SMS confirmation,\n - TSL: Time slot where 0=TS1, 1=TS2\n - DAD: Data ACK disable (inverted!)\n - ETM: Enable through mode\n - RNG: Ranging\n - RGP: Enable DMR-APRS RX\n - DAP: Enable DMR-APRS PTT\n - APRSPTT: FM APRS PTT mode 0=Off, 1=start of transmission, 2 = end of transmission\n - ATy: APRS Type: 0=Off, 1=Analog APRS, 2=DMR-APRS\n"
  },
  {
    "path": "doc/code/dmr6x2uv_generalsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Idle channel tone             | Display mode                  | Enable automatic key lock     | Automatic shutdown time       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Transmit timeout in 30s       | Language                      | Boot display                  | Enable boot password          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | VFO frequency step            | Squelch level VFO A           | Squelch level VFO B           | Power save mode               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | VOX sensitivity               | VOX delay in 100+500*n ms     | VFO scan type                 | MIC gain                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | PF1 short press function      | PF2 short press function      | PF3 short press function      | P1 short press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | P2 short press function       | Work mode B                   | Work mode A                   | STE Type                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | STE freq. no signal           | Group call hang time in sec   | Private call hang time in sec | Prewave time in 20ms          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Wake head period in 20ms      | WFM channel index             | WFM VFO Mode enable           | Work mode MEM zone A idx      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Work mode MEM zone B          | Unused set to 0x00            | Enable recording              | DTMF duration                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Man down enable               | Unused set to 0x00            | Display brightness            | Backlight duration            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Enable GPS                    | Enable SMS alert              | Unused set to 0x00            | WFM monitor enable            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Work mode main channel        | Enable sub channel            | TBST frequency                | Enable call alert             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | GPS Time zone                 | Talk permit tone              | Digital call reset tone       | VOX source                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Enable pro mode               | Unused set to 0x00            | Enable key tone               | Menu exit time                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | Filter own ID enable          | Startup tone                  | Enable call end prompt        | Max speaker volume            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Remote stun/kill enable       | Unused set to 0x00            | Remote monitor enable         | GPS RX positions              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Enable select TX contact      | PF1 long press function       | PF2 long press function       | PF3 long press function       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | P1 long press function        | P2 long press function        | Long press duration           | Enable Volume change prompt   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | Auto repeater A direction     | Digital monitor slot          | Digital monitor color code    | Digital monitor match ID      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Digital monitor hold slot     | Display last caller           | Unknown                       | Man down delay in sec         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Analog call hold in seconds   | Enable display clock          | Max headphone volume          | Enable GPS range message      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Unknown settings                                                                              | Enable enhanced audio         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | VFO Scan UHF minimum frequency in 10Hz, little endian                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | VFO Scan UHF maximum frequency in 10Hz, little endian                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | VFO Scan VHF minimum frequency in 10Hz, little endian                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | VFO Scan VHF maximum frequency in 10Hz, little endian                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Autorep. off. idx UHF, ffh=off| Autorep. off. idx VHF, ffh=off| Unknown                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c  ...                                                            | Enable maintain call channel  | Priority zone A idx, 0xff=off |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Priority zone B idx, 0xff=off | Enable SMS confirmation       | Call tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74 | Call tone frequency 2 in Hz, little endian                    | Call tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n78 | Call tone frequency 4 in Hz, little endian                    | Call tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c | Call tone duration 1 in ms, little endian                     | Call tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Call tone duration 3 in ms, little endian                     | Call tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Call tone duration 5 in ms, little endian                     | Idle tone frequency 1 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Idle tone frequency 2 in Hz, little endian                    | Idle tone frequency 3 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | Idle tone frequency 4 in Hz, little endian                    | Idle tone frequency 5 in Hz, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Idle tone duration 1 in ms, little endian                     | Idle tone duration 2 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Idle tone duration 3 in ms, little endian                     | Idle tone duration 4 in ms, little endian                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Idle tone duration 5 in ms, little endian                     | Reset tone frequency 1 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Reset tone frequency 2 in Hz, little endian                   | Reset tone frequency 3 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | Reset tone frequency 4 in Hz, little endian                   | Reset tone frequency 5 in Hz, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4 | Reset tone duration 1 in ms, little endian                    | Reset tone duration 2 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8 | Reset tone duration 3 in ms, little endian                    | Reset tone duration 4 in ms, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Reset tone duration 5 in ms, little endian                    | Record delay in 200ms         | Call display mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 | Call-sign display color       | Enable simplex repeater       | GPS ranging interv. in sec.   | Enable speaker in simpl. rep. |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb4 | Show current contact          | Key sound adjustable          | 0   0   0   0 |KNL|KBL|SKL|PKL| SimplRepSlot 0=TS1 1=TS2 2=CH |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb8 | Show last call on startup     | SMS format                    | GPS units                     |                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nbc | Auto repeater lower VHF frequency, in 10Hz, little endian                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 | Auto repeater upper VHF frequency, in 10Hz, little endian                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc4 | Auto repeater lower UHF frequency, in 10Hz, little endian                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc8 | Auto repeater upper UHF frequency, in 10Hz, little endian                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ncc | Auto repeater B direction     | AddrBk is sent with own code  | Enable default boot channel   | Boot zone A idx               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd0 | Boot zone B idx               | Boot ch A idx, 0xff=VFO       | Boot ch B idx, 0xff=VFO       | Enable keep last caller       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd4 | RX Backlight delay            | Display mode                  | ManDial GrpCall hgn time sec. | ManDial PrvCall hgn time sec. |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd8 |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndc |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Display mode: 0x00=Channel, 0x01=Frequency\n - VFO frequency step: 0x00=2.5kHz, 0x01=5kHz, 0x02=6.25kHz, 0x03=10kHz, 0x04=12.5kHz, 0x05=20kHz, 0x06=25kHz, 0x07=30kHz, 0x08=50kHz\n - Squelch level: 0x00=Off, 0x01=1, ..., 0x05=5\n - Automatic shutdown time: 0x00=Off, 0x01=10min, 0x02=30min, 0x03=60min, 0x04=120min\n - Power save mode: 0x00=Off, 0x01=1:1, 0x02=2:1\n - VOX sensitivity: 0x00=Off, 0x01=1, ..., 0x03=3\n - Work mode A/B: 0x00=VFO, 0x01=MEM\n - Work mode main channel: 0x00=A, 0x01=B\n - TBST frequency: 0x00=1000Hz, 0x01=1450Hz, 0x02=1750Hz, 0x03=2100Hz\n - GPS Time zone: 0x00=GMT-12, ..., 0x0c=GMT, 0x19=GMT+13\n - Boot display: 0x00=Default, 0x01=Custom Text, 0x02=Custom Image\n - VFO scan type: 0x00=TO, 0x01=CO, 0x02=SE\n - Key press function: 0x00=Off, 0x01=Voltage, 0x02=Power, 0x03=Repeater, 0x04=Reverse, 0x05=Digital Encryption, 0x06=Call, 0x07=VOX,\n                       0x08=V/M, 0x09=Sub PTT, 0x0a=Scan, 0x0b=FM, 0x0c=Alarm, 0x0d=Record Switch, 0x0e=Record, 0x0f=SMS, 0x10=Dial,\n                       0x11=GPS Information, 0x12=Monitor, 0x13=Main Ch. switch, 0x14=HotKey 1, ..., 0x19=HotKey 6, 0x1a=Work Alone,\n                       0x1b=Nuisance Delete, 0x1c=Digital Monitor, 0x1d=Sub Ch Switch, 0x1e=Priority Zone, 0x1f=Programming Scan,\n                       0x20=MIC sound quality, 0x21=Last call reply, 0x22=Channel type switch, 0x23=Simplex repeater, 0x24=Max. volume,\n                       0x25=Ranging, 0x26=Channel ranging, 0x27=Slot switch, 0x28=Analog squelch, 0x29=Roaming, 0x2a=Zone select,\n                       0x2b=Roaming settings, 0x2c=FixTime Mute, 0x2d=CTCSS/DCS settings, 0x2e=APRS type, 0x2f=APRS settings\n - Talk permit tone: 0x00=Off, 0x01=Digital, 0x02=Analog, 0x03=Both\n - STE Type: 0x00=Off, 0x01=Silent, 0x02=120deg, 0x03=180deg, 0x04=240deg\n - STE freq. no signal: 0x00=Off, 0x01=55.2Hz, 0x02=259.2Hz\n - VOX source: 0x00=Internal, 0x01=External, 0x02=Both\n - Display brightness: 0x00=1, ..., 0x04=5\n - Backlight duration: 0x00=Always, 0x01=5s, ..., 0x06=30s, 0x07=1m, ..., 0x0a=5m\n - MIC gain: 0x00=1, ..., 0x04=5\n - Max speaker/headphone volume: 0x00=Indoors, 0x01=1, ..., 0x08=8\n - Menu exit time: (n+1)*5s\n - Auto repeater A/B direction: 0x00=Off, 0x01=positive, 0x02=negative\n - Digital monitor slot: 0x00=Off, 0x01=single, 0x02=both´\n - Display last caller: 0x00=Off, 0x01=Show ID, 0x02=Show name, 0x03=Both\n - Call display mode: 0x00=Name based, 0x01=Call-sign based\n - Call-sign display color: 0x00=orange, 0x01=red, 0x02=yellow, 0x03=green, 0x04=turquoise, 0x05=blue, 0x06=white, 0x07=black\n - Enable keep last caller: A channel switch keeps laster caller.\n - RX Backlight delay: 0x00=Always, 0x01=1s, ..., 0x1e=30s.\n - Display mode: 0x00=black background, 0x01=blue background\n - Key sound adjustable: 0x00=Adjustable, 0x01=1, ..., 0x0f=15\n - GPS units: 0x00=metric, 0x01=imperial\n - KNL: Knob lock enable\n - KBL: Keyboard lock enable\n - SKL: Side key lock enable\n - PKL: Professional key lock enable\n - SMS format: 0x00=M-format, 0x01=H-format, 0x02=DMR standard\n"
  },
  {
    "path": "doc/code/dmr6x2uv_settingsextension.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enable send talker alias      | Talker alias display priority | Talker alias encoding         | Font Color                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Enable custom ch. background  | Roaming zone index 0-based    | Enable auto roaming           | Enable repeater check         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Out of repeater range alert   | Rep. out of range reminder    | Repeater check interval      | Repeater reconnections         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Start roaming condition       | Auto roaming interval        | Roaming eff. waiting time     | Roaming return condition       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Mute timer (n+1) min          | Encryption type               | Zone A name color             | Zone B name color             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Channel A name color          | Channel B name color          |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c |                                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n - Talker alias display priority: 0x00=None, 0x01=Contact alias, 0x02=Over-the-air alias\n - Talker alias encoding: 0x00=ISO-8, 0x01=ISO-7, 0x02=Unicode\n - Font Color: 0x00=White, 0x01=Black, 0x02=Orange, 0x03=Red, 0x04=Yellow, 0x05=Green, 0x06=Turquoise, 0x07=Blue\n - Encryption type: 0x00=Common(Basic), 0x01=AES\n - Zone/Channel A/B name color: 0x00=Orange, 0x01=Red, 0x02=Yellow, 0x03=Green, 0x04=Turquoise, 0x05=Blue, 0x06=White, 0x07=Black\n - Out of repeater range alert: 0x00=Off, 0x01=Bell, 0x02=Voice\n - Rep. out of range reminder: Number of times, the reminder is shown (n-1), 0x00=1, 0x01=2, ..., 0x09=10\n - Repeater check interval (n+1)*5 seconds: 0x00=5s, ..., 0x09=50s\n - Repeater reconnections: 0x00=3, 0x01=4, 0x03=5\n - Start roaming condition: 0x00=fixed time, 0x01=out of range\n - Auto roaming interval: 0x00=1min, ..., 0xff=256min\n - Roaming eff. waiting time: 0x00=off, 0x01=1s, ..., 0x1e=30s\n - Roaming return condition: 0x00=fixed time, 0x01=out of range\n\n"
  },
  {
    "path": "doc/code/dr1801uv_alarmsystembankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Alarm system count [0-8]      | Unused filled with 0x00                                                                       |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Alarm system 0                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c | Alarm system 1                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00ac | Alarm system 7                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00c0  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_alarmsystemelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Index+1,0=invalid             | Alarm enable                  | Unknown 0x0001                                                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Channel index, uint16 le, 0=None, 0xffff=selected             | Unknown 0x0000                                                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | System name, 16 x ASCII, 0-padded                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_channelbankelement.txt",
    "content": "         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\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000000 | Number of channels                                            | Unused filled with 0x00                                       |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000004 | Channel 0                                                                                                                  ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000034                                                                                                                              ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000038 | Channel 1                                                                                                                  ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000068  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n        ...                                                                                                                         ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00cfd0 | Channel 1023                                                                                                               ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d000  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d004 | Channel 0 name, 20 x ASCII, 0-padded                                                                                       ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d014  ...                                                                                                                         ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d018 | Channel 1 name, 20 x ASCII, 0-padded                                                                                       ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d028  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n        ...                                                                                                                         ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n11ff00 | Channel 1023 name, 20 x ASCII, 0-padded                                                                                    ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n120000  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_channelelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Index, uint16, little endian, 0xffff invalid                  | Type, 0x01=FM, 0x03=DMR       | Power 0x00=low, 0x01=high     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | RX frequency in Hz, uint32, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | TX frequency in Hz, uint32, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Contact index+1, uint16, little endian, 0=none                | Admit criterion               | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Color code                    | Time-slot, 1=TS1, 2=TS2       | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Encryption key index+1, 0=off | 0   0   0   0   0   0 |DCD|PCC| FM signaling                  | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Alarm sys index+1, 0=none     | Bandwidth 0x01=12.5, 0x02=25  | Auto-scan enable              | Scan list index+1, 0=none     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX CTCSS Freq in 0.1Hz/DCS code                               | RX sub-tone type              | RX DCS inverted               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | TX CTCSS Freq in 0.1Hz/DCS code                               | TX sub-tone type              | TX DCS inverted               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | 0x00                          |TKA| 0   0   0   0   0   0   0 | Unused set to 0x0000                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | PTT ID index+1, 0=none        |                               | Group List index+1, 0=None    | Unused filled with 0x00    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                            | Lone worker 0x00=off, 0x01=on |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Unused, filled with 0x00                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n - Admit criterion: 0x00=Always, 0x01 = Same CC/CTCSS, 0x02 = Channel Free\n - DCD: DCDM enable\n - PCC: Private call confirm\n - Sub-tone type: 0=None, 1=CTCSS, 2=DCS\n - FM Signaling: 0=none, 1=DTMF\n - TKA: Enable talk around.\n"
  },
  {
    "path": "doc/code/dr1801uv_contactbankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Number of elements, uint16, little endian                     | Index+1 of first element, uint16, little endian.              |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Contact 0                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c | Contact 1                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5fec | Contact 1023                                                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6000  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_contactelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Next index+1, uint16, little endian                           | Name length                   | 0x02 or 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | DMR ID, uint24, little endian                                                                 |GRP|PVC|ALC| 0   0   0   0   0 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Name, 16 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n - GRP: Group call\n - PVC: Private call\n - ALC: All call\n"
  },
  {
    "path": "doc/code/dr1801uv_dmrsettingselement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Hold time in sec [1-90]       | Remote listen in sec [10-120] | Active wait, 5ms [120-600]    | Active resent count [1-10]    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Pre-send count [4-15]         | Kill enc. enabled             | Active enc. enabled           | Check enc. enabled            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Kill dec. enabled             | Active dec. enabled           | Check dec. enabled            | SMS format                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Voice head count [0-20]       | 0   0   0   0 |RMD|RME|CAD|CAE| Unused set 0x0000                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - SMS format: 0 = Compressed IP, 1 = Defined Data, 2 = IP Data\n  - CAD: Call alter decoding\n  - CAE: Call alter encoding\n  - RME: Remote monitor encoding\n  - RME: Remote monitor decoding\n\n"
  },
  {
    "path": "doc/code/dr1801uv_dtmfidbankelement.txt",
    "content": "DTMF ID Bank:\n       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Number of IDs (fixed 16)      | Unused, set to 0x00                                                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | DTMF ID 0                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018 | DTMF ID 1                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0130 | DTMF ID 15                                                                                                                 ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0140  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_dtmfidelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Length                        | Unused, set to 0x00                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Code, 16 x one DTMF number per byte, 0-padded                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\n"
  },
  {
    "path": "doc/code/dr1801uv_dtmfsettingselement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | DTMF radio ID, 5 x ASCII, 0-padded                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                            | Radio ID Length               | Unused set to 0x00                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | DTMF kill code, 6 x ASCII, 0-padded                                                                                        ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                            | Kill code length              | Unused, set to 0x00           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | DTMF wake code, 6 x ASCII, 0-padded                                                                                        ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  ...                                                            | Wake code length              | Unused, set to 0x00           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018 | Delimiter                     | Group code                    | Decode response               | Auto reset time in sec. [5,60]|\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c | Kill/wake enable              | Kill type                     | Unused, set to 0x00                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | DTMF system bank                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | DTMF ID bank                                                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0194  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0198 | PTT ID bank                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0298  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Delimiter/Group code: 0h=None, ah=A, bh=B, ch=C, dh=D, eh=*, fh=#\n  - Decode response: 0=None, 1=Reminder, 2=Reply, 3=Both\n  - Kill type: 0=Disable TX, 1=Disable TX & RX\n"
  },
  {
    "path": "doc/code/dr1801uv_dtmfsystembankelement.txt",
    "content": "DTMF System Bank:\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DTMF system count (=4)        | Unused, set to 0x00                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | DTMF system 0                                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | DTMF system 1                                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | DTMF system 3                                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_dtmfsystemelement.txt",
    "content": "DTMF System:\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Side tone enable              | Unused, set to 0x00           | Pre time in ms, uint16, little endian                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Code duration in ms, uint16, little endian                    | Code Interval in ms, uint16, little endian                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Reset time in 0.1s, uint16, little endian                     | Unused, set to 0x00                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_encryptionkeybankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Encryption key 0                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Encryption key 1                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c | Encryption key 9                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0074  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_encryptionkeyelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Index + 1                     | Key length, uint16, little endian. Fixed to 0x08              | Unused, set to 0x00           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Key, 8 x ASCII, 0-padded                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_grouplistbankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Group list count              | Unused, filled with 0x00                                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Group list 0                                                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048 | Group list 1                                                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0088  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10c0 | Group list 63                                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1100  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_grouplistelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Number of members, uint16, little endian                      | Group list index+1, uint16, little endian, 0=invalid          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Member index0 +1, uint16, little endian                       | Member index1 +1, uint16, little endian                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Member index8 +1, uint16, little endian                       | Member index9 +1, uint16, little endian                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Unused, filled with 0                                                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_keysettingselement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Key 0 Index                   | Key 0, Short press function   | Key 0, Long press function    | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Key 1 Index                   | Key 1, Short press function   | Key 1, Long press function    | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Key 2 Index                   | Key 2, Short press function   | Key 2, Long press function    | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Unused or further key settings. Filled with 0x00                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Key index: SideKey1 = 0, SideKey2 = 1, TopKey = 2\n  - Key function: None = 0, ToggleAlertTones = 1, EmergencyOn = 2, EmergencyOff = 3,\n      TogglePower = 4, Monitor = 5, DeleteNuisance = 6, OneTouch1 = 7, OneTouch2 = 8, OneTouch3 = 9,\n      OneTouch4 = 10, OneTouch5 = 11, ToggleTalkaround = 13, ToggleScan = 14, ToggleEncryption = 15,\n      ToggleVOX = 16, ZoneSelect = 17, ToggleLoneWorker = 19, PhoneExit = 20\n"
  },
  {
    "path": "doc/code/dr1801uv_messagebankelement.txt",
    "content": "     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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Number of messages, uint32, little endian                                                                                     |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Message 0                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048 | Message 1                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0058  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0138 | Message 7                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0160  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_messageelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Index+1, 0=invalid            | Length                        | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Message, 64 x ASCII, 0-padded                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_onetouchsettingelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Contact index+1, uint16, little endian                        | Action                        | Message index + 1             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Type                          | DTMF ID index+1               | Unused set to 0x0000                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Action: 0=Call, 1=Message\n  - Type: 0=Disabled, 1=DMR, 2=Analog\n"
  },
  {
    "path": "doc/code/dr1801uv_onetouchsettingselement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | One-touch setting 0                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | One-touch setting 1                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | One-touch setting 4                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_pttidbankelement.txt",
    "content": "PTT ID Bank:\n       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Number of PTT IDs (fixed 20h) | Unused, set to 0x00                                                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | PTT ID 0                                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | PTT ID 1                                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00fc | PTT ID 31                                                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0100  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_pttidelement.txt",
    "content": "PTT ID:\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DTMF system index+1, 0=none   | TX mode                       | ID mode                       | Start ID index+1, 0=none      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | End ID index+1, 0=none        | Unused, set to 0x00                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nWhere:\n  - TX mode: 0=None, 1=Pre, 2=Post, 3=Both\n  - ID mode: 0=Forbidden, 1=Each, 2=Once\n"
  },
  {
    "path": "doc/code/dr1801uv_scanlistbankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Number of elements            | Unused, filled with 0x00                                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Scan list 0                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | Scan list 1                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00a0  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n02d4 | Scan list 9                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0320  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_scanlistelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Index+1, 0=invalid            | Number of entries             | Priority channel 1            | Priority channel 2            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Priority channel 1 index, uint16, little endian               | Priority channel 2 index, uint16, little endian               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Revert channel                | 0x00                          | Revert channel index, uint16, little endian                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Unused, filled with 0x00                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Name, 32 x ASCII, 0-padded                                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Entry channel index 0, uint16, little endian                  | Entry channel index 1, uint16, little endian                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Entry channel index 14, uint16, little endian                 | Entry channel index 15, uint16, little endian                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Priority channel 1/2: 0=None, 1=Fixed, 2=Selected\n  - Revert channel: 0 = Last active, 1 = Fixed, 2 = Selected\n"
  },
  {
    "path": "doc/code/dr1801uv_settingselement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID, uint24, little endian                                                                 | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 0x00                          | 0x00                          | 0x01                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Power save enable             | Power save mode               | VOX level 1-3                 | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | VOX delay, in 500ms           | Encryption enable             | Key-lock delay in sec. [1,90] | 0   0   0   0   0 |LS2|LS1|LPT|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Language                      | Squelch mode                  | 0x00                          | Roger tone enable             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | 0x00                          | 0x01                          | Ring tone [1,20], 0=off       | Key lock enable               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Radio name, 16 x ASCII, 0-padded                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | 0 |MST|DCE|AVE|DVE|ACO|DCO| 1 | 0x00                          | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Reverse burst frequency 0.1 Hz, 0=off                         | 0x01                          | Back-light time               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | VOX enable                    | 0x00                          | Campanding (?) enable         | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | 0x00                          | 0x00                          | Up channel mode               | Down channel mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | 0x00                          | 0x00                          | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Channel display               | Dual stand-by mode            | Scan mode                     | Boot screen                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Boot line 1, 8 x ASCII, 0-padded                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | Boot line 2, 8 x ASCII, 0-padded                                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | LED enable                    | Lone worker response time 1s  | 0   0   0   0   0   0 |BPE|PPE| Prog. password length         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Programming password, 6 x ASCII, 0-padded                                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58  ...                                                            | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | Lone worker reminder time 1s  | Boot password length          | Boot password, 6 x ASCII, 0-padded                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Power save mode: 0x01 = 50%, 0x02 = 25%, 0x03 = 12%\n  - LS1: Lock side-key 1\n  - LS2: Lock side-key 2\n  - LPT: Lock PTT\n  - Language: 0 = simplified Chinese, 1 = English\n  - DCO: Digital call out roger tone\n  - DVE: Digital voice end roger tone\n  - DCE: Digital call end roger tone\n  - ACO: Analog call out roger tone\n  - AVE: Analog voice end roger tone\n  - MST: Message tone\n  - Back-light time: 0 = Infinite, 1 = Off, 2 = 5s, 3 = 10s\n  - Squelch Mode: 0x00 = Normal, 0x01 = Silent\n  - Up/down channel mode: 0=channel, 1=VFO\n  - Channel display: 0 = Number, 1 = Name, 2 = Frequency\n  - Dual stand-by mode: 0=off, 1 = double double, 2 = double single\n  - Scan mode: 0=time, 1=carrier, 2=search\n  - Boot screen: 0 = Picture, 1 = Text\n  - BPE: Boot password enable\n  - PPE: Programming password enable\n"
  },
  {
    "path": "doc/code/dr1801uv_vfobankelement.txt",
    "content": "         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\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000000 | VFO A                                                                                                                      ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000030                                                                                                                              ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000034 | VFO B                                                                                                                      ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000064  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000068 | VFO A name, 20 x ASCII, 0-padded                                                                                           ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000078  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00007c | VFO B name, 20 x ASCII, 0-padded                                                                                           ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00008c  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_zonebankelement.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Zone count, uint16, little endian                             | Up current zone index, uint16, little endian                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Down current zone index, uint16_t, little endian              | Unused, set to 0x0000                                         |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Zone 0                                                                                                                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0070 | Zone 1                                                                                                                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00d4  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c90 | Zone 149                                                                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3cf4  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/dr1801uv_zoneelement.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Zone name, 32 x ASCII, 0-padded                                                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name length, uint16, little endian                            | Number of entries, uint16, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Zone index, uint16, little endian                             | 0x00                          | 0x00                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Member index 00, uint16, little endian, 0-based               | Member index 01, uint16, little endian, 0-based               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | Member index 30, uint16, little endian, 0-based               | Member index 31, uint16, little endian, 0-based               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_channel_bank.txt",
    "content": "        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\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00000 | Channel count                                                 | Channel 0                                                  ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00044  ...                                                                                                                            |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00048 | Channel 1                                                                                                                  ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n       ...                                                                                                                         ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n117bc | Channel 1023                                                                                                               ...\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n11800                                                                 |\n      +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_channel_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x 16bit unicode, little endian, 0-padded                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Bandwidth                     | Scan list index+1             | Channel type                  | Enable talkaround             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Enable RX only                | Unknown 0x01                  | Enable scan auto-start        | RX frequency               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28  ...                                                                                            | TX frequency               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                                                                                            | DTMF PTT setting index+1      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Power                         | Admit criterion               | Unknown 0x00                  | Unknown 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | RX tone mode                  | RX CTCSS frequency            | RX DCS code                   | TX tone mode                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38 | TX CTCSS frequency            | TX DCS code                   | Unknown 0x00                  | Unknown 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Timeslot                      | Color code [0-15]             | Group list index+2            | Unknown 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | TX contact index+1, 16bit unsigned int, little endian         | Emergency system index+1      | Unknown 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | Encryption key index+1        | Unknown 0x00                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Bandwidth: 0=12.5kHz, 1=25kHz\n  - Channel type: 0=FM, 1=DMR\n  - RX/TX frequency in Hz, 32bit unsigned int, little endian\n  - Power: 0=Low, 1=High\n  - Admit criterion: 0=Always, 1=Color code/CTCSS, 2=Free\n  - RX/TX tone mode: 0=None, 1=CTCSS, 2=DCS, 3=DCS inverted\n  - RX/TX CTCSS frequency:\n      62.5Hz,  67.0Hz,  69.3Hz,  71.9Hz, 74.4Hz,  77.0Hz,  79.7Hz,  82.5Hz,  85.4Hz,  88.5Hz,\n      91.5Hz,  94.8Hz,  97.4Hz, 100.0Hz, 103.5Hz, 107.2Hz, 110.9Hz, 114.8Hz, 118.8Hz, 123.0Hz,\n     127.3Hz, 131.8Hz, 136.5Hz, 141.3Hz, 146.2Hz, 151.4Hz, 156.7Hz, 159.8Hz, 162.2Hz, 165.5Hz,\n     167.9Hz, 171.3Hz, 173.8Hz, 177.3Hz, 179.9Hz, 183.5Hz, 186.2Hz, 189.9Hz, 192.8Hz, 196.6Hz,\n     199.5Hz, 203.5Hz, 206.5Hz, 210.7Hz, 218.1Hz, 225.7Hz, 229.1Hz, 233.6Hz, 241.8Hz, 250.3Hz,\n     254.1Hz\n  - RX/TX DCS code:\n      23,  25,  26,  31,  32,  36,  43,  47,  51,  53,  54,  65,  71,  72,  73,  74,\n     114, 115, 116, 122, 125, 131, 132, 134, 143, 145, 152, 155, 156, 162, 165, 172,\n     174, 205, 212, 223, 225, 226, 243, 244, 245, 246, 251, 252, 255, 261, 263, 265,\n     266, 271, 274, 306, 311, 315, 325, 331, 332, 343, 346, 351, 356, 364, 365, 371,\n     411, 412, 413, 423, 431, 432, 445, 446, 452, 454, 455, 462, 464, 465, 466, 503,\n     506, 516, 523, 526, 532, 546, 565, 606, 612, 624, 627, 631, 632, 645, 654, 662,\n     703, 712, 723, 731, 732, 734, 743, 754\n  - Timeslot: 0=TS1, 1=TS2, 2=DCDM-TS1, 3=DCDM-TS2\n  - Group list index: 0=current, 1=no-match, index+2 otherwise.\n"
  },
  {
    "path": "doc/code/gd73_contact_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Contact count, 16bit unsigned int, little endian              | Unused, filled 0x00                                        ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0800  ...                                                            | Contact 0                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0824  ...                                                                                        | Contact 1                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9bbc  ...                            | Contact 1023                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c00  ...                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_contact_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x 16bit unicode, little endian, 0-padded                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Type                          | DMR ID, 32bit unsigned int, little endian                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                            |\n   +---+---+---+---+---+---+---+---+\n\nWhere:\n  - Type: 0=Group Call, 1=Private Call, 2=All Call\n"
  },
  {
    "path": "doc/code/gd73_dmr_settings_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Call hangtime in sec-1 [1,90] | Active wait time              | Active retries                | TX preambles                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Enable dec. disable-radio     | Enable dec. radio-check       | Enable dec. enable-radio      | Unknown, filled with 0x00  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n  - Call hang time in seconds, stored -1s, i.e., 0=1s, 1=2s, ..., 10=11s (default), ..., 89=90s\n  - Active wait time (WTF): 0=120ms, 1=125ms, ..., 36=300ms (default), ..., 96=600ms\n  - Active retries (WTF) [1,10], 2=default\n  - TX preambles (WTF) [0,63], 0=default\n"
  },
  {
    "path": "doc/code/gd73_dtmf_code_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Length                        | Digit 0       | Digit 1       | Digit 2       | Digit 3       | Digit 4       | Digit 5       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Digit 6       |  Digit 7      | Digit 8       | Digit 9       | Digit 10      | Digit 11      | Digit 12      | Digit 13      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Digit 14      | Digit 15      |\n   +---+---+---+---+---+---+---+---+\n\nWhere:\n  - Length: Specifies the number of DTMF digits.\n  - digits: 0=0, ..., 9=9, 10=a, ..., 13=d, 14=*, 15=#\n"
  },
  {
    "path": "doc/code/gd73_dtmf_ptt_settings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DTMF system index             | PTT ID type                   | PTT ID mode                   | Connect ID index              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Disconnect ID index           |\n   +---+---+---+---+---+---+---+---+\nWhere:\n  - DTFM system index: 0-based [0,3]\n  - PTT ID type: 0=None, 1=Pre-only (default), 2=Post-only, 3=Both\n  - PTT ID mode: 0=Never, 1=Always, 2=Once\n  - (Dis-)Connect ID: 0-based [0,15]\n"
  },
  {
    "path": "doc/code/gd73_dtmf_system_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Side-tone enable              | Preamble duration             | Tone duration                 | Pause duration                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Dead time                     |\n   +---+---+---+---+---+---+---+---+\n\nWhere:\n  - Preamble duration: multiples of 10ms: 0=0ms, ..., 40=400ms (default), ..., 100=1000ms.\n  - Tone/pause duration: multiples of 10ms: 0=30ms (default), 1=40ms, ..., 187=1900ms.\n  - Dead time: multiples of 200ms: 0=0.2s, 1=0.4, ..., 10=2.2s, ..., 164=33.0s.\n"
  },
  {
    "path": "doc/code/gd73_encryption_key_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Key 0\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                            | Key 1                                                                                      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048  ...                                                                                            | Key 15                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_encryption_key_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Key size (in nibble)          | Key data\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                            |\n   +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_group_list_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Group list count              | Group list 0                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | Group list 1                                                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00a4  ...                                                                                            |  Group list 2              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50bc | Group list 249                                                                                                             ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n510c  ...                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_group_list_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 8 x 16 bit unicode, little endian, 0-padded                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Member count                  | Member 0 index+1, 16bit unsigned integer, little endian       | Member 1 index+1           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50  ...                            | Member 32 index+1                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_message_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Message count                 | Message 0                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  ...                                                            | Message 1                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00a0                                                                                                 | Message 2                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04c0 | Message 15                                                                                                                 ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0510  ...                            |\n     +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_message_element.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Message size (chars)          | Message text, 40 x 16bit unicode, little endian                                            ...                                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050  ...                            |\n     +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_one_touch_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused/unknown 0x00           | DMR contact index+1, little endian, 0=None                    | One-touch action              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Message index+1, 0=None       |\n   +---+---+---+---+---+---+---+---+\n\nWhere:\n  - One-touch action: 0=Call, 1=Message\n"
  },
  {
    "path": "doc/code/gd73_scan_list_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Scan list count               | Unknown 0x01                  | Unknown, filled with 0x00                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010  ...                            | Scan list 1                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n05a0  ...                                                            | Scan list 15                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0600  ...                            |\n     +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_scan_list_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 8 x 16bit unicode, little endian, 0-padded                                                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Channel count                 | Channel 0, index+1, little endian                             | Channel 1, index+1,        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50  ...                            | Priority channel 1 mode       | Priority channel 2 mode       | Pri. ch. 1, zone index+1      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Pri. ch. 2, zone index+1      | Pri. ch. 1, channel index+1, little endian                    | Pri. ch. 2, channel index+1...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58  ... little endian              | TX ch. mode                   | TX ch. zone index +1          | TX ch. channel index+1,    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ... little endian              | Hold time in 0.5s, 0..10s     | TX Hold time in 0.5s, 0..5s   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n - Priority channel 1/2 mode: 0=None, 1=Fixed, 2=Selected\n"
  },
  {
    "path": "doc/code/gd73_settings_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Radio name, 16 x Unicode, little-endian, filled/padded with 0x0000                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | DMR ID, 24bit unsigned int little endian                                                      | Pad byte 0x00                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Language                      | Unknown 0x00                  | Vox level [0,4]               | Squelch level [0,9]           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | TOT                           | TX Interrupt enable           | Power-save enable             | Power save timeout            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Read lock enable              | Write lock enable             | Unknown 0x00                  | Display mode                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 | Read lock pin, 6xASCII numbers [0x30,0x39], 0-padded                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34  ...                                                            | Write lock pin, 6xASCII numbers [0x30,0x39], 0-padded      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n38  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c | Unknown, 0x01                 | DMR mic gain [0,5]            | Unknown 0x01                  | FM mic gain [0,5]             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | Lone worker resp. timeout in minutes [1, 480], little endian  | Lone worker reminder period   | Boot display mode             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | Boot text line 1, 16 x 16bit Unicode, little endian, 0-padded                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64 | Boot text line 2, 16 x 16bit Unicode, little endian, 0-padded                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n84 | Enable key tones              | Key tone volume [0,13]        | Low battery tone enable       | Low battery tone vol. [0,13]  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n88 | Long press duration           | Unknown                                                       | P1 short press                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c | P1 long press                 | P2 short press                | P2 long press                 | Unused 0x00                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Unused 0x00                   | One-touch 1 settings                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94  ...                                                            | One-touch 2 settings                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98  ...                                                                                            | One-touch 3 settings       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | One-touch 4 settings                                                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na4  ...                            | One-touch 5 settings                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na8  ...                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nWhere:\n - Language: 0x00=Chinese, 0x01=English\n - TOT: Transmit timeout, 0=off, ..., 16=180, ..., 48=500s, N = (T-20)/10\n - Display mode: 0=Channel Name, 1 = Channel Frequency\n - Power-save timeout: Timeout in seconds [10, 60]\n - Lone worker reminder period: in seconds [10, 200]\n - Boot display mode: 0=Off, 1=Text, 2=Image, 3=both\n - Long press duration: In multiple of 0.5s: 0=0s, 1=0.5s, ..., 31=15.5s\n - P1/2 short/long press: 0=None, 1=Radio enable, 2=Radio check, 3=Radio disable, 4=Power level,\n   5=Monitor, 6=Emergency on, 7=Emergency Off, 8=Zone switch, 9=Scan toggle, a=VOX toggle,\n   b=One touch 1, ..., f=One Touch 5, 10=Talkaround toggle, 11=Lone worker, 12=TBST 1750Hz,\n   13=Call/Swell\n"
  },
  {
    "path": "doc/code/gd73_timestamp.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Frequency range               | Year/100                      | Year % 100                    | Month                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Day                           | Hour                          | Minute                        | Second                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unknown 0x05                  | Unused, filled with 0x00                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                            | Serial number, up to 16 x ASCII, 0-padded                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                            | Model name, up to 16 x ASCII, 0-padded                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                            | Device ID, up to 16 x ASCII, 0-padded                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40  ...                            | Model number, up to 16 x ASCII, 0-padded                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50  ...                            | Software version, up to 16 x ASCII, 0-padded                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60  ...                            |\n   +---+---+---+---+---+---+---+---+\n\nWhere\n  - Frequency range: 0 = 406.1-470.0MHz, 1=446.000-446.995MHz, 2=400.0-470.0MHz\n"
  },
  {
    "path": "doc/code/gd73_zone_bank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Zone count                    | Zone 0                                                                                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030  ...                                                            | Zone 1                                                     ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0060  ...                                                                                            | ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c10 | Zone 63                                                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c40  ...                            |\n     +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd73_zone_element.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 8 x 16bit Unicode, little endian, 0-padded                                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Channel count                 | Channel index+1 00, 16bit unsigned int, little endian         | Channel index+1 01         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14  ...                            | Channel index+1 02                                            | ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                            | Channel index+1 14                                            | Channel index+1, 15        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30  ...                            |\n   +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd77_callsign_db_entry.txt",
    "content": "0                               8\n+---+---+---+---+---+---+---+---+---+---+---+---+\n| DMR ID, 8xBCD | Name, 8xASCII, 0-terminated   |\n+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd77_callsign_db_header.txt",
    "content": "0                               8\n+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +\n|'I' 'D' '-' 'V' '0' '0' '1'  0 | # entries, LE | Entries     ... |\n+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +\n"
  },
  {
    "path": "doc/code/gd77_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x ASCII, 0xff terminated                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Mode (Analog/Digital)         | Unused, set to 0x00                                           | Transmit timeout              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Transmit timeout rekey        | Admit criterion               | Unknown, set to 0x50          | Scan list index (+1)          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | CTCSS/DCS RX                                                  | CTCSS/DCS TX                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Unused, set to 0x00           | TX DTMF system index (+1)     | Unused set to 0x00            | RX DTMF system index (+1)     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Unused, set to 0x00           | Privacy group index           | TX color-code                 | RX group list index (+1)      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | RX color code                 | Emergency system index (+1)   | Contact index (+1), little endian                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unused set to 0                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nDCC = Data call confirm, EAA = Emergency Alarm ACK, SLT = Repeater slot, PRV = Privacy Enable,\nPCC = Private call confirm, NSF = Non-STE Frequency, DCD = Direct call dual capacity mode, PWR = Power, ASE = Autoscan enable,\nLWK = Lone worker enable, TLK = Talk around enable, RXO = RX only, BW = Band width, SQT = Squelch type\n"
  },
  {
    "path": "doc/code/gd77_contact.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Contact DMR ID 8 BCD digits, little endian                                                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Type 0=Grp, 1=Priv, 2 = All   | RX tone 0=off, 1=on           | Ring style [0..10]            | Valid if 0xff, 0x00 otherwise |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd77_grouplist.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x ASCII, 0xff terminated                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Member 00 index (0=disabled), little-endian                   | Member 01 index, little-endian                                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Member 1e index, little-endian                                | Member 1f index, little-endian                                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd77_grouplistbank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | # Members GL 0 (N+1)          | # Members GL 1                | # Members GL 2                | # Members GL 3                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048 | # Members GL 48               | # Members GL 49               | # Members GL 4a               |  # Members GL 4e              |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c | Unused filled with 0                                                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n007c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0080 | RX Group List 0                                                                                                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00cc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n17f0 | RX Group List 7e                                                                                                           ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n183c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/gd77_scanlist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 15 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                            |TKB|  PLT  |ChM| 0   0   0   1 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1, 0=EOL, little-endian                     | Channel 01 index +1, little-endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c | Channel 30 index +1, little-endian                            | Channel 31 index +1, little-endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050 | Priority Channel 1, index+2, 0=None, 1=Current, little-endian | Priority Channel 2, index+2, 0=None, 1=Current, little-endian |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | TX Channel, index+2, 0=last active, 1=Current, little-endian  | Sig. hold time N x 25ms       | Pri. sample time N x 250ms    |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nChM = Channel mark; PLT = PL type; TKB = Talkback\n"
  },
  {
    "path": "doc/code/gd77_scanlistbank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | List 00 enable (0=disabled)   | List 01 enable                | List 2 enable                 | List 3 enable                 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c | List 3c enable                | List 3d enable                | List 3e enable                | List 3f enable                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040 | Scanlist 00                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0094  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n15e4 | Scanlist 3f                                                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n163c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/md390_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 |LWK| 1 |SQT|ASC| BandW | ChMod |   ColorCode   |TimeSlt|RXO|ALT|DCC|PCC| PRIV  |   PrivIdx     |DPD|CUH| 1   0 |EAA| 0 |RXFreqR|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 |AdimtCr|PWR|VOX| 0 |RVB|TXFreqR| Unknown set to 0xc3           | TX Contact name index + 1, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | 0   0 |      Tx Timeout       |    Tx Timeout Rekey Delay     | Emergency System index + 1    | Scan List index + 1           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | RX Group List index + 1       | GPS System index +1           | DTMF decode bitmap            | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Signaling System index +1  | TX Signaling System index +1  | Unused set to 0xff            | 1   1   1   1   1   1 |RXG|TXG|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name 16 x 16bit unicode characters                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description\n LWK = Lone Worker, default=0;\n SQT = Squelch type, 0=tight, 1=normal, default=1;\n ASC = Auto scan, default=0;\n BandW = Bandwidth, 0=12.5kHz, 2=25kHz, default = 0;\n ChMod = Channel Mode, 1=Analog or 2=Digital, default=1;\n ColorCode = ColorCode [0..15], default=1;\n TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1;\n RXO = RX Only, 0=off, 1=on, default=0;\n ALT = Allow Talkaround (inverted), default=1 (off);\n DCC = Data Call Confirmed, default=0;\n PCC = Private Call Confirmed, default=0;\n PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (off);\n PrivIdx = Privacy Index, [0,15], default=0.\n DPD = Display PTT ID (inverted), default = 1.\n CUH = Compressed UDP data header (inverted), default=1 (off).\n EAA = Emergency Alarm Ack, default = 0;\n RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low);\n AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0.\n PWR = Specifies the power, 0=low, 1=high.\n VOX = VOX Enable, 0=Disable, 1=Enable, default = 0;\n RVB = Reverse burst 0=disable, 1=enable, default=1;\n TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0;\n TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3;\n InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0;\n Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0;\n Power = Power, 0=low, 1=middle, 2=high, default=high;\n ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1;\n RXG = Receive GSP info (inverted), default=1;\n TXG = Send GSP info (inverted), default=1;\n"
  },
  {
    "path": "doc/code/md390_menusettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 1 | 1 | 1 |GPI| 1 |PRG|DSM|PWD| Unused, set to 0xff                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere:\n RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial;\n CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround;\n CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list;\n SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu;\n KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch;\n GPI = Hide GPS information; GPS = Hide GPS settings; PRG = Hide radio program menu; DSM = Show Display mode settings;\n PWD = Show password settings; NWZ = Show new zone menu; ZNS = Show zone settings; TXM = Show TX mode settings;\n MHT = Show menu hang time; PCM = Show private-call match settings; GCM = Show group-call match settings;\n NSL = Show new scan list menu; EDZ = Show edit zone menu;\n"
  },
  {
    "path": "doc/code/opengd77_callsign_db_entry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID stored in BCD, little-endian                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | String containing Name, Callsign, etc. Up to 15 bytes ASCII, 0x00 terminated and padded                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_callsign_db_header.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Magic 'ID-' string                                                                            | Size, 0x5d = 19bytes          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Version string, fixed to '001'                                                                | Unused, set to 0x00           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Number of contacts, 16bit uint, little endian                 | Unused set to 0x0000                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x ASCII, 0xff terminated                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Mode (Analog/Digital)         | Power [0-10]                  | Unused, set to 0.             | Transmit timeout in 15s       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | TOT rekey delay in sec.       | Admit criterion               | Unknown, set to 0x50          | Scan list index (+1) 0=None.  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | CTCSS/DCS RX, 4-digit BCD little endian, 0xffff=off.          | CTCSS/DCS TX, 4-digit BCD little endian, 0xffff=off.          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Unused, set to 0x00           | TX DTMF system index          |RID| Unused set to 0           | Radio ID, 24bit big-endian ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28  ...                                                            | TX color-code                 | RX group list index (+1)      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | RX color code                 | Emergency system index (+1)   | Contact index (+1), little endian                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unused set to 0                                                                               | Squelch in 5%, 0=default      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nRID = Channel radio ID enable, default=0.\nDCC = Data call confirm,\nEAA = Emergency Alarm ACK,\nSLT = Repeater slot,\nPRV = Privacy Enable,\nPCC = Private call confirm,\nNSF = Non-STE Frequency,\nDCD = Direct call dual capacity mode,\nPWR = Power,\nSZS = Scan zone skip enable, default=off. Was \"auto scan enable\" in orig. GD77.\nSAS = Scan all skip enable, default=off. Was \"lone worker enable\" in orig. GD77.\nTLK = Talk around enable,\nRXO = RX only,\nBW  = Band width,\nSQT = Squelch type\n"
  },
  {
    "path": "doc/code/opengd77_contact.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Contact DMR ID 8 BCD digits.                                                                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Type 0=Grp, 1=Priv, 2 = All   | RX tone 0=off, 1=on           | Ring style [0..10]            | Timeslot override             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_clear_screen_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 01h                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_control_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 06h                           | Action                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_display_text_request.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 03h                           | Column address (usually 0)    | Row address (00h, 10h, ..)    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Size (bug? always 3)          | Alignment                     | Inverted                      | Payload max. 16x ASCII     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_okay_response.txt",
    "content": "     7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+\n00 | '-' (2dh)                     |\n   +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_ping_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | feh                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_render_screen_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 03h                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+...+---+\n00 | Fixed prefix 'C' (43h)        | Command flag                  | Payload   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+...+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_show_cps_screen_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 00h                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_command_start_gps_request.txt",
    "content": "     7   6   5   4   3   2   1   0   7   6   5   4   3   2   1   0\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'C' (43h)        | 7h                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_read_request.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'R' (52h)        | Memory                        | Address 32bit unsigned int, big endian                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                            | Length, 16bit unsigned int, big endian                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_protocol_read_response.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Fixed prefix 'R' (52h)        | Length, 16bit unsigned int, big endian                        | Data, length bytes         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nnn  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_radio_info.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Struct version number, 32bit unsigned int, little-endian, default 0x0003                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Radio type, 32bit unsigned int, little endian                                                                                 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Git version string, 16x ASCII                                                                                                 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018 | Build date-time string, 16x ASCII                                                                                          ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0024  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0028 | Flash chip part number, 32 bit unsigned int, little endian                                                                    |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Features, 16bit unsigned int, little endian                   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_zone.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1, little endian.                           | Channel 01 index +1, little endian.                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00ac | Channel 78 index +1, little endian.                           | Channel 79 index +1, little endian.                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/opengd77_zonebank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Unknown                                                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | 68 bits encoding whether a particular zone within this bank is enabled.                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018  ...            | 0   0   0   0 | Unused set to 0                                                                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0030 | 68 Zones, 176bytes each                                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2ecc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/openrtx_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Channel Mode                  |Unused set to 0    |RXO| BandW | Power 10dBm + n*0.2           | RX Frequency in Hz, little ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ... endian                                                                                     | TX Frequency in Hz, little ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ... endian                                                                                     | Scan list index +1, 0=None    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | Group list index +1, 0=None   | Name, 32 x ASCII, 0-padded                                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c  ...                            | Description, 32 x ASCII, 0-padded                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c  ...                            | Ch. lat. int. part, signed    | Ch. lat. decimal part, little endian                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Ch. lon. int. part, signed    | Ch. lon. decimal part, little endian                          | Ch. alt. meters, little    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54  ... endian                     | Mode specific settings 5 bytes see below                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58  ...                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nChannel Mode: 0=FM, 1=DMR, 2=M17\nBandW: Bandwidth, ??\nRXO: RX only\n\nFM settings\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n55 | CTCSS RX tone idx         |RTE| CTCSS TX tone idx         |TTE| Unused, set to 0                                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n59  ...                            |\n   +---+---+---+---+---+---+---+---+\nRTE: RX Tone enable\nTTE: TX Tone enable\n\n\nDMR settings\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n55 | TX color code | RX color code | Time slot 1,2                 | Contact index, little endian                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n59 | Unused, set to 0              |\n   +---+---+---+---+---+---+---+---+\n\nM17 settings\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n55 | TX CAN        | RX CAN        | Encr. mode    | Channel mode  | GPS mode                      | Contact index, little endian...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n59  ...                            |\n   +---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/openrtx_contact.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 32 x ASCII, 0-pad                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Mode, only DMR or M17         | Mode specific settings, 6 bytes, see below                                                 ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nMode: 0=None, 1=FM, 2=DMR, 3=M17, only DMR and M17 are valid.\n\n\nDMR Contact settings.\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n21 | DMR ID, 32bit uint, little endian                                                                                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n25 | Unused, set to 0  |RXT| CType | Unused set to 0               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nRXT: RX Tone enable (ring).\nCType: Contact type.\n\nM17 Contact settings:\n     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n21 | M17 encoded address                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n25  ...                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/openrtx_header.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Magic number 0x43585452, little endian                                                                                     ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Version number (MAJOR << 8) | MINOR, little endian            | Author, 32 x ASCII, 0-pad                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28  ...                                                            | Description, 32 x ASCII, 0-pad                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48  ...                                                            | Timestamp, little endian                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50  ...                                                            | Contact count, little endian                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Channel count                                                 | Zone count                                                    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/openrtx_zone.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 32 x ASCII, 0-pad                                                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Number of channels, uint16_t, little endian                   | Channel Index 0, uint32_t, little endian                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                            | Channel Index 1, uint32_t, little endian                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28  ...                                                            | ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\n"
  },
  {
    "path": "doc/code/radioddity_bootsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Boot text enable (0x01=text)  | Boot passwd enable            | Unused, set to 0                                              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Boot passwd, 6 x BCD numbers, big endian                                                      | Unused set to 0               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unused set to 0                                                                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_boottext.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Boot message 1, 16 x ASCII, 0xff terminated and padded.                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Boot message 2, 16 x ASCII, 0xff terminated and padded.                                                                    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_buttonsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown, set to 0x01          | Long press duration in 250ms  | Short SK1 event               | Long SK1 event                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Short SK2 event               | Long press SK2 event          | Short press TK action (GD77)  | Long press TK action (GD77)   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | One-touch 0 action type       | One-touch 0, contact index                                    | One-touch 0, message index    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | One-touch 5 action type       | One-touch 5, contact index                                    | One-touch 5, message index    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x 1byte ASCII encoded, 0xff terminated text                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency in 10Hz encoded as 8-digits BCD, little-endian                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency in 10Hz encoded as 8-digits BCD, little-endian                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Mode 0=analog, 1digital       | Unused set to 0                                               | TX timeout (TOT) N x 15s      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | TOT rekey delay in s          | Admit 0=always, 1=free, 2=CC  | !!! Unknown set to 0x50 !!!   | Scan-list index +1            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Unused set to 0x00            | TX signaling index +1         | Unused set to 0x00            | RX signaling index +1         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | !!! Unknown set to 0x16 !!!   | Privacy Grp 0=None, 1=53474c39| TX Color code [0,15]          | RX group-list index +1        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | RX Color code [0,15]          | Emergency sys. index +1       | TX contact index +1                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |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 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unused set to 0                                                                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nEAC = Emergency alarm ACK; DCC = Data call confirmed; SLT = Repeater slot {1,2}; PRV = Privacy enabled; PCC = Private call confirmed;\nSTE = Non STE is frequency(?!?); DCM = Dual capacity direct mode enable; PWR = Power; VOX = Voice operated switch wnable;\nTLK = Allow talkaround; RXO = RX only; BW = Bandwidth;\n\n"
  },
  {
    "path": "doc/code/radioddity_channelbank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | 128 bits encoding whether a particular channel within this bank is enabled.                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | 128 Channels, 56bytes each                                                                                                 ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c0c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_contact.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Contact DMR ID 8 BCD digits.                                                                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Type 0=Grp, 1=Priv, 2 = All   | RX tone 0=off, 1=on           | Ring style [0..10]            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_dtmfcontact.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | DTMF number, 16 digits, table = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, *, #}, 1 byte per digit                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_generalsettings.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Radio name, 8 x 1byte ASCII encoded, 0xff terminated text                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | 8-digit BCD DMR ID, big-endian.                                                                                               |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Unused set to 0x00000000                                                                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Unused set to 0x00            | Tx preamble N x 60ms          | Monitor type 0=open, 1=silent | VOX sensitivity [1..10]       |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Low battery interval N x 5s   | Call alert dur N x 5s         | Lone worker response in min   | Lone worker reminder in sec   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018 | 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|\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c | 1 |IQO|DAL| 0   0   0   0   0 | ScanM |ANI|DBW|TXT| 0   0   0 | Rep. STE      | Rep. delay    | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Prog. passwd 8 x ASCII, 0xff terminated                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0024  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nUNT = Unknown number tone\nRST = Reset tone\nUMV = Up-channel mode VFO\nDMV = Down-channel mode VFO\nSBP = Send wakeup preamble.\nSBR = Enable sleep mode during RX.\nDAT = Disables all tones;\nFIT = Channel free indication tone\nSTT = Self-test pass tone\nPAT = Talk permit tone analog\nPDT = Talk permit tone digital\nIQO = Inhibit quick-key override\nDAL = Disables all LEDs\nScanM = Scan mode\nANI = Enable animation\nDBW = Always TX on active channel on double-wait\nTXT = TX exit tone\n"
  },
  {
    "path": "doc/code/radioddity_grouplist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Contact 00 index +1, 0 end of list, little endian             | Contact 01 index +1, little endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Contact 14 index +1, little endian                            | Contact 15 index +1, little endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_grouplistbank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | N Contacts Group 00, 0=EOL    | N Contacts Group 01           | N Contacts Group 02           |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                            | N Contacts Group 61           | N Contacts Group 62           | N Contacts Group 63           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040 | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n007c  ...                            | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0080 | 64 Group lists, 32 bytes each                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n087c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_menusettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 1   1   1   0   0 |DBL|DSM|OTC| Unknown, set to 0xff          |ChanDis| 0   0 |BKLTime|KeyLock|DblWait|KYT| 1 | 0   0   0   0 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nRMO = Remote monitor menu enable, RCK = Radio check menu enable, MND = Manual dial menu enable, ECN = Edit contact menu enable,\nCAL = Call alert menu enable, ESL = Edit scan list enable, SCS = Scan start enable, MSG = Message menu enable,\nINS = Intro screen menu enable, BKL = Backlight menu enable, PWR = Power setting enable, TON = Tone menu enable,\nTLK = Talkaround menu enable, PRG = Program-password menu enable, RDI = Radio-disable menu, REN = Radio-enable menu,\nANC = Answered calls menu enable, MIC = Missed calls menu enable, PLK = Password and lock menu enable, VOX = VOX menu enable,\nPRV = Privacy menu enable, SQL = Squelch menu enable, LED = LED indicator menu enable, KPL = Keyboard lock menu enable,\nDBL = Double-wait menu enable, DSM = Display mode menu enable, OTC = Outgoing calls menu enable, BKLTime = Backlight time,\nDblWait = Double wait mode, KYT = Key tone enable,\n"
  },
  {
    "path": "doc/code/radioddity_messagebank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | # Messages                    | Unused filled with 0                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Length message 0              | Length message 1              | Length message 2              | Length message 3              |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044 | Length message 3c             | Length message 3d             | Length message 3e             | Length message 3f             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048 | Unused filled with 0                                                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0084  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0088 | Message 0, max 144 x ASCII, 0xff terminated                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0114  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n23f8 | Message 3f, max 144 x ASCII, 0xff terminated                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2484  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_privacy.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Privacy Type, 0=None, 1=basic | Unused, set to 0x00           | Key bitmap                                                    |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004 | Unused set to 0x00000000                                                                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Key 01, 32bit, 0xffff=off                                                                                                     |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Key 01 copy, 32bit, 0xffff=off                                                                                                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Key 02, 32bit, 0xffff=off                                                                                                     |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Key 02 copy, 32bit, 0xffff=off                                                                                                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0080 | Key 16, 32bit, 0xffff=off                                                                                                     |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0084 | Key 16 copy, 32bit, 0xffff=off                                                                                                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_scanlist.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 15 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                            |TKB|  PLT  |ChM| 0   0   0   0 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1, 0=EOL, little-endian                     | Channel 01 index +1, little-endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c | Channel 30 index +1, little-endian                            | Channel 31 index +1, little-endian                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050 | Priority Channel 1, index+2, 0=None, 1=Current, little-endian | Priority Channel 2, index+2, 0=None, 1=Current, little-endian |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | TX Channel, index+2, 0=last active, 1=Current, little-endian  | Sig. hold time N x 25ms       | Pri. sample time N x 250ms    |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nChM = Channel mark; PLT = PL type; TKB = Talkback\n"
  },
  {
    "path": "doc/code/radioddity_scanlistbank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Valid scanlist 00 1=Yes, 0=No | Valid scanlist 01             | Valid scanlist 02             |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00f8  ...                            | Valid scanlist 249            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00fc | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0100 | 250 scan lists, 88 bytes each                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n56ec  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_vfochannel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x ASCII, 0xff terminated                                                                                          ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 x BCD digits, little-endian                                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Mode (Analog/Digital)         | Unused, set to 0x00                                           | Transmit timeout              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | Transmit timeout rekey        | Admit criterion               | Unknown, set to 0x50          | Scan list index (+1)          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | CTCSS/DCS RX                                                  | CTCSS/DCS TX                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Unused, set to 0x00           | TX DTMF system index (+1)     | Unused set to 0x00            | RX DTMF system index (+1)     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Unused, set to 0x00           | Privacy group index           | TX color-code                 | RX group list index (+1)      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | RX color code                 | Emergency system index (+1)   | Contact index (+1), little endian                             |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Offset Frequency in 1kHz, 4-digit BCD, little endian          | StepSize      |OffsDir| 0   0 |                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nDCC = Data call confirm, EAA = Emergency Alarm ACK, SLT = Repeater slot, PRV = Privacy Enable,\nPCC = Private call confirm, NSF = Non-STE Frequency, DCD = Direct call dual capacity mode, PWR = Power, ASE = Autoscan enable,\nLWK = Lone worker enable, TLK = Talk around enable, RXO = RX only, BW = Band width, SQT = Squelch type\nOffsDir = Offset direction, 0=none, 1=+, 2=-,\n"
  },
  {
    "path": "doc/code/radioddity_zone.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1                                           | Channel 01 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Channel 14 index +1                                           | Channel 15 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/radioddity_zonebank.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | 250 bits encoding whether a particular zone within this bank is enabled.                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c  ...                                                                                                    | 0 | 0 | 0 | 0 | 0 | 0 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | 250 Zones, 48bytes each                                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2efc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5r_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name, 16 x 1byte ASCII encoded, 0xff terminated text                                                                       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency in 10Hz encoded as 8-digits BCD, little-endian                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency in 10Hz encoded as 8-digits BCD, little-endian                                                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | Mode 0=analog, 1digital       | Unused set to 0                                               | TX timeout (TOT) N x 15s      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | TOT rekey delay in s          | Admit 0=always, 1=free, 2=CC  | !!! Unknown set to 0x50 !!!   | Scan-list index +1            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Unused set to 0x00            | TX signaling index +1         | Unused set to 0x00            | RX signaling index +1         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | !!! Unknown set to 0x16 !!!   | Privacy Grp 0=None, 1=53474c39| TX Color code [0,15]          | RX group-list index +1        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | RX Color code [0,15]          | Emergency sys. index +1       | TX contact index +1                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |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 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n34 | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            | Squelch [0..9]                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nEAC = Emergency alarm ACK; DCC = Data call confirmed; SLT = Repeater slot {1,2}; PRV = Privacy enabled; PCC = Private call confirmed;\nSTE = Non STE is frequency(?!?); DCM = Dual capacity direct mode enable; PWR = Power; VOX = Voice operated switch wnable;\nTLK = Allow talkaround; RXO = RX only; BW = Bandwidth;\n\n"
  },
  {
    "path": "doc/code/rd5r_generalsettings.txt",
    "content": "       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\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Radio name, 8 x 1byte ASCII encoded, 0xff terminated text                                                                  ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | 8-digit BCD DMR ID                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c | Unused set to 0x00000000                                                                                                      |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Unused set to 0x00            | Tx preamble N x 60ms          | Monitor type 0=open, 1=silent | VOX sensitivity [1..10]       |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Low battery interval N x 5s   | Call alert dur N x 5s         | Lone worker response in min   | Lone worker reminder in sec   |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0018 | 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|\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c | 1 |IQO|DAL| 0   0   0   0   0 | ScanM |ANI|DBW|TXT| 0   0   0 | Rep. STE      | Rep. delay    | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0020 | Prog. passwd 8 x ASCII, 0xff terminated                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0024  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nUNT = Unknown number tone\nRST = Reset tone\nUMV = Up-channel mode VFO\nDMV = Down-channel mode VFO\nSBP = Save battery by disable preamble\nSBR = Save battery by disable receive\nDAT = Disables all tones;\nFIT = Channel free indication tone\nSTT = Self-test pass tone\nPAT = Talk permit tone analog\nPDT = Talk permit tone digital\nIQO = Inhibit quick-key override\nDAL = Disables all LEDs\nScanM = Scan mode\nANI = Enable animation\nDBW = Always TX on active channel on double-wait\nTXT = TX exit tone\n"
  },
  {
    "path": "doc/code/rd5r_timestamp.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Year, 4xBCD, big-endian                                       | Month 2xBCD                   | Day 2xBCD                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Hour, 2xBCD                   | Minute, 2xBCD                 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rbootsettings.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Boot text enable (0x01=text)  | Boot passwd enable            | Unused, set to 0                                              |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Boot passwd, 6 x BCD numbers, big endian                                                      | Unused set to 0               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | Unused set to 0                                                                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rbuttonsettings.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unknown, set to 0x01          | Long press duration in 250ms  | Short SK1 event               | Long SK1 event                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Short SK2 event               | Long press SK2 event          | Short press TK action (GD77)  | Long press TK action (GD77)   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | One-touch 0 action type       | One-touch 0, contact index                                    | One-touch 0, message index    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | One-touch 5 action type       | One-touch 5, contact index                                    | One-touch 5, message index    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rcontact.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Contact DMR ID 8 BCD digits.                                                                                                  |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0014 | Type 0=Grp, 1=Priv, 2 = All   | RX tone 0=off, 1=on           | Ring style [0..10]            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rdtmfcontact.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | DTMF number, 16 digits, table = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, *, #}, 1 byte per digit                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rgrouplist.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Contact 00 index +1, 0 end of list                            | Contact 01 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Contact 14 index +1                                           | Contact 15 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rgrouplisttab.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | N Contacts Group 00, 0=EOL    | N Contacts Group 01           | N Contacts Group 02           |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n003c  ...                            | N Contacts Group 61           | N Contacts Group 62           | N Contacts Group 63           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0040 | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n007c  ...                            | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0080 | 64 Group lists, 32 bytes each                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n087c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rmenusettings.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 1   1   1   0   0 |DBL|DSM|OTC| Unknown, set to 0xff          |ChanDis| 0   0 |BKLTime|KeyLock|DblWait|KYT| 1 | 0   0   0   0 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nRMO = Remote monitor menu enable, RCK = Radio check menu enable, MND = Manual dial menu enable, ECN = Edit contact menu enable,\nCAL = Call alert menu enable, ESL = Edit scan list enable, SCS = Scan start enable, MSG = Message menu enable,\nINS = Intro screen menu enable, BKL = Backlight menu enable, PWR = Power setting enable, TON = Tone menu enable,\nTLK = Talkaround menu enable, PRG = Program-password menu enable, RDI = Radio-disable menu, REN = Radio-enable menu,\nANC = Answered calls menu enable, MIC = Missed calls menu enable, PLK = Password and lock menu enable, VOX = VOX menu enable,\nPRV = Privacy menu enable, SQL = Squelch menu enable, LED = LED indicator menu enable, KPL = Keyboard lock menu enable,\nDBL = Double-wait menu enable, DSM = Display mode menu enable, OTC = Outgoing calls menu enable, BKLTime = Backlight time,\nDblWait = Double wait mode, KYT = Key tone enable,\n"
  },
  {
    "path": "doc/code/rd5rmessagetab.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | # Messages                    | Unused filled with 0                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0004  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0008 | Length message 0              | Length message 1              | Length message 2              | Length message 3              |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0044 | Length message 3c             | Length message 3d             | Length message 3e             | Length message 3f             |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0048 | Unused filled with 0                                                                                                       ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0084  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0088 | Message 0, max 144 x ASCII, 0xff terminated                                                                                ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0114  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...                                                                                                                         ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n23f8 | Message 3f, max 144 x ASCII, 0xff terminated                                                                               ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2484  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rscanlist.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 15 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                            | 0   0   0   0 |ChM|  PLT  |TKB|\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1, 0 end of list                            | Channel 01 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004c | Channel 30 index +1                                           | Channel 31 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0050 | Priority Channel 1, index+2, 0=None, 1=Current                | Priority Channel 2, index+2, 0=None, 1=Current                |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0054 | TX Channel, index+1, 0=last active                            | Sig. hold time N x 25ms       | Pri. sample time N x 250ms    |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nChM = Channel mark; PLT = PL type; TKB = Talkback\n"
  },
  {
    "path": "doc/code/rd5rscanlisttab.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Valid scanlist 00 1=Yes, 0=No | Valid scanlist 01             | Valid scanlist 02             |                            ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00f8  ...                            | Valid scanlist 249            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00fc | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            | Unused set to 0x00            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0100 | 250 scan lists, 88 bytes each                                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n56ec  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rvfosettings.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | VFO A settings ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0034  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0038 | VFO B settings ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n006c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rzone.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Name, 16 ASCII encoded bytes, 0xff terminated                                                                              ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | Channel 00 index +1                                           | Channel 01 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n      ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n002c | Channel 14 index +1                                           | Channel 15 index +1                                           |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/rd5rzonetab.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | 250 bits encoding whether a particular zone within this bank is enabled.                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n001c  ...                                                                                                    | 0 | 0 | 0 | 0 | 0 | 0 |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0010 | 250 Zones, 48bytes each                                                                                                    ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2edc  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_buttonsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0                                              | Side button 1 short press     | Side button 1 long press      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Side button 2 short press     | Side button 2 long press      | Unused set to 0x00                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | Unknown set to 0x01           | Long press dur x250ms         | Unused set to 0xffff                                          |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | One-touch setting 0                                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | One-touch setting 5                                                                                                           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 |LWK| 1   1 |ASC| BandW | ChMod |   ColorCode   |TimeSlt|RXO|ALT|DCC|PCC| PRIV  |   PrivIdx     |DPD| 1   1   0 |EAA| 0 |RXFreqR|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 |AdimtCr| 1 |VOX| 0   1 |TXFreqR| Device specific settings      | TX Contact name index + 1, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | 0   0 |      Tx Timeout       |    Tx Timeout Rekey Delay     | Emergency System index + 1    | Scan List index + 1           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | RX Group List index + 1       | GPS System index +1           | DTMF decode bitmap            | Device specific settings      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | RX Signaling System index +1  | TX Signaling System index +1  | Unused set to 0xff            | 1   1   1   1   1   1 |RXG|TXG|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name 16 x 16bit unicode characters                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n LWK = Lone Worker, default=0.\n ASC = Auto scan, default=0.\n BandW = Bandwidth, 0=12.5kHz, 1=20kHz or 2=25kHz, default = 0.\n ChMod = Channel Mode, 1=Analog or 2=Digital, default=1.\n ColorCode = ColorCode [0..15], default=1.\n TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1.\n RXO = RX Only, 0=off, 1=on, default=0.\n ALT = Allow Talkaround (inverted), default=1 (off).\n DCC = Data Call Confirmed, default=0;\n PCC = Private Call Confirmed, default=0.\n PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (basic).\n PrivIdx = Privacy Index, [0,15], default=0.\n DPD = Display PTT ID (inverted), default = 1.\n EAA = Emergency Alarm Ack, default = 0.\n RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low).\n AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0.\n VOX = VOX Enable, 0=Disable, 1=Enable, default = 0;\n TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0.\n Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0.\n LMS = Leader/MS: Leader or MS, default=1.\n DCD = DCDM switch (inverted), default=1.\n ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1.\n RXG = Receive GSP info (inverted), default=1.\n TXG = Send GSP info (inverted), default=1.\n"
  },
  {
    "path": "doc/code/tyt_contact.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID (binary coded), little endian                                                          | 1   1 |RXT| 0   0   0 | Type  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Contact Name 16 x 16bit Unicode                                                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nwhere\n - Type = Call type where 0=disabled, 1=group, 2=private, 3=all\n - RXT  = Receive tone enable;\n"
  },
  {
    "path": "doc/code/tyt_emergencysettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 1   1   1   1   1 |ERM|RMD|RDD| Remote mon durarion in 10s    | TX time-out in 25ms           | Message limit [1-3]           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Unused, set to 0xff                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\n\nwhere:\n ERM = Emergency remote monitor decode.\n RMD = Remote monitor decode.\n RDD = Radio disable decode.\n"
  },
  {
    "path": "doc/code/tyt_emergencysystem.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Name 16 x 16bit unicode, 0-terminated                                                                                      ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | 0   1 | AMode | 1   1 | AType | Impolite retires              | Polite retires                | Hot MIC durarion in 10s       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 | Revert channel index +1, 0=Disabled, 0xffff=selected          | Unused, set to 0xffff                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_gpssystem.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Revert channel index + 1, little endian, 0 = current          | Repeat intv. N x 30s, 0=off   | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Destination contact index + 1, little endian, 0 = none        | 10 x unused bytes set to 0xff                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_grouplist.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Group List Name 16 x 16bit unicode characters                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Member Contact index+1 00, little endian                      |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                            | Member Contact index+1 31, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_menusettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 1 | 1 | 1 | 1 | 1 |PRG|DSM|PWD| Unused, set to 0xff                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere:\n RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial;\n CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround;\n CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list;\n SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu;\n KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch;\n PRG = Hide radio program menu; DSM = Show Display mode settings; PWD = Show password settings; NWZ = Show new zone menu;\n ZNS = Show zone settings; TXM = Show TX mode settings; MHT = Show menu hang time; PCM = Show private-call match settings;\n GCM = Show group-call match settings; NSL = Show new scan list menu; EDZ = Show edit zone menu;\n"
  },
  {
    "path": "doc/code/tyt_onetouchsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 1   1 | Type  | Action        | Message index +1, 0=none.     | Contact index +1, little endian, 0=none.                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_privacy.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Enhanced key 0, 16bytes raw                                                                                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Enhanced key 7, 16 bytes raw                                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n7c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 | Unused, filled with 0xff                                                                                                   ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Basic key 0, 16bit uint little-endian                         | Basic Key 1, 16bit uint little-endian                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n    ...                                                                                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac | Basic key 14, 16bit uint little-endian                        | Basic key 15, 16bit uint little-endian                        |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nNote: All keys are stored in reversed order. For both, basic and AES keys.\n"
  },
  {
    "path": "doc/code/tyt_scanlist.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Scan List Name 16 x 16bit unicode characters, each unicode char is encoded in little-endian                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Priority Channel 1, index+1, 0x0000=current, 0xffff=none      | Priority Channel 1, index+1, 0x0000=current, 0xffff=none      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 |Tx Designated Channel index+1, 0x0000=current, 0xffff=none     | !!! Unknown set to 0xf1 !!!   | Hold Time N x 25ms            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Prio. Sample Time N x 250ms   | Unused, set to 0xff           | Member Channel index+1 00                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Member Channel index+1 01                                     |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64  ...                                                            | Member Channel index+1 30                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nNote:\n - All indices are encoded little-endian.\n"
  },
  {
    "path": "doc/code/tyt_settings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Intro Line 1, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Intro Line 2, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n26 | Reserved 24 bytes, set to 0xff                                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 | 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            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | DMR ID 24bit, little endian                                                                   | Unused 0x00                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms  | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10]       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Unused, set 0x00              | Unused, set 0x00              | RX Low Bat. Intv N x 5s       | Call Alert Tone Dur N x 5s    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec  | Unused, set to 0x00           | Scan Dig. Hang Time N x 100ms |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Scan Anal. Hang Time N x 100ms| 0   0   0   0   0   0 |BcLTime| Keypad Lock Time N x 5s       | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | Power-on password 8 x BCD numbers, 0x00000000=default                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | Radio prog. password 8 x BCD numbers, 0x00000000=disabled                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Unused set to 0xffffffff                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | Unused set to 0xffffffff                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Radio name 16 x 16bit unicode chars                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1;\n DAL = Disable all LEDs (inverted);\n TPD = Talk permit tone digital;\n TPA = Talk permit tone analog;\n PWE = Password and lock enable (inverted);\n CIT = Ch. free indication tone (inverted);\n DAT = Disable all tones (inverted);\n SMR = Save Mode Receive;\n SPR = Save Preamble;\n INP = Intro Picture;\n BcLTime = Backlight time, 0=Always, t=n*5s;\n Keypad Lock Time = 0xff=manual otherwise\n"
  },
  {
    "path": "doc/code/tyt_timestamp.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0x00           | Date YYYY-MM-DD hh:mm:ss encoded as 14 BCD numbers                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | CPS Version, VV.VV encoded using table {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?}                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tyt_zone.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Zone name, 16 x 16bit unicode chars                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Member Channel index+1 00 VFO A                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                            | Member Channel index+1 15 VFO A                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tytcallsigndbentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID in binary format                                                                       | Unused set to 0xff            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Callsign 16 x ASCII                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Name, City, etc.; 100 x ASCII                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tytcallsigndbindex.txt",
    "content": "         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\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000000 | Number of entries in DB                                                                       | Pad set to 0xff               |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000004 | 4096 Index entries, see IndexElement::Entry                                                                                ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004000  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/tytcallsigndbindexentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 12 high-bits of the DMR ID                    | 20-bit index within the callsign DB list                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390_bootsettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0xffffff                                                                       | Boot zone index +1            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Boot CH A index +1 (in zone)  | Unused, set to 0xff           | Boot CH B index +1 (in zone)  | Unused set to 0xffff       ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08  ...                            | Unknown index, set to 0x0001, little endian                   | Unused filled with 0xff    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390_channel.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 |LWK| 1   1 |ASC| BandW | ChMod |   ColorCode   |TimeSlt|RXO|ALT|DCC|PCC| PRIV  |   PrivIdx     |DPD| 0 |EAA| 1   1   0 |RXFreqR|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 |AdimtCr| 1 |VOX| 0   1 |TXFreqR|TOffFre|InCallC| 0 |PrK| 0   0 | TX Contact name index + 1, little endian                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | 0   0 |      Tx Timeout       |    Tx Timeout Rekey Delay     | Emergency System index + 1    | Scan List index + 1           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c | RX Group List index + 1       | GPS System index +1           | DTMF decode bitmap            | Squelch [0..9]                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | RX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | TX Frequency, 8 digits BCD, little endian                                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n18 | RX CTCSS/DCS                                                  | TX CTCSS/DCS                                                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Name 16 x 16bit unicode characters / Stepsize (byte 0x20)                                                                  ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n LWK = Lone Worker, default=0.\n ASC = Auto scan, default=0.\n BandW = Bandwidth, 0=12.5kHz, 1=20kHz or 2=25kHz, default = 0.\n ChMod = Channel Mode, 1=Analog or 2=Digital, default=1.\n ColorCode = ColorCode [0..15], default=1.\n TimeSlt = Repeater/time-slot, 2=TS2, 1=TS1, default=1.\n RXO = RX Only, 0=off, 1=on, default=0.\n ALT = Allow Talkaround (inverted), default=1 (off).\n DCC = Data Call Confirmed, default=0;\n PCC = Private Call Confirmed, default=0.\n PRIV = Privacy type, 0=None, 1=Basic or 2=Enhanced, default=0 (basic).\n PrivIdx = Privacy Index, [0,15], default=0.\n DPD = Display PTT ID (inverted), default = 1.\n EAA = Emergency Alarm Ack, default = 0.\n RXFreqR = RX reference frequency, 0=Low, 1=Medium, 2=High, default=0 (low).\n AdimtCr = Admit Criterion, 0=Always, 1=Channel Free or 2=Correct CTS/DCS, 3=Colorcode, default=0.\n VOX = VOX Enable, 0=Disable, 1=Enable, default = 0;\n TXFreqR = TX reference frequency: 0=Low, 1=Medium, 2=High, default=0.\n TOffFre = Non-QT/DQT Turn-off Freq., 3=none, 0=259.2Hz, 1=55.2Hz, default=3.\n InCallC = In Call Criteria, 0=Always, 1=Follow Admit Criteria, 2=TX Interrupt, default=0.\n PrK = Privacy key. Also Enables encryption.\n Tx Timeout = Tx Timeout x 15sec, 0-Infinite, 1=15s, etc, 37=555s, default=0.\n Power = Power, 0=low, 1=middle, 2=high, default=high.\n LMS = Leader/MS: Leader or MS, default=1.\n DCD = DCDM switch (inverted), default=1.\n ALI = Allow interrupt (inverted), 0=allow, 1=Disabled, default=1.\n RXG = Receive GSP info (inverted), default=1.\n TXG = Send GSP info (inverted), default=1.\n"
  },
  {
    "path": "doc/code/uv390_menusettings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 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|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | 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        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere:\n RDS = Show radio disable; REN = Show radio enable; RMO = Show remote monitor; RCK = Show radio check; MDL = Show manual dial;\n CED = Show contact edit; CAL = Show call alert; TXT = Show text message; ToA = Show tone or Alert; TlA = Show talkaround;\n CLO = Show call-log outgoing, CLA = Show call-log answered; CLM = Show call-log missed; ESL = Show edit scan list;\n SCN = Show scan menu; VOX = Show VOX settings; SQL = Show squelch settings; LED = Show LED indicator menu;\n KPL = Show keypad lock; INS = Show intro screen; BKL = Show backlight; PWR = Show power settings; RSW = Show record switch;\n GPI = Hide GPS information; GPS = Hide GPS settings; PRG = Hide radio program menu; DSM = Show Display mode settings;\n PWD = Show password settings; NWZ = Show new zone menu; ZNS = Show zone settings; TXM = Show TX mode settings;\n MHT = Show menu hang time; PCM = Show private-call match settings; GCM = Show group-call match settings;\n NSL = Show new scan list menu; EDZ = Show edit zone menu;\n"
  },
  {
    "path": "doc/code/uv390_settings.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Intro Line 1, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Intro Line 2, 10 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n26 | Reserved 24 bytes, set to 0xff                                                                                             ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 |  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 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n44 | DMR ID 24bit, little endian                                                                   | Unused 0x00                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n48 | TX Preamble Duration N x 60ms | Grp Call Hang Time N x 100ms  | Prv. Call Hang Time N x 100ms | VOX sensitivity [1..10]       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n4c | Unused, set 0x00              | Unused, set 0x00              | RX Low Bat. Intv N x 5s       | Call Alert Tone Dur N x 5s    |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 | Lone Worker Resp. Time in min | Lone Worker Rem. Time in sec  | Unused, set to 0x00           | Scan Dig. Hang Time N x 100ms |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n54 | Scan Anal. Hang Time N x 100ms| 0   0   0   0   0   0 |BcLTime| Keypad Lock Time N x 5s       | Channel Mode                  |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n58 | Power-on password 8 x BCD numbers, 0x00000000=default                                                                         |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c | Radio prog. password 8 x BCD numbers, 0xffffffff=disabled                                                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | PC Programming password 8 x ASCII, 0x00 terminated, filled with 0xff=disabled                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n68 | Unused set to 0xffffff                                                                        | Timezone,0=UTC-12 | 1 |PCM|GCM|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n6c | Unused set to 0xffffffff                                                                                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 | Radio name 16 x 16bit unicode chars                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n8c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 | Channel hang time N x 100ms   | Unused, set to 0xff           | 1   1   1   1   1 |PbZ| 1   1 | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n94 | Radio ID 1, binary encoded                                                                    | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n98 | Radio ID 2, binary encoded                                                                    | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n9c | Radio ID 3, binary encoded                                                                    | Unused set to 0x00            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 | 1 |ERI| MicLevel  | 1   1   1 | 15 reserved bytes filled with 0xff                                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nac  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n\nField description:\n TXM = TX Mode, 0=LastCallCH, 1=LastCallCH+HandCH, 2 = DesignedCH, 3 = DesignedCH+HandCH, default=3;\n MNT = Monitor type: 1 = Open Squelch, 0 = Silent, default=1;\n DAL = Disable all LEDs (inverted);\n TPD = Talk permit tone digital;\n TPA = Talk permit tone analog;\n PWE = Password and lock enable (inverted);\n CIT = Ch. free indication tone (inverted);\n DAT = Disable all tones (inverted);\n SMR = Save Mode Receive;\n SPR = Save Preamble;\n KPT = Keypad tones;\n INP = Intro Picture;\n CVN = Channel voice announce;\n MSB = Mode select B (0=VFO, 1=MR);\n MSA = Mode select A (0=VFO, 1=MR);\n BcLTime = Backlight time, 0=Always, t=n*5s;\n PCM = Private call match;\n GCM = Group call match;\n PbZ = Public zone;\n ERI = Edit Radio ID (inverted);\n"
  },
  {
    "path": "doc/code/uv390contact.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID (binary coded)                                                                         |   Contact Type    |RXT| 1   1 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Contact Name 16 x 16bit Unicode                                                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nwhere\nRXT = Receive tone;\n"
  },
  {
    "path": "doc/code/uv390gpssystem.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Revert channel index + 1, 0 = current                         | Repeat intv. N x 30s          | Unused, set to 0xff           |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Destination contact index + 1                                 | 10 x unused bytes set to 0xff                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390message.txt",
    "content": "       0                               8                               16                              24\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n0000 | Message text, 144 x 16 bit unicode text, 0x00 terminated                                                                   ...\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n011c  ...                                                                                                                            |\n     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390rxgrouplist.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Group List Name 16 x 16bit unicode characters                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Member Contact index+1 00                                     |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                            | Member Contact index+1 31                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390scanlist.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Scan List Name 16 x 16bit unicode characters                                                                               ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Priority Channel 1, index+1, 0x0000=current, 0xffff=none      | Priority Channel 1, index+1, 0x0000=current, 0xffff=none      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n24 |Tx Designated Channel index+1, 0x0000=current, 0xffff=none     | !!! Unknown set to 0xf1 !!!   | Hold Time N x 25ms            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n28 | Prio. Sample Time N x 250ms   | Unused, set to 0xff           | Member Channel index+1 00                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n2c | Member Channel index+1 01                                     |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n64  ...                                                            | Member Channel index+1 30                                     |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390timestamp.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Unused, set to 0x00           | Date YYYY-MM-DD hh:mm:ss encoded as 14 BCD numbers                                         ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n08 | CPS Version, VV.VV encoded using table {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?}                                       |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390userdb.txt",
    "content": "        0                               8                               16                              24\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000000 | Number of entries in DB                                                                       | Pad set to 0x00               |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n000004 | 4096 Index entries                                                                                                         ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004000  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n004004 | 122197 Callsign entries                                                                                                    ...\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndfffd8  ...                                                                                                                            |\n       +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390userdbcallsign.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | DMR ID in binary format                                                                       | Unused set to 0xff            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n04 | Callsign 16 x ASCII                                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n14 | Name, City, etc.; 100 x ASCII                                                                                              ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n74  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390userdbentry.txt",
    "content": "     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\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | 12 high-bits of the DMR ID                    | 20-bit index within the callsign DB list                                      |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390zone.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Zone name, 16 x 16bit unicode chars                                                                                        ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n1c  ...                                                                                                                            |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 | Member Channel index+1 00 VFO A                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n3c  ...                                                            | Member Channel index+1 15 VFO A                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/code/uv390zoneext.txt",
    "content": "     0                               8                               16                              24\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | Member Channel index+1 16 VFO A                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n5c  ...                                                            | Member Channel index+1 63 VFO A                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 | Member Channel index+1 00 VFO B                               |                                                            ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ndc  ...                                                            | Member Channel index+1 63 VFO B                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n"
  },
  {
    "path": "doc/dmr-intro/fig/Makefile",
    "content": "FIGS = 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 \\\n       fm_duplex_b.tex fm_echolink_c.tex repeater_local.tex simplex_allcall.tex talkgroup_ex1a.tex timeslot.tex trunk_net_ex3.tex \\\n       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\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_duplex_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DL0LDS};\n  \\user{U2}{6,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$431.9625 MHz$} (R1) ;\n  \\path[->] (R1) edge node[above,rotate=-17] {$439.5625 MHz$} (U2);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_duplex_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DL0LDS};\n  \\activeuser{U2}{6,0}{DL2XYZ};\n  \\path[->] (U2) edge node[above,rotate=-17] {$431.9625 MHz$} (R1) ;\n  \\path[->] (R1) edge node[above,rotate=17] {$439.5625 MHz$} (U1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_echolink_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\repeater{R2}{6,1}{DB0LDS};\n  \\user{U2}{9,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$DTMF: 662699$} node[below,rotate=17]{$431.825 MHz$} (R1) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_echolink_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\activerepeater{R2}{6,1}{DB0LDS};\n  \\user{U2}{9,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$431.825 MHz$} (R1) ;\n  \\path[->,dashed] (R1) edge node[above] {via Echolink} (R2) ;\t\n  \\path[->] (R2) edge node[above,rotate=-17] {$439.150 MHz$} (U2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_echolink_c.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\activerepeater{R2}{6,1}{DB0LDS};\n  \\activeuser{U2}{9,0}{DL2XYZ};\n  \\path[->] (R1) edge node[above,rotate=17] {$439.425 MHz$} (U1) ;\n  \\path[->,dashed] (R2) edge node[above] {via Echolink} (R1) ;\t\n  \\path[->] (U2) edge node[above,rotate=-17] {$431.875 MHz$} (R2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_simplex_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\user{U2}{6,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above] {$144.500 MHz$} (U2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/fm_simplex_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activeuser{U2}{6,0}{DL2XYZ};\n  \\path[->] (U2) edge node[above] {$144.500 MHz$} (U1) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/repeater.tex",
    "content": "\\newcommand{\\repeater}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [black,thick,domain=-45:225] plot ({0.2*cos(\\x)}, {0.5+0.2*sin(\\x)});%\n   \\draw [black,thick,domain=-45:225] plot ({0.4*cos(\\x)}, {0.5+0.4*sin(\\x)});%\n   \\node (xxx) at (0,-.2) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\\newcommand{\\activerepeater}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [red,thick,domain=-45:225] plot ({0.2*cos(\\x)}, {0.5+0.2*sin(\\x)});%\n   \\draw [red,thick,domain=-45:225] plot ({0.4*cos(\\x)}, {0.5+0.4*sin(\\x)});%\n   \\node (xxx) at (0,-.2) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\n\\newcommand{\\user}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,fill=black] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [black,fill=black] (0,.5) circle (.2); %\n   \\node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\\newcommand{\\activeuser}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [red,fill=red] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [red,fill=red] (0,.5) circle (.2); %\n   \\node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n"
  },
  {
    "path": "doc/dmr-intro/fig/repeater_local.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT};\n  \\user{u2}{ 2,0}{DL1XYZ};\t\n  \\user{u3}{ 4,0}{DL2XYZ};\t\n  \\draw[dotted] (5,4) -- (5,-1);\n  \\activeuser{u4}{ 6,0}{DL3XYZ};\n  \\user{u5}{ 8,0}{DL4XYZ};\n  \\user{u6}{10,0}{DL5XYZ};\n  \\activerepeater{R1}{1,3}{DB0ABC};\n  \\repeater{R2}{3,3}{DB0DEF};\n  \\activerepeater{R3}{7,3}{DB0GHI};\n  \\activerepeater{R4}{9,3}{DB0JKL};\n  \\draw[->] (u1) -- node[above,rotate=70]{GC: TG9} (R1);\n  \\draw[->] (R1) -- node[above,rotate=-70]{GC: TG9} (u2);\n  \\draw[->] (u4) -- node[above,rotate=70]{GC: TG8} (R3);\n  \\draw[->] (R3) -- node[above,rotate=-70]{GC: TG8} (u5);\n  \\draw[->] (R4) -- node[above,rotate=-70]{GC: TG8} (u6);\n  \\path[->] (R3) edge[dashed,bend left] node[above]{via Netzwerk} (R4);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/repeater_privatecall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT 2621370};\n  \\activerepeater{R1}{1,3}{DB0ABC};\n  \\draw[dotted] (2,4) -- (2,-1);\n  \\user{u2}{ 4,0}{I/DL2XYZ\\\\2621234};\t\n  \\activerepeater{R2}{3,3}{I0ABC};\n  \\draw[->] (u1) -- node[above,rotate=70]{PC: 2621234} (R1);\n  \\draw[->] (R2) -- node[above,rotate=-70]{PC: 2621234} (u2);\n  \\path[->] (R1) edge[dashed,bend left] node[above]{via Netzwerk} (R2);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/simplex_allcall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 6,1}{DL1XYZ, TG99};\n   \\user{u3}{ 6,0}{DL2XYZ, TG99};\n   \\user{u4}{ 6,-1}{DL3XYZ};\n   \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{All Call} (u2);\n   \\path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{All Call} (u3);\n   \\path[->] (u1) edge[bend right] node[above, rotate=-10]{$433.450 MHz$} node[below, rotate=-10]{All Call} (u4);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/simplex_groupcall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 6,1}{DL1XYZ, TG99};\n   \\user{u3}{ 6,0}{DL2XYZ, TG99};\n   \\user{u4}{ 6,-1}{DL3XYZ};\n   \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{GC: TG99} (u2);\n   \\path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{GC: TG99} (u3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/simplex_privatecall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT};\n  \\activeuser{u2}{ 6,1}{DL1XYZ, TG99};\n  \\user{u3}{ 6,0}{DL2XYZ, TG99};\n  \\user{u4}{ 6,-1}{DL3XYZ};\n  \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DL1XYZ} (u2);\n  \\path[->] (u2) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DM3MAT} (u1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/talkgroup_ex1a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\repeater{R3}{9,3}{I0ABC};\n   \\path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/talkgroup_ex1b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\user{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\activeuser{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\activerepeater{R3}{9,3}{I0ABC, (TG2621)};\n   \\path[->] (u4) edge node[above,rotate=-70]{GC: TG2621} (R3);\n   \\path[->] (R1) edge node[above,rotate=70]{GC: TG2621} (u1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R3) edge[bend right] node[below]{GC: TG2621} (R2);\n   \\path[->] (R3) edge[bend right] node[above]{GC: TG2621} (R1);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/talkgroup_ex1c.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\activerepeater{R3}{9,3}{I0ABC, (TG2621)};\n   \\path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R3) edge node[above,rotate=-70]{GC: TG2621} (u4);\n   \\path[->] (R1) edge[bend left] node[below]{GC: TG2621} (R2);\n   \\path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/timeslot.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usepackage{pgfplots}\n\\usetikzlibrary{shapes.geometric}\n\\usetikzlibrary{decorations, decorations.text}\n%\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}\n  \\draw[|-,dotted, semithick] (-1,-0.2) -- (0,-0.2);\n  \\draw[|-,semithick] (0,-0.2) -- (1,-0.2);\n  \\draw[|-,semithick] (1,-0.2) -- (2,-0.2);\n  \\draw[|-,semithick] (2,-0.2) -- (3,-0.2);\n  \\draw[|-,semithick] (3,-0.2) -- (4,-0.2);\n  \\draw[|-,semithick] (4,-0.2) -- (5,-0.2);\n  \\draw[|-,semithick] (5,-0.2) -- (6,-0.2);\n  \\draw[|->,dotted,semithick] (6,-0.2) -- (7,-0.2);\n  \\node at (7, -.5) {$t$};\n  \\draw [thick,decoration={brace,mirror},decorate] (0,-0.4) -- (1,-0.4) node [pos=0.5, anchor=north,yshift=-0.55] {$30\\ ms$}; \n  \\fill[red!30] (0.1,0) -- (0.1,1) -- (0.9,1) -- (0.9,0) -- cycle;\n  \\node at (0.5,0.5) {TS 1};\n  \\fill[blue!30] (1.1,0) -- (1.1,1) -- (1.9,1) -- (1.9,0) -- cycle;\n  \\node at (1.5,0.5) {TS 2};\n  \\fill[red!30] (2.1,0) -- (2.1,1) -- (2.9,1) -- (2.9,0) -- cycle;\n  \\node at (2.5,0.5) {TS 1};\n  \\fill[blue!30] (3.1,0) -- (3.1,1) -- (3.9,1) -- (3.9,0) -- cycle;\n  \\node at (3.5,0.5) {TS 2};\n  \\fill[red!30] (4.1,0) -- (4.1,1) -- (4.9,1) -- (4.9,0) -- cycle;\n  \\node at (4.5,0.5) {TS 1};\n  \\fill[blue!30] (5.1,0) -- (5.1,1) -- (5.9,1) -- (5.9,0) -- cycle;\n  \\node at (5.5,0.5) {TS 1};  \n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/trunk_net_ex1.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{r1}{ 0,0}{Reinigung 1};\n  \\user{r2}{ 2,0}{Reinigung 2};\t\n  \\draw[dotted] (3,4) -- (3,-1);\n  \\user{s1}{ 4,0}{Sicherheit 1};\n  \\user{z} { 6,0}{Zentrale};\n  \\draw[dotted] (7,4) -- (7,-1);\n  \\user{s2}{ 8,0}{Sicherheit 2};\n  \\user{r3}{10,0}{Reinigung 3};\n  \\repeater{R1}{1,3}{Terminal 1, TG: R,S};\n  \\repeater{R2}{5,3}{Terminal 2, TG: R,S};\n  \\repeater{R3}{9,3}{Vorfeld, TG: S};\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/trunk_net_ex2.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{r1}{ 0,0}{Reinigung 1};\n  \\user{r2}{ 2,0}{Reinigung 2};\t\n  \\draw[dotted] (3,4) -- (3,-1);\n  \\activeuser{s1}{ 4,0}{Sicherheit 1};\n  \\activeuser{z} { 6,0}{Zentrale};\n  \\draw[dotted] (7,4) -- (7,-1);\n  \\user{s2}{ 8,0}{Sicherheit 2};\n  \\activeuser{r3}{10,0}{Reinigung 3};\n  \\activerepeater{R1}{1,3.5}{Terminal 1, TG: R,S};\n  \\activerepeater{R2}{5,3.5}{Terminal 2, TG: R,S};\n  \\activerepeater{R3}{9,3.5}{Vorfeld, TG: S};\n  \\draw[->] (r1) -- node[above,rotate=74] {PC: Reinigung 3} (R1);\n  \\path[->,dashed] (R1) edge [bend left] node[above] {via Netzwerk} (R3);\n  \\draw[->] (R3) -- node[above,rotate=-74] {PC: Reinigung 3} (r3);\n  \\draw[->] (z) -- node[above,rotate=-74] {PC: Sicherheit 1} (R2);\n  \\draw[->] (R2) -- node[above,rotate=74] {PC: Sicherheit 1} (s1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/trunk_net_ex3.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Reinigung 1};\n   \\activeuser{r2}{ 2,0}{Reinigung 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Sicherheit 1};\n   \\activeuser{z} { 6,0}{Zentrale};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Sicherheit 2};\n   \\user{r3}{10,0}{Reinigung 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: R,S};\n   \\activerepeater{R2}{5,3}{Terminal 2, TG: R,S};\n   \\repeater{R3}{9,3}{Vorfeld, TG: S};\n   \\draw[->] (z) -- node[above,rotate=-74] {TG: R} (R2);\n   \\path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/trunk_net_ex4a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Reinigung 1};\n   \\activeuser{r2}{ 2,0}{Reinigung 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Sicherheit 1};\n   \\user{z} { 6,0}{Zentrale};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Sicherheit 2};\n   \\user{r3}{10,0}{Reinigung 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: R,S};\n   \\repeater{R2}{5,3}{Terminal 2, TG: R,S};\n   \\activerepeater{R3}{9,3}{Vorfeld, TG: S,(R)};\n   \\draw[->] (r3) -- node[above,rotate=-74] {TG: R} (R3);\n   \\path[->,dashed] (R3) edge [bend right] node[above] {via Netzwerk} (R1);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/fig/trunk_net_ex4b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Reinigung 1};\n   \\activeuser{r2}{ 2,0}{Reinigung 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Sicherheit 1};\n   \\activeuser{z} { 6,0}{Zentrale};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Sicherheit 2};\n   \\activeuser{r3}{10,0}{Reinigung 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: R,S};\n   \\activerepeater{R2}{5,3}{Terminal 2, TG: R,S};\n   \\activerepeater{R3}{9,3}{Vorfeld, TG: S,(R)};\n   \\draw[->] (z) -- node[above,rotate=-74] {TG: R} (R2);\n   \\path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1);\n   \\path[->,dashed] (R2) edge [bend left] node[above] {via Netzwerk} (R3);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: R} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: R} (r2);\n   \\draw[->] (R3) -- node[above,rotate=-74] {TG: R} (r3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/dmr-intro/script/Makefile",
    "content": "\nclean:\n\trm *.aux *.idx *.ilg *.ind *.log *.out *.pdf *.synctex.gz *.toc\n"
  },
  {
    "path": "doc/dmr-intro/script/script_de.tex",
    "content": "\\documentclass[11pt, a4paper,parskip=half]{scrartcl}\n\\usepackage{standalone}\n\\usepackage[utf8]{inputenc}\n\\usepackage{geometry}\n\\usepackage{graphics}\n\\usepackage{subcaption}\n\\usepackage{eurosym}\n\\usepackage[ngerman]{babel}\n\\usepackage{rotating}\n\\usepackage{makeidx}\n\\usepackage[tikz]{bclogo}\n\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric,patterns,snakes}\n\n\\usepackage{hyperref}\n\\hypersetup{colorlinks=true, linkcolor=blue, filecolor=blue, urlcolor=blue}\n\\urlstyle{same}\n\n\n\\title{DMR -- Digital Mobile Radio}\n\\subtitle{Ein Mobilfunknetz für Funkamateure}\n\\author{Hannes Matuschek, DM3MAT,\\\\\\texttt{dm3mat [at] darc [dot] de}}\n\\date{\\today}\n\n\\input{../fig/repeater}\n\n\\newenvironment{merke}{\\begin{bclogo}[couleur=blue!30,arrondi=.1,logo=\\bccrayon,ombre=true]{Merke}}{\\end{bclogo}}   \n\\newenvironment{hinweis}{\\begin{bclogo}[couleur=blue!30,arrondi=.1,logo=\\bcinfo,ombre=true]{Hinweis}}{\\end{bclogo}}   \n\\newenvironment{achtung}{\\begin{bclogo}[couleur=red!30,arrondi=.1,logo=\\bcattention,ombre=true]{Achtung}}{\\end{bclogo}}   \n\\newcommand{\\adef}[1]{\\emph{#1}\\index{#1}}\n\\newcommand{\\aref}[1]{#1\\index{#1}}\n\\newcommand{\\altdef}[2]{\\emph{#1}\\index{#1|seealso{#2}}}\n\n\\makeindex\n\n\\begin{document}\n\n\\begin{titlepage}\n\\maketitle\n\\vfill\n\\begin{abstract}\n Dieses Script soll eine Einführung in DMR (digital mobile radio) für den \n unbedarften Funkamateur oder jeden Interessierten sein. Ich versuche dem \n Leser Details solange zu verheimlichen, bis es absolut notwendig wird \n diese zu erklären. Die meisten\n Einführungen in DMR, die ich bisher gesehen habe, sind eher eine lange Liste\n von Begriffserklärungen, die ohne Erfahrung mit DMR schwer zu verstehen sind. \n  \n Viel der empfundenen Komplexität von DMR, rührt aus dem Ursprung dieser \n Technik. DMR wurde für den kommerziellen Funk auf Großveranstaltungen oder \n großen industriellen Anlagen entwickelt, auch \n \\href{https://de.wikipedia.org/wiki/B\\%C3\\%BCndelfunk}{Bündelfunk} genannt. \n Ich werde daher mit einem Beispiel erläutern wofür DMR entwickelt wurde und fange erst dann\n an zu erklären, wie DMR für den Amateurfunk eingesetzt wird.\n\\end{abstract}\n\\end{titlepage}\n\\pagebreak\n\n\\tableofcontents\n\\pagebreak\n\n\\input{script_de_01_vorwissen}\n\n\\input{script_de_02_ursprung}\n\n\\input{script_de_03_simplex}\n\n\\input{script_de_05_privatecall}\n\n\\input{script_de_04_lokal}\t\n\n\\input{script_de_07_talkgroup}\n\n\\input{script_de_06_textmsg}\n\n\\input{script_de_11_technik}\n\n\\input{script_de_09_roaming}\n\n\\input{script_de_12_codeplug}\n\n\\input{script_de_10_netze}\n\n\\appendix\n\\printindex\n\n\\end{document}"
  },
  {
    "path": "doc/dmr-intro/script/script_de_01_vorwissen.tex",
    "content": "\\section{Vorwissen: Relaisbetrieb} \\label{sec:vorwissen} \\index{Relaisbetrieb}\nIn 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. \n\nWenn Sie sich aber für Amateurfunk interessieren oder selbst noch keine Erfahrung mit dem Relaisbetrieb haben, empfehle ich Ihnen diesen Abschnitt zu lesen. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/fm_simplex_a}\n \\input{../fig/fm_simplex_b}\n \\caption{Einfacher Simplexbetrieb, DM3MAT sendet auf der Frequenz $144.500 MHz$ direkt zu DL2XYZ. Dieser antwortet dann auf der selben Frequenz.} \\label{fig:basicsimlpex}\n\\end{figure}\n\nDie 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. \n\nAuf 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. \n\n\\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. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/fm_duplex_a}\n \\input{../fig/fm_duplex_b}\n \\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}\n\\end{figure}\n\nFü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. \n\n\\subsection{Echolink} \\label{sec:echolink} \\index{Echolink}\nWenn 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}. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/fm_echolink_a}\n \\input{../fig/fm_echolink_b}\n \\input{../fig/fm_echolink_c}\n \\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}\n\\end{figure}\n\nDiese 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. \n\nEs 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. \n\n\\begin{merke}\nSobald zwei Repeater per Echolink miteinander verbunden sind, verhalten sich beide wie ein einziger Repeater. \n\\end{merke}\n\nEs 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."
  },
  {
    "path": "doc/dmr-intro/script/script_de_02_ursprung.tex",
    "content": "\\section{DMR Einführung \\& Ursprung} \\label{sec:ursprung}\nDMR 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.\n\nDMR 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. \n\nAuf so einem Flughafen arbeiten sehr viele Leute mit sehr unterschiedlichen Aufgaben. Da hätten wir (ohne Anspruch auf Vollständigkeit)\n\\begin{itemize}\n \\item Die Reinigungskolonne,\n \\item die Sicherheitsleute wie Gepäckkontrolle oder Wachschutz,\n \\item das Vorfeld, also die Betankung, die Gepäckverladung \\& das Catering, \n \\item die Betriebsfeuerwehr und\n \\item die Zentrale.\n\\end{itemize}\n\nAll diese Mitarbeiter bekommen ein Funkgerät und sollen die folgenden Möglichkeiten haben:\n\\begin{itemize}\n \\item Direkte Kommunikation zur Zentrale, alle Personen sollen die Zentrale erreichen können.\n \\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.\n \\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. \n\\end{itemize}\n \nGleichzeitig 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. \n\nVergleicht 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. \n\nBesser 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. \n\nUm 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.\n\n\\begin{merke}\n DMR hat mehr Ähnlichkeit mit einem Telefonnetz mit zusätzlichen Gruppenruf als mit klassischem FM-Repeaterbetrieb.\n\\end{merke} \n\nDas 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.\n\nAuß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. \n\n\\begin{merke}\n 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.\n\\end{merke}\n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/trunk_net_ex1}\n \\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}\n\\end{figure}\n\nIn 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. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/trunk_net_ex2}\n \\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}\n\\end{figure}\n\nIn 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. \n\n\\begin{merke}\nDas 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.\n\\end{merke}\n\nSolange 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. \n\nIm 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. \n\nDies 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. \n\n\\begin{figure}[p]\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/trunk_net_ex3}\n  \\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}\n \\end{subfigure}\\vspace{0.5cm}\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/trunk_net_ex4a}\n  \\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} \n \\end{subfigure}\\vspace{.5cm}\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/trunk_net_ex4b}\n  \\caption{Nach der temporären Abonnierung, ist nun der Teilnehmer \\emph{Reinigung 3} auch auf dem Vorfeld erreichbar.} \\label{fig:exnet4b}\n \\end{subfigure}\n \\caption{Temporäres Abonnement einer Sprechgruppe auf einem Repeater.} \\label{fig:exnet4}\n\\end{figure}\n\nDamit 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. \n\nDieses 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.}.\n\nMit 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.\n\n"
  },
  {
    "path": "doc/dmr-intro/script/script_de_03_simplex.tex",
    "content": "\\section{DMR Simplex Betrieb} \\label{sec:simplex}\nDie 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. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/simplex_privatecall}\n \\caption{Beispiel eines DMR Simplex Direktrufs von DM3MAT an DL1XYZ.} \\label{fig:splxpc}\n\\end{figure}\n\nIn 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. \n\nAn 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.\n\n\\begin{figure}[!ht]\n  \\centering\n  \\input{../fig/simplex_groupcall}\n  \\caption{Beispiel eines DMR Simplex Gruppenrufs von DM3MAT an die Sprechgruppe TG99.} \\label{fig:splxgc}\n\\end{figure}\n\nUm 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}). \n\nIn 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.\n\n\\begin{figure}[!ht]\n  \\centering\n  \\input{../fig/simplex_allcall}\n  \\caption{Beispiel eines DMR All Calls von DM3MAT alle die ihn hören können.} \\label{fig:splxac}\n\\end{figure}\n\nUm 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. \n\n\\begin{merke}\n 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. \n\\end{merke}\n\n\\subsection{DMR Simplex Frequenzen}\n\\begin{table}[!ht]\n \\centering\n \\begin{tabular}{|l|c||l|c|} \\hline\n  Name & Frequenz & Name & Frequenz \\\\ \\hline \\hline\n  S0 (Anruf) & $433.4500 MHz$ & S4 & $433.6500 MHz$ \\\\\n  S1         & $433.6125 MHz$ & S5 & $433.6625 MHz$ \\\\\n  S2         & $433.6250 MHz$ & S6 & $433.6750 MHz$ \\\\\n  S3         & $433.6375 MHz$ & S7 & $433.6875 MHz$ \\\\ \\hline\n \\end{tabular}\n \\caption{Liste der acht üblichen DMR Simplexkanäle. Der Kanal \\emph{S0} ist der Anrufkanal.} \\label{tab:simplex}\n\\end{table}\n\nIm 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_04_lokal.tex",
    "content": "\\section{Lokaler Repeater Betrieb} \\label{sec:lokal}\nEigentlich 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.\n \n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/repeater_local}\n \\caption{Beispiel mit zwei Regionen (links \\& rechts) mit je zwei Repeatern.} \\label{fig:tg9tg8}\n\\end{figure}\n\nDie 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.    \n\nDie 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_05_privatecall.tex",
    "content": "\\section{Direkte Anrufe} \\label{sec:privatecall}\nDirektrufe (\\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. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/repeater_privatecall}\n \\caption{Beispiel eines Direktrufs über Ländergrenzen hinweg.} \\label{fig:pc}\n\\end{figure}\n\nDas 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. \n\nEbenso 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.\n\nIn 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_06_textmsg.tex",
    "content": "\\section{Datendienste} \\label{sec:data}\nDa DMR von sich aus schon eine digitale Betriebsart ist, bei der meist Sprache in digitalisierter \nForm übertragen wird, ist es natürlich auch möglich reine Datendienste über DMR anzubieten. Zum \neinen gibt es einen Textnachrichtendienst, der dem SMS-Dienst der Mobiltelefone nachempfunden ist. \nZum anderen gibt es auch die Möglichkeit, die eigene Position per DMR an das APRS\\footnote{APRS \nsteht für \\emph{Automatic Packet Reporting System} und ermöglicht das Übertragen von kleinen \nDatensätzen über Packet-Radio wie zum Beispiel die Position, Wetter oder Textnachrichten. Mehr \ndazu erfahren sie in der \\href{https://de.wikipedia.org/wiki/Automatic_Packet_Reporting_System}{Wikipedia}.} \nNetz zu übertragen.\n \n\\subsection{Textnachrichten (SMS)} \\label{sec:textmsg}\nMit 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. \n\nEs 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:\n\\begin{enumerate}\n \\item 262993 -- GPS und Wetter\n \\begin{itemize}\n  \\item Wenn Sie \\texttt{help} senden, erhalten daraufhin eine Auflistung aller Kommandos.\n  \\item Wenn Sie \\texttt{wx} senden, erhalten Sie das aktuelle Wetter am Standort des Repeaters, den Sie verwenden.\n  \\item Wenn Sie \\texttt{wx STADTNAME} senden, erhalten Sie das aktuelle Wetter für die angegebene Stadt.\n  \\item Wenn Sie \\texttt{gps} senden, erhalten Sie die letzte Positionsinformation, die Sie zuletzt an das DMR Netz gesendet hatten.\n  \\item Mit \\texttt{gps CALL} können Sie auch die letzte Position des angegebenen Teilnehmers abfragen.\n  \\item Mit \\texttt{rssi} erhalten Sie vom Repeater einen Signalrapport.\n \\end{itemize} \n \\item 262994 -- Repeater Informationen \\& Pagernachrichten\n  \\begin{itemize}\n   \\item Wenn Sie \\texttt{rpt} senden, erhalten Sie eine Liste der statisch und dynamisch abonnierten Sprechgruppen des Repeaters.  \n   \\item Wenn Sie \\texttt{CALL NACHRICHT}, wird die angegebene Nachricht an das angegebene Call per Pager (DAPNET) geschickt.\n  \\end{itemize}\n\\end{enumerate}\n \n\\subsection{Positionsübermittlung (APRS via DMR)} \\label{sec:aprs}\nWie 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. \n\nNeben 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_07_talkgroup.tex",
    "content": "\\section{Talkgroup Betrieb} \\label{sec:talkgroup}\nEin 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}). \n\n\\begin{figure}[p]\n \\centering\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/talkgroup_ex1a}\n  \\caption{Beispiel für eine typische Nachmittagsrunde auf einer Sprechgruppe.} \\label{fig:tgex1}\n \\end{subfigure}\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/talkgroup_ex1b}\n  \\caption{Der OM im Ausland abonniert diese Sprechgruppe an dem lokalen Repeater temporär durch einen Gruppenruf zu dieser Sprechgruppe.} \\label{fig:tgex2}\n \\end{subfigure}\n \\begin{subfigure}{\\linewidth}\n  \\centering\n  \\input{../fig/talkgroup_ex1c}\n  \\caption{Danach kann auch der OM im Urlaub wie gewohnt an dieser Nachmittagsrunde teilnehmen.} \\label{fig:tgex3}\n \\end{subfigure}\n\\end{figure}\n\nFü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}). \n\nNachdem 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.\n\n\\begin{table}[!ht]\n \\centering\n \\begin{tabular}{|l|c|} \\hline\n  Name & Sprechgruppe \\\\ \\hline\n  Global & 91 \\\\\n  Europa & 92 \\\\\n  Deutschland & 262 \\\\\n  Mecklenburg-Vorpommern \\& Sachsen-Anhalt & 2620 \\\\\n  Berlin \\& Brandenburg & 2621 \\\\\n  Hamburg \\& Schleswig-Holstein & 2622 \\\\\n  Niedersachsen \\& Bremen & 2623 \\\\\n  Nordrhein-Westfalen & 2624 \\\\\n  Rheinland-Pfalz \\& Saarland & 2625 \\\\\n  Hessen & 2626 \\\\\n  Baden-Württemberg & 2627 \\\\\n  Bayern & 2628 \\\\\n  Sachsen \\& Thüringen & 2629 \\\\ \\hline\n \\end{tabular}\n \\caption{} \\label{tab:talkgroups}\n\\end{table}\n\n\\subsection{Cluster}\nIm 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. \n\nWü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.\n\nAus 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.\n"
  },
  {
    "path": "doc/dmr-intro/script/script_de_08_aprs.tex",
    "content": ""
  },
  {
    "path": "doc/dmr-intro/script/script_de_09_roaming.tex",
    "content": "\\section{Roaming} \\label{sec:roaming}\nViele 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. \n\nEs 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. \n\nUm 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. \n\nWenn 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. \n\nHat 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. \n\nDieses 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_10_netze.tex",
    "content": "\\section{DMR-Netze} \\label{sec:netze}\nSie 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. \n\nLediglich 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. \n\n\\subsection{Reflektoren} \\label{sec:reflector} \\index{Reflektor}\nIm DMR+ Netz spielen sogenannte Reflektoren eine zentrale Rolle. Sie entsprechen in etwa den Sprechgruppen, wie sie im Brandmeisternetz verwendet werden. \n\nDer 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.\n\nDies 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. "
  },
  {
    "path": "doc/dmr-intro/script/script_de_11_technik.tex",
    "content": "\\section{Technischer Hintergrund} \\label{sec:technik}\nNachdem 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}.\n\n\\subsection{Zeitschlitze (Time Slots)} \\label{sec:timeslot}\nWie 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. \n\n\\begin{figure}[!ht]\n \\centering\n \\input{../fig/timeslot}\n \\caption{Graphische Darstellung der \\emph{time-division media access} (TDMA) Technik.}\n\\end{figure}\n\nDas 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.\n\nWas 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.\n\nWas auf welchem Zeitschlitz passieren soll, hängt stark von der Konfiguration des einzelnen Repeaters ab. Grundsätzlich gilt aber:\n\\begin{merke}\n Überregionale Kommunikation sollte auf Zeitschlitz 1 und lokale sowie regionale Kommunikation auf dem Zeitschlitz 2 stattfinden.\n\\end{merke} \n\n\\subsection{Farbcodes (Color Codes)} \\label{sec:colorcode} \\index{Color Code}\nFarbcodes 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. \n\n\\begin{merke}\n Um einen Repeater nutzen zu können muss nicht nur dessen Ein- und Ausgabefrequenz sondern auch dessen Farbcode bekannt sein!\n\\end{merke}"
  },
  {
    "path": "doc/dmr-intro/script/script_de_12_codeplug.tex",
    "content": "\\section{Codeplug Programmierung} \\label{sec:codeplug}\nNachdem 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}. \n\nDoch bevor Sie loslegen können benötigen Sie wie alle DMR Teilnehmer eine eindeutige Nummer, die DMR ID.\n\\begin{hinweis}\n 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.\n\\end{hinweis}\nIhre DMR ID erhalten Sie in der Regel innerhalb von 24 Stunden per Mail. Sobald Sie eine DMR ID erhalten haben kann es los gehen.\n\nDa 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. \n\n\\begin{achtung}\n 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.}. \n\\end{achtung}\n\nDer 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.\n\nWenn 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.\n\nDie Konfiguration Ihres Funkgerätes erfolgt in 5-6 Schritten:\n\\begin{enumerate}\n \\item Allgemeine Einstellungen,\n \\item Kontakte anlegen,\n \\item Empfangsgruppen festlegen,\n \\item alle Kanäle anlegen,\n \\item Kanäle in Zonen einteilen und\n \\item optional Scanlisten anlegen.\n\\end{enumerate}\n\nIn den folgenden Abschnitten möchte ich die einzelnen Konfigurationsschritte im Detail beschreiben.\n\n\\subsection{Allgemeine Konfiguration} \\label{sec:cp:basic}\nDie 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! \n\nIhr Rufzeichen tragen Sie bitte in das Feld \\emph{Radio Name} ebenfalls in der Rubrik \\emph{Radio Settings} ein.  \n\n\\subsection{Kontakte Anlegen} \\label{sec:cp:contact}\nNachdem 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.\n\n\\begin{table}[!ht]\n \\centering\n \\begin{tabular}{|l|c|c||l|c|c|}\\hline\n  Name        & Typ        & Nummer & Name & Typ & Nummer \\\\ \\hline\n  Lokal       & Gruppenruf & 9        & Ham/SlHo    & Gruppenruf & 2622 \\\\\n  Regional    & Gruppenruf & 8        & NiSa/Bre    & Gruppenruf & 2623 \\\\\n  TG99        & Gruppenruf & 99       & NRW         & Gruppenruf & 2624 \\\\\n  Rundumruf   & All Call   & 16777215 & RhPf/Saar   & Gruppenruf & 2625 \\\\\n  Weltweit    & Gruppenruf & 91       & Hessen      & Gruppenruf & 2626 \\\\\n  Europa      & Gruppenruf & 92       & BaWü        & Gruppenruf & 2627 \\\\\n  D-A-CH      & Gruppenruf & 920      & Bay         & Gruppenruf & 2628 \\\\\n  Deutschland & Gruppenruf & 262      & Sa/Th       & Gruppenruf & 2629 \\\\\n  Österreich  & Gruppenruf & 232      & Echo Test   & Direktruf  & 262997 \\\\\n  Schweiz     & Gruppenruf & 228      & SMS Serv.   & Direktruf  & 262993 \\\\\n  EMCOM\\footnote{Ausschließlich für Notfunk.} EU    & Gruppenruf & 9112 & \n  DAPNET      & Direktruf  & 262994 \\\\\n  EMCOM WW    & Gruppenruf & 9911     & APRS GW     & Direktruf  & 262999 \\\\\n  MeVo/SaAn   & Gruppenruf & 2620     & DM3MAT      & Direktruf  & 2621370 \\\\\n  Ber/Bra     & Gruppenruf & 2621     & ...         & ...        & ... \\\\ \\hline\n \\end{tabular}\n \\caption{Beispielkontakte für Deutschland.} \\label{tab:contacts}\n\\end{table}\n\nNatü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}.\n\n\n\\subsection{Empfangsgruppen Zusammenstellen} \\label{sec:cp:grouplist}\nIm 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.\n\nDie 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. \n\nFü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. \n\nZuletzt 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}).\n  \n\\begin{table}\n \\centering\n \\begin{tabular}{|l|l|} \\hline\n Name     & Gruppenrufe \\\\ \\hline\n Simplex  & Lokal, Regional, TG99 \\\\\n WW/EU/DL & Weltweit, Europa, D-A-CH, Deutschland, EMCOM EU \\\\\n Ber/Bra  & Lokal, Regional, Ber/Bra \\\\\n Sa/Th    & Lokal, Regional, Sa/Th \\\\ \\hline\n \\end{tabular}\n \\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}\n\\end{table}\n\n\n\\subsection{Kanäle Anlegen} \\label{sec:cp:channel}\nBevor 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}.\n\nWenn 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.\n\n\\subsubsection{Simplexkanäle Anlegen}\n\\begin{table}[!ht]\n \\begin{tabular}{|l|p{2.5cm}|p{2.5cm}|c|c|c|c|} \\hline\n 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\n DMR S0     & $433.4500 MHz$     & $433.4500 MHz$     & 1           & 1        & Rundumruf  & Simplex \\\\\n DMR S1     & $433.6125 MHz$     & $433.6125 MHz$     & 1           & 1        & Rundumruf  & Simplex \\\\\n ...        & ...                & ...                & ...         & ...      & ...        & ... \\\\ \\hline\n \\end{tabular}\n \\caption{Beispieltabelle für die DMR Simplexkanäle.} \\label{tab:ch:simplex}\n\\end{table}\n\nIn 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. \n\nDie 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. \n\nIm \\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.\n\nDer 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. \n\nDie 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.  \n\nDie 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. \n\nIn 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. \n\nDie 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.  \n\n\\subsubsection{Repeaterkanäle Anlegen}\nDas 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.\n\n\\begin{sidewaystable}[p]\n \\centering\n \\begin{tabular}{|l|c|c|c|c|c|c|} \\hline\n  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\n  DB0LDS TS1       & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & ---         & WW/EU/DL \\\\\n  DB0LDS DL TS1    & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & Deutschland & WW/EU/DL \\\\\n  DB0LDS Sa/Th TS1 & $439.5625 MHz$ & $431.9625 MHz$ & 1 & 1 & Sa/Th       & Sa/Th \\\\\n  DB0LDS TG9 TS2    & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & L9          & Ber/Bra \\\\\n  DB0LDS TG8 TS2    & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & L8          & Ber/Bra \\\\\n  DB0LDS BB TS2    & $439.5625 MHz$ & $431.9625 MHz$ & 2 & 1 & Ber/Bra     & Ber/Bra \\\\ \\hline\n \\end{tabular}\n \\caption{Beispielkonfiguration der Kanäle für den Repeater DB0LDS in Wildau bei Berlin.} \\label{tab:ch:repeater}\n\\end{sidewaystable}\n\nIch 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.\n\nAm 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. \n\nDer 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. \n\nDer 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}).\n\n\\begin{merke}\n Auf jedem Kanal kann auf einen eingehenden Ruf innerhalb der sogenannten \\aref{Hangtime} geantwortet werden, egal welcher Standardkontakt für diesen Kanal festgelegt wurde. \n\\end{merke} \n\nÄ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.\n\nKanä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.\n\n\\begin{merke}\n 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.\n\\end{merke}\n\nDas 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.\n\n\\subsubsection{Weitere Kanaloptionen}\nDie 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.\n\nDie \\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.\n\nDie 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.\n\nDie 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.\n\nDie Option \\adef{Privacy Group} legt die Verschlüsselung der Aussendungen für diesen Kanal fest. Diese Funktion darf im Amateurfunk gar nicht verwendet werden.\n\nDie 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.\n\nDie 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.\n\nWenn die Option \\adef{RX Only} angewählt ist, können Sie auf diesen Kanal nicht senden.\n\nDie 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.\n\nDie 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. \n\nDiese 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.\n\n\n\\subsection{Zonen Zusammenstellen} \\label{sec:zone} \\index{Zone}\nWenn 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.. \n\nSie 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.\n\n\\begin{hinweis}\n 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.\n\\end{hinweis}\n\n\\subsection{Scanlisten Zusammenstellen}\nScanlisten 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.\n\n"
  },
  {
    "path": "doc/dmr-intro/talk-barcamp2021/slides.tex",
    "content": "\\documentclass[aspectratio=169]{beamer}\n\\usetheme{Boadilla}\n\n\\usepackage{hyperref}\n\\usepackage{graphicx}\n\\usepackage{subcaption}\n\\usepackage{standalone}\n\\usepackage[ngerman]{babel}\n\\usepackage{tikz}\n\\usepackage{listings}\n\n\\title[QDMR]{QDMR ein universeller Code-Plug Editor (CPS) für Linux \\& Mac\\\\ (und vielleicht Windows)}\n\\subtitle{}\n\n\\author{Hannes, DM3MAT}\n\\institute{\\texttt{dm3mat [at] darc [dot] de}}\n\\date{25. März 2021}\n\n\\lstset{ %\nbasicstyle=\\tiny    % the size of the fonts that are used for the line-numbers\n}\n\n\n\\input{../fig/repeater}\n\n\\begin{document}\n\\begin{frame}\n \\titlepage\n\\end{frame}\n\n\\begin{frame} \\frametitle{Übersicht}\n \\tableofcontents\n\\end{frame}\n\n\n\\section{Ganz kurze Einführung in DMR}\n\\begin{frame} \\frametitle{Einführung -- DMR ein Mobilfunknetz für den Amateurfunk} \n\\begin{block}{Was ist DMR?}\n \\begin{itemize}\n  \\item Ein digitaler Daten und Sprachübertragungsmodi für UKW.\n  \\item Gedacht als Ersatz für den analogen Bündelfunk.\n  \\item Endpunkte werde über \\emph{Telefonnummern} identifiziert.\n  \\item Verwendet 12.5kHz Raster (wie NBFM). \n  \\item Erlaubt vernetzten Repeater betrieb (über sog. Sprechgruppen).\n  \\item Erlaubt durch Kompression das \\emph{Bedienen} zweier Endgeräte auf einem physischen Kanal (zwei unabhängige \\emph{Zeitschlitze}).\n \\end{itemize}\n\\end{block}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Repeaterbetrieb und Direktrufe}\n \\begin{block}{Merke}\n  Alle DMR Repeater sind untereinander vernetzt und vermitteln Gespräche.\n \\end{block}\n \\begin{center}\n   \\input{../fig/repeater_privatecall}\n \\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Repeaterbetrieb und Sprechgruppen}\n \\begin{block}{Sprechgruppe}\n  Sprechgruppen sind reservierte Telefonnummern für Telefonkonferenzen: Ein \\emph{Gruppenruf} zu einer Sprechgruppe wird von allen Repeatern ausgesandt, die diese Sprechgruppe abonniert haben.\n  \n  Es gibt Sprechgruppen für Regionen aber auch für bestimmte Themen.\n \\end{block}\n \\begin{center}\n  \\begin{tabular}{|l|c|} \\hline\n  Name & Sprechgruppe \\\\ \\hline\n  Lokal & 9 \\\\\n  Global & 91 \\\\\n  Europa & 92 \\\\\n  Deutschland & 262 \\\\\n  Berlin \\& Brandenburg & 2621 \\\\\n  ... & ... \\\\\n  Whitesticker & 264022\\\\ \\hline\n \\end{tabular}\n \\end{center}\n\\end{frame}\n\n\\begin{frame}\\frametitle{Lokale und regionale Verbindungen}\n \\begin{center}\n  \\input{../fig/repeater_local}\n \\end{center} \n\\end{frame}\n\n\\begin{frame}\\frametitle{Nachmittagsrundenbeispiel}\n \\begin{center}\n  \\input{../fig/talkgroup_ex1a}\n \\end{center}\n\\end{frame}\n\n\\begin{frame}\\frametitle{Nachmittagsrundenbeispiel}\n \\begin{center}\n  \\input{../fig/talkgroup_ex1b}\n \\end{center}\n\\end{frame}\n\n\\begin{frame}\\frametitle{Nachmittagsrundenbeispiel}\n \\begin{center}\n  \\input{../fig/talkgroup_ex1c}\n \\end{center}\n\\end{frame}\n\n\n\\section{Code Plugs und CPS}\n\\begin{frame}\\frametitle{Kanäle einrichten: analog}\n Kanäle für analog Repeater einzurichten ist leicht: Ein- und Ausgabe Frequenz und ggf. Subton.\n\\begin{center}\n \\begin{tabular}{l|l|l|l}\n Name & Ausgabe & Eingabe & Subton \\\\ \\hline\n DB0LDS & $439.5625$ & $431.9625$ & $67.0Hz$ \\\\\n \\end{tabular}\n\\end{center}\n\\end{frame}\n\n\\begin{frame}\\frametitle{Kanäle einrichten: DMR}\nAus 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. \n\\vspace{0.5cm}\n\\begin{center}\n \\begin{tabular}{l|l|l|l|l|l|l}\n Name & Ausgabe & Eingabe & CC & TS & Kontakt & Empfangsgruppe \\\\ \\hline\n DL DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 262 & 91, 92, 262 \\\\\n Sa/Th DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 2629 & 2629 \\\\\n WS DB0LDS & $439.5625$ & $431.9625$ & 1 & 1 & 264022 & 264022 \\\\\n L9 DB0LDS & $439.5625$ & $431.9625$ & 1 & 2 & L9 & 8, 9, 2621 \\\\\n BB DB0LDS & $439.5625$ & $431.9625$ & 1 & 2 & 2621 & 8, 9, 2621 \\\\\n \\end{tabular}\n\\end{center}\n\\vspace{.5cm}\nHier 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.\n\\end{frame}\n\n\\begin{frame} \\frametitle{Code-plug programming software (CPS)}\n 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. \n \\vspace{0.5cm} \n Probleme:\n \\begin{itemize}\n  \\item Die Software ist Mist. Bedienkonzept aus den 90er Jahren.\n  \\item Sehr auf kommerziellen Einsatz ausgelegt. \n  \\item Sehr viele unnütze/verbotene Optionen (Verschlüsselung)\n  \\item Schlechte Übersetzung aus dem Chinesischen.\n  \\item Hardware- oder gar versionsspezifische, binäre code plugs \n  \\item Jeder muss quasi seinen eigenen code plug bauen. Es gibt keinen Austausch.\n \\end{itemize}\n\\end{frame}\n\n\\section{QDMR -- Ein Versuch einer universellen CPS}\n\\begin{frame} \\frametitle{QDMR -- Ein Versuch einer universellen CPS}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-general-settings.png}\n\\end{center}\nQDMR 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.\n\\end{frame}\n\n\\begin{frame}\n Unterstütze Geräte (bisher):\n \\begin{itemize}\n  \\item Open GD-77 Firmware (RD-5R, GD-77, ...)\n  \\item Radioddity RD-5R\n  \\item Retevis RT3S\n  \\item TyT MD-UV390\n  \\item AnyTone AT-D878UV\n \\end{itemize}\n \\pause Quasi fertig:\n \\begin{itemize}\n  \\item Radioddity GD-77\n  \\item TYT MD-UV380, MD-2017, MD-9600\n  \\item Retevis RT84\n  \\item Anytone AT-D868UVE\n  \\item BTECH DMR-6X2 (läuft wahrscheinlich schon, identisch zum AT-D878UV)\n \\end{itemize}  \n \\pause Nahe Zukunft:\n \\begin{itemize}\n  \\item Hytera Geräte (vielen Dank an Matt)!\n \\end{itemize}\n\\end{frame}\n\n\\begin{frame}\n \\begin{center}\n  {\\Huge Demo!}\n \\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Grundeinstellungen (1/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-general-settings.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kontakte/Sprechgruppen (2/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-contacts-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kontakte/Sprechgruppen: Direktruf}\n\\begin{center}\n \\includegraphics[width=0.25\\linewidth]{../fig/qdmr-edit-contact-private-call.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kontakte/Sprechgruppen: Gruppenruf}\n\\begin{center}\n \\includegraphics[width=0.25\\linewidth]{../fig/qdmr-edit-contact-group-call.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kontakte/Sprechgruppen (2/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-contacts-example.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Empfangsgruppen (3/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-rxgroup-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Empfangsgruppen: Hinzufügen}\n\\begin{center}\n \\includegraphics[width=0.25\\linewidth]{../fig/qdmr-edit-rxgroup-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Empfangsgruppen: Hinzufügen}\n\\begin{center}\n \\includegraphics[width=0.25\\linewidth]{../fig/qdmr-edit-rxgroup-select.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Empfangsgruppen (3/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-rxgroup-list.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kanäle (4/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-channels-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kanäle: Analog}\n\\begin{center}\n \\includegraphics[width=0.5\\linewidth]{../fig/qdmr-channels-edit-analog.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kanäle: Digital}\n\\begin{center}\n \\includegraphics[width=0.5\\linewidth]{../fig/qdmr-channels-edit-digital.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kanäle (4/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-channels-example.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Zonen (5/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-zone-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Zone hinzufügen}\n\\begin{center}\n \\includegraphics[width=0.5\\linewidth]{../fig/qdmr-zone-edit.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Kanäle zur Zone hinzufügen}\n\\begin{center}\n \\includegraphics[width=0.25\\linewidth]{../fig/qdmr-zone-edit-select.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Zonen (5/5)}\n\\begin{center}\n \\includegraphics[width=0.75\\linewidth]{../fig/qdmr-zone-empty.png}\n\\end{center}\n\\end{frame}\n\n\\begin{frame} \\frametitle{Code-plug Format}\n\\begin{center}\n \\includegraphics[width=0.9\\linewidth]{../fig/qdmr-codeplug-format.png}\n\\end{center}\n\\end{frame}\n\n\n\\section{Down the rabbit hole}\n\\begin{frame} \\frametitle{Howto reverse engineer a code-plug?}\n Hilfsmittel:\n \\begin{itemize}\n  \\item Eine Windows Instanz in einer virtuellen Maschine (VirtualBox, o.ä.) mit der original CPS\n  \\item Wireshark mit \\emph{usbmon} Kernelmodul (bei allen Linux Distributionen dabei)\n  \\item Viel Geduld\n  \\item Viel Frustrationstoleranz\n \\end{itemize}\n Vorgehen:\n \\begin{itemize}\n  \\item Die Kommunikation zwischen CPS und Gerät belauschen und mitschneiden.\n  \\item Die Rohdaten aus den Mitschnitten extrahieren und Gemeinsamkeiten/Unterschiede darin finden.\n  \\item Daraus das Kommunikationsprotokoll sowie das Paketformat ableiten (sehr aufwendig).\n  \\item Danach differentielle Analyse des Code-plugs. D.h., \n  \\begin{itemize}\n    \\item \\textbf{Eine} kleine Änderung im code-plug auf das Gerät spielen.\n    \\item Kommunikation mitschneiden\n    \\item Code-plug daraus extrahieren.\n    \\item Vergleichen vor und nach der Änderung.\n  \\end{itemize}\n \\end{itemize}\n\\end{frame}\n\n\\begin{frame}[fragile]\\frametitle{Howto reverse engineer a code-plug?}\nBeispiel Hytera (Rohdaten):\n\\begin{verbatim}\n7e 00 00 fe 20 10 00 00 00 0c 60 e5\n7e 01 00 00 20 10 00 00 00 14 31 d3 02 03 02 01 00 00 2c 03    \n7e 01 00 00 20 10 00 00 00 24 02 f1 02 c5 01 11 00 [...] 00 5b 03\n7e 01 00 00 20 10 00 00 00 14 41 c3 02 01 02 01 00 12 1c 03    \n\\end{verbatim}\n\\end{frame}\n\n\\begin{frame}[fragile]\\frametitle{Howto reverse engineer a code-plug?}\nBeispiel Hytera (Code-plug extrahieren):\n\\begin{lstlisting}\n< RES: flags=00, src=10, dest=20, seq=0004\n  | RSP: type=81C7 seq=EA crc=170F (1887)\n  | RD: addr=00000000\n  | |  00000000 16 01 52 43 44 42 01 db 07 02 18 50 44 37 38 30 | ..RCDB.....PD780\n  | |  00000010 30 30 47 30 30 4d 30 30 30 30 30 00 00 85 85 00 | 00G00M00000.....\n  | |  00000020 00 00 00 00 00 00 56 6a 19 00 9a 00 00 00 00 00 | ......Vj........\n...\n\\end{lstlisting}\n\\end{frame}\n\n\\begin{frame}[fragile]\\frametitle{Howto reverse engineer a code-plug?}\nBeispiel Hytera (Code-plug vergleichen, einfach mit \\emph{diff}):\nHier wurde gar nichts geändert, dennoch gibt es einen unterschied im übertragenen Code-plug\n\\begin{lstlisting}\n< 00000090 80 a1 03 1c e5 07 03 16 02 17 db 07 03 0a 0a 21 | ...............!\n---\n> 00000090 80 a1 03 1c e5 07 03 16 02 1a db 07 03 0a 0a 21 | ...............!\n\\end{lstlisting}\n\\begin{itemize}\n \\item \\texttt{0x07e3} = 2021, \\texttt{0x03} = 3, \\texttt{0x16} = 22, \\texttt{0x02} = 2, \\texttt{0x17}=23\n \\item \\texttt{0x07e3} = 2021, \\texttt{0x03} = 3, \\texttt{0x16} = 22, \\texttt{0x02} = 2, \\texttt{0x1a}=26\n \\item $\\Rightarrow$ Unterschied ist Zeitstempel der letzten Programmierung.\n\\end{itemize}\n\\end{frame}\n\n\\begin{frame}[fragile]\\frametitle{Howto reverse engineer a code-plug?}\nBeispiel Hytera (Code-plug vergleichen, einfach mit \\emph{diff}):\nHier wurde die DMR ID von 1 auf 12345678 geändert:\n\\begin{lstlisting}\n< 00065AA0 04 00 4c 00 00 00 62 00 00 00 01 00 00 00 10 06 | ..L...b.........\n---\n> 00065AA0 04 00 4c 00 00 00 62 00 00 00 4e 61 bc 00 10 06 | ..L...b...Na....\n\\end{lstlisting}\n\\begin{itemize}\n \\item \\texttt{0x01} = 1, \\texttt{0xbc614e} = 12345678\n \\item $\\Rightarrow$ Die DMR ID wird an der Adresse \\texttt{0x00065AAA} als 24bit little-endian Zahl abgelegt.\n\\end{itemize}\n\\end{frame}\n\n\\end{document}"
  },
  {
    "path": "doc/dmr-intro/talk-ov2023/slides.tex",
    "content": "\\documentclass[aspectratio=169]{beamer}\n\\usetheme{Boadilla}\n\n\\usepackage{hyperref}\n\\usepackage{graphicx}\n\\usepackage{subcaption}\n\\usepackage{standalone}\n\\usepackage[ngerman]{babel}\n\\usepackage{tikz}\n\\usepackage{pgfplots}\n\\usetikzlibrary{shapes.geometric}\n\\usepackage{listings}\n\\usepackage[utf8]{inputenc}\n\\usepackage{tikzsymbols}\n\\usepackage{standalone}\n\n\\title[DMR]{Kurze Einführung in Digital Mobile Radio (DMR)}\n\\subtitle{Ein Mobilfunknetz für den Amateurfunk}\n\n\\author{Hannes, DM3MAT}\n\\institute{\\texttt{dm3mat [at] darc [dot] de}}\n\\date{5. April 2023}\n\n\\lstset{ %\nbasicstyle=\\tiny    % the size of the fonts that are used for the line-numbers\n}\n\n\n\\input{../fig/repeater}\n\n\\begin{document}\n\\begin{frame}\n \\titlepage\n\\end{frame}\n\n\\begin{frame} \\frametitle{Übersicht}\n \\tableofcontents\n\\end{frame}\n\n\n\\section{Motivation}\n\\begin{frame}{Motivation}\nSchnatterfunk:\n\\begin{itemize}\n \\item Ziel: Ich möchte mit bestimmten Leuten oder auch mit Irgendjemanden reden.\n \\pause\\item Auf UKW: Reichweite begrenzt. Gerade mit Handfunke.\n \\pause\\item Lösung: Repeater.\n \\pause\\item Nächstes Problem: Reichweite immer noch begrenzt.\n \\pause\\item Lösung: Repeater vernetzen! (Echolink)\n \\pause\\item Immer noch Probleme: \n \\begin{itemize}\n  \\pause\\item Wo sitzen die Leute, mit denen ich reden Will?\n  \\pause\\item Welche Repeater sind dort in der Nähe?\n  \\pause\\item Welche EL-Nummer haben die? \n \\end{itemize}\n \\pause\\item Eigentliches Problem: Was interessieren mich Repeater? Mich interessieren die Leute!\n\\end{itemize}\n\\end{frame}\n\n\\begin{frame}{Repeatertransparenz}\nEigentlich sollten FM-Relais 4 verschiedene Anwendungsfälle abdecken:\n\\begin{enumerate}\n \\pause\\item Direktes QSO mit einer bestimmten Person, egal wo diese sitzt. \\pause\\Sadey\n \\pause\\item Teilnahme an themenspezifischer Runde, egal wo die Teilnehmer sitzen. \\pause\\Neutrey\n \\pause\\item Teilnahme an regionaler Runde. \\pause\\Neutrey\n \\pause\\item QSO zum nächsten Dorf. \\pause\\Smiley\n\\end{enumerate}\n\\end{frame}\n\n\\begin{frame}{Repeatertransparenz}\nEs wäre also schön, wenn der einzelne Repeater nicht mehr so im Zentrum stehen würde. \n\n\\pause Wir vernetzen also die Repeater und packen wir was anderes in die Mitte:\n\\begin{block}{Sprechgruppe/Talkgroup}\n Eine Sprechgruppe/Talkgroup ist ein virtueller Raum/Repeater. Er existiert nicht physisch durch einen Zusammenschluss bestimmter Repeater, sondern im Netz aller Repeater. \n \n 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.\n\\end{block}\n\\end{frame}\n\n\n\\section{Ursprung}\n\\begin{frame}{Ursprung}\nDigital 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]\n\n\\pause Beispiel Flughafen (Gebäude): Es gibt eine Vielzahl an Gruppen:\n\\begin{itemize}\n \\item Die Reinigungskolonne,\n \\item die Sicherheitsleute wie Gepäckkontrolle oder Wachschutz,\n \\item die Techniker,\n \\item die Betriebsfeuerwehr und\n \\item die Zentrale.\n\\end{itemize}\n\\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. \n\\end{frame}\n\n\\begin{frame}{Beispiel: Flughafen}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/trunk_net_ex1.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/trunk_net_ex2.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/trunk_net_ex3.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/trunk_net_ex4a.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/trunk_net_ex4b.tex}\n\\end{frame}\n\n\\section{Sprechgruppen}\n\\begin{frame}{Nachmittagsschnatterrunde}\n\\begin{block}{Anwendungsbeispiel im Amateurfunk: die Nachmittagsrunde}\n\\begin{itemize}\n \\item Eine Nachmittagsrunde findet in der regionalen Sprechgruppe statt. Z.B., in der TG 2621 \\emph{Berlin/Brandenburg} kurz BB.\n \\item Alle Teilnehmer befinden sich in der Region BB, außer DL3XYZ, der ist im Urlaub.\n\\end{itemize} \n\\end{block}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/talkgroup_ex1a.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/talkgroup_ex1b.tex}\n\\end{frame}\n\n\\begin{frame}\n\\centering\n\\includegraphics[width=\\linewidth]{../fig/talkgroup_ex1c.tex}\n\\end{frame}\n\n\n\\section{Technischer Hintergrund}\n\\begin{frame}{Technischer Hintergrund}\nDMR ist ein digitaler Funkstandard. D.h., die Sprache wird abgetastet, verlustbehaftet komprimiert (Codec) und digital übertragen (4-MFSK).\\\\[0.5cm]\n\nDa digitaler Mode, können Metadaten (Quelle, Ziel, weitere Daten) parallel mit der Sprache übertragen werden.\\\\[0.5cm]\n\nModerne Codecs erlauben Sprachübertragung mit geringer Bandbreite (Datenübertragungsrate). DMR verwendet AMBE+2 (ebenso wie System Fusion).\\\\[0.5cm]\n\\end{frame}\n\n\\begin{frame}{Technischer Hintergrund}\nDMR 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]\n\nWir könnten Kanalbreite halbieren (6.25kHz) oder aber zwei QSOs gleichzeitig auf einem Kanal per TDMA (time-division multiple access) ermöglichen.\\\\[0.5cm]\n\\end{frame}\n\n\\begin{frame}{TDMA - 2 QSOs auf einer Frequenz}\n\\begin{center}\n \\includegraphics[width=0.7\\linewidth]{../fig/timeslot.tex}\n\\end{center}\n 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]\n \n Was/Wann Zeitschlitz 1/2 ist, bestimmt der Repeater. Er gibt den Takt vor. \\\\[0.5cm]\n \n Somit sind auf einem 12.5kHz Kanal zwei parallele unabhängige QSOs möglich.\n\\end{frame}\n\n\\begin{frame}{Qualität und Reichweite}\n Technisches Limit 150km, für Relaisbetrieb ausreichend.\\\\[0.5cm]\n \n Hörbare Kompressionsartefakte durch AMBE+2. Auch bei idealer Verbindung. Dennoch sehr gute Verständlichkeit, vglb. 12.5kHz FM. \\\\[0.5cm]\n \n Qualität beleibt auch bei schlechtem SNR erhalten. Dann aber schnelle Degradierung.\n\\end{frame}\n\n\\section{Konfiguration}\n\\begin{frame}{Konfiguration (Brandmeister)}\nDie Konfiguration (sog. Codeplug erstellen) von DMR Funkgeräten ist recht komplex. \\\\[0.5cm]\n\n\\pause In FM ist ein Kanal ein Relais. In DMR sind Relais nicht wichtig, sondern Sprechgruppen. Wir programmieren daher logische Kanäle:\n\\begin{center}\n\\begin{tabular}{|c|c|} \\hline\nFM & DMR \\\\ \\hline \\hline\nRX Frequenz & RX Frequenz \\\\\nTX Ablage & TX Ablage \\\\\n --- & Zeitschlitz \\\\\n(CTCSS/DCS) & Color Code \\\\\n--- & TX Kontakt \\\\\n--- & Empfangsgruppen \\\\\\hline\n\\end{tabular}\n\\end{center}\n\\end{frame}\n\n\\begin{frame}{Konfiguration (Brandmeister)}\n\\begin{description}\n \\item[RX/TX Frequenz] Klar.\n \\item[Zeitschlitz] Hatten wir schon. Konvention: \n \\begin{itemize}\n  \\item TS1 überregionaler Funkverkehr\n  \\item TS2 regionaler/lokaler Funkverkehr\n \\end{itemize}\n \\item[Color Code] Meist fix 1. Vergleichbar mit CTCSS/DSC Tönen in FM. Vermeidet Probleme bei Überreichweiten.\n \\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. \n \\item[Empfangsgruppen] Sprechgruppen, die auf einem Kanal empfangen werden sollen.\n\\end{description}\n\\end{frame}\n\n\\begin{frame}{Konfiguration -- DMR-ID}\n \\begin{itemize}\n  \\item Jeder Teilnehmer, Sprechgruppe und Dienst benötigt eine eindeutige DMR ID. \n  \\item Eine ID pro Teilnehmer, nicht Gerät! Zu bekommen bei \\url{https://radioid.net}.\n  \\item Funktioniert wie eine Telefonnummer, incl. Prefix. Z.B., 262/263 ... Deutschland.\n  \\item Datenbank ID$\\leftrightarrow$Call frei verfügbar. Kann auch aufs Gerät gespielt werden um ID aufzulösen.\n \\end{itemize}\n\\end{frame}\n\n\n\\begin{frame}{Konfiguration -- Sprechgruppen}\n\\begin{description}\n \\item[TG8] Regionaler Verbund von Repeatern. Nicht festgelegt.\n \\item[TG9] Nur dieser Repeater. Verhält sich wie FM Repeater.\n \\item[TG91] Welt weit, ist viel los dort.\n \\item[TG92] Europa\n \\item[TG2621] Berlin/Brandenburg\n \\item[TG2629] Sachsen/Thüringen\n\\end{description}\n\\end{frame}\n\n\n\\begin{frame}{Konfiguration -- Kanäle}\nIn DMR werden pro Repeater mind. zwei Kanäle programmiert. Einen für jeden Zeitschlitz. Meist aber eher mehr. \\\\[0.5cm]\n\nZiel: Kontakt zu Sprechgruppen. \n\\begin{enumerate}\n \\item Liste der Sprechgruppen zusammenstellen, die uns interessieren.\n \\begin{itemize}\n  \\item TG9, TG2621, TG2629, TG26223 (Chaosrunde CCC), ...\n \\end{itemize}\n \\item Liste der Repeater raus suchen.\n \\begin{itemize}\n  \\item DB0LDS, DB0PDM, DM0LEI, ...\n \\end{itemize}\n\\end{enumerate}\n\\end{frame}\n\n\\begin{frame}{Konfiguration -- Empfangsgruppen}\nÜ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]\n\n\\begin{description}\n \\item[B/B] TG8, TG9, TG2621\n \\item[Sa/Th] TG8, TG9, TG2629\n \\item[CCC] TG26223\n\\end{description}\n\\end{frame}\n\n\\begin{frame}{Konfiguration -- Kanäle}\n Jetzt sind wir so weit, die Kanäle anlegen zu können:\n \\begin{center}\n \\begin{tabular}{|l|llllll|}\\hline\n Name & RX Freq & Ablage & CC & TS & Kontakt & RX Gr. \\\\ \\hline \\hline\n DB0LDS BB  & 439.5625 & -7.6 & 1 & 2 & Berlin/Brandenburg & B/B  \\\\\n DB0LDS ST  & 439.5625 & -7.6 & 1 & 1 & Sachsen/Thüringen  & Sa/Th  \\\\\n DB0LDS CCC & 439.5625 & -7.6 & 1 & 1 & Chaosrunde         & CCC  \\\\\n DB0PDM BB  & 438.4000 & -7.6 & 1 & 2 & Berlin/Brandenburg & B/B  \\\\\n DM0LEI BB  & 439.1500 & -7.6 & 1 & 1 & Berlin/Brandenburg & B/B  \\\\\n DM0LEI ST  & 439.1500 & -7.6 & 1 & 2 & Sachsen/Thüringen  & Sa/Th \\\\ \\hline\n \\end{tabular}\n \\end{center}\n\\end{frame}\n\n\\begin{frame}{Konfiguration -- Zonen}\n Da so sehr viele Kanäle entstehen, werden diese in Zonen zusammengefasst (meist nach Regionen):\\\\[0.5cm]\n \\begin{description}\n  \\item[Berlin/Brandenburg] DB0LDS BB, DB0PDM BB, DB0LDS ST, DB0LDS CCC\n  \\item[Sachsen/Thüringen] DM0LEI BB, DM0LEI ST\n \\end{description}\\vspace{0.5cm}\n \n \\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.\n\\end{frame}\n\n\\section{Demo}\n\\begin{frame}\n\\begin{block}{}\n Demo...\n\\end{block}\n\\end{frame}\n\n\\section*{QDMR}\n\\begin{frame}{QDMR}\nDa Konfiguration recht kompliziert sein kann, wird eine Software vom Hersteller verwendet (CPS, Codeplug Programming Software).\n\\begin{itemize}\n \\item CPS vom Hersteller nur für Windows\n \\item Bedienkonzept aus den 90ern. \n \\item Miese Übersetzung aus dem Chinesischen. \n \\item Sehr auf kommerzielle Anwender ausgerichtet.\n \\item Keine Anbindung an User und Repeater Datenbanken.\n \\item Kein Austausch von Konfigurationen zwischen Geräten oder gar Herstellern.\n\\end{itemize}\n\\end{frame}\n\n\\begin{frame}{QDMR}\n Da ich kein Windows habe, brauchte ich eine Lösung um meine DMR Geräte zu programmieren: Also eigene Software schreiben. \n \\begin{itemize}\n  \\item basiert auf Tool \\texttt{dmrconfig} von Serge, KK6ABQ\n  \\item läuft auf Linux und Mac OS X\n  \\item verwendet gemeinsame Konfiguration für 20 verschiedene Geräte von verschiedenen Herstellern\n  \\item Einbindung der User DB (DMR ID $\\leftrightarrow$ Calls)\n  \\item Einbindung der Sprechgruppen DB von Brandmeister\n  \\item Einbindung von Repeater Book\n  \\item nur 100k Zeilen C++ Code.\n \\end{itemize}\n\\end{frame}\n\\end{document}"
  },
  {
    "path": "doc/dmrconf.in.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<refentry xml:id=\"dmrconf\"\n  xmlns=\"http://docbook.org/ns/docbook\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n  <refmeta>\n    <refentrytitle>dmrconf</refentrytitle>\n    <manvolnum>1</manvolnum>\n    <refmiscinfo class=\"source\">dmrconf</refmiscinfo>\n    <refmiscinfo class=\"version\">${PROJECT_VERSION}</refmiscinfo>\n    <refmiscinfo class=\"manual\">User Commands</refmiscinfo>\n  </refmeta>\n\n  <info>\n    <author>\n      <personname>\n        <firstname>Hannes</firstname> <surname>Matuschek</surname>\n      </personname>\n      <email>dm3mat@darc.de</email>\n      <contrib>Main author</contrib>\n    </author>\n  </info>\n\n  <refnamediv>\n    <refname>dmrconf</refname>\n    <refpurpose>Command-line tool for programming DMR radios.</refpurpose>\n  </refnamediv>\n\n  <refsynopsisdiv>\n    <cmdsynopsis>\n      <command>dmrconf</command>\n      <arg choice=\"opt\"><option>OPTIONS</option></arg>\n      <arg><option>COMMAND</option></arg>\n      <arg choice=\"opt\">file</arg>\n    </cmdsynopsis>\n  </refsynopsisdiv>\n\n  <refsect1>\n    <title>Description</title>\n    <para>\n      <command>dmrconf</command> is a command-line tool to program DMR radios.\n      That is, generating and uploading codeplugs to these radios. To this \n      end, <command>dmrconf</command> uses a common human-readable text format\n      to describe the codeplug for all supported radios (see below). This \n      allows one to share codeplugs between different radios.\n    </para>\n\n    <para>\n      Additionally, <command>dmrconf</command> also allows one to download \n      codeplugs from the radio and to store it in the human-readable text \n      format. \n    </para>\n  </refsect1>\n\n  <refsect1>\n    <title>Commands</title>\n    <variablelist>\n      <varlistentry>\n        <term><command>detect</command></term>\n        <listitem>\n          <para>\n            Detects a connected radios. You may specify a specific device using\n            the <option>-D</option> or <option>--device</option> option.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>read</command></term>\n        <listitem>\n          <para>\n            Reads a codeplug from the radio and stores it into the given file.\n            This command may need the <option>-y</option> or \n            <option>-b</option> options if the file type cannot be inferred \n            from the filename.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>write</command></term>\n        <listitem>\n          <para>\n            Writes the specified codeplug to the radio. This command may need \n            the <option>-c</option>, <option>-y</option> or <option>-b</option>\n            options if the file type cannot be inferred from the filename.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>write-db</command></term>\n        <listitem>\n          <para>\n            Writes the call-sign database to the device. This command may need \n            the <option>--id</option> option to select call-signs if the \n            complete database does not fit into the device. If specified, all \n            call-signs closest to the specified ID are used. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>verify</command></term>\n        <listitem>\n          <para>\n            Verifies the codeplug with the connected radio or the specified \n            radio passed with the <option>--radio</option> option. This command\n            may also need the <option>-y</option> or <option>-b</option> \n            options if the file type cannot be inferred from the filename.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>encode</command></term>\n        <listitem>\n          <para>\n            Encodes a YAML codeplug as a binary one for the connected or \n            specified radio using the <option>--radio</option> option. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>encode-db</command></term>\n        <listitem>\n          <para>\n            Encodes the call-sign database as a binary one for the connected or\n            specified radio using the <option>--radio</option> option. This \n            command may need the <option>--id</option> option to select \n            call-signs if the complete database does not fit into the device. \n            If specified, all call-signs closest to the specified ID are used. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>decode</command></term>\n        <listitem>\n          <para>\n            Decodes a binary codeplug and stores the result in human-readable \n            form. The radio must be specified using the \n            <option>--radio</option> option.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><command>info</command></term>\n        <listitem>\n          <para>\n            Prints some information about the given file.\n          </para>\n        </listitem>\n      </varlistentry>\n    </variablelist>\n  </refsect1>\n\n  <refsect1>\n    <title>Options</title>\n    <variablelist>\n      <varlistentry>\n        <term><option>-c</option> or <option>--csv</option></term>\n        <listitem>\n          <para>\n            Specifies the file format for the input file for \n            <command>verify</command>, <command>encode</command> and \n            <command>write</command> commands. This option is not needed if the\n            filetype can be inferred from the filename. That is, if the file \n            ends on <filename>.conf</filename> or <filename>.csv</filename>.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-y</option> or <option>--yaml</option></term>\n        <listitem>\n          <para>\n            Specifies the file format for the input or output file for the \n            <command>verify</command>, <command>read</command> and \n            <command>write</command> commands. This option is not needed if the\n            filetype can be inferred from the filename. That is, if the file \n            ends on <filename>.yaml</filename> or <filename>.yml</filename>.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-b</option> or <option>--bin</option></term>\n        <listitem>\n          <para>\n            Specifies the file format for the input or output file for the \n            <command>verify</command>, <command>read</command> and\n            <command>write</command> commands. This option is not needed if the\n            filetype can be inferred from the filename. That is, if the file \n            ends on <filename>.bin</filename> or <filename>.dfu</filename>.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-m</option> or <option>--manufacturer</option></term>\n        <listitem>\n          <para>\n            Specifies the file format for the input file for the \n            <command>decode</command> command to be the manufacturer binary \n            codeplug format. Not all manufacturer formats are implemented.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-D</option> or <option>--device=DEVICE</option></term>\n        <listitem>\n          <para>\n            Specifies the device to use. Either a USB <token>BUS:DEVICE</token> \n            number combination or the name of a serial interface. The device\n            must be specified if the automatic radio detection fails, is unsave \n            or if more than one radio is connected to the host.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-R</option> or <option>--radio=</option>NAME</term>\n        <listitem>\n          <para>\n            Specifies the radio for the <command>verify</command>,\n            <command>encode</command> or <command>decode</command> commands.\n            This option can also be used to override the automatic radio \n            detection for the <command>read</command> and \n            <command>write</command> commands. Be careful using this option \n            when writing to the device. An incompatible code-plug might be \n            written.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-I</option> or <option>--id=</option>DMR_ID</term>\n        <listitem>\n          <para>\n            Specifies the DMR ID or a comma separated list of DMR ID prefixes \n            for the <command>write-db</command> or <command>encode-db</command>\n            commands. More than one ID may be specified using a \n            comma-separator.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-n</option> or <option>--limit=</option>N</term>\n        <listitem>\n          <para>\n            Limits several amounts, depending on the context. When encoding or \n            writing the call-sign db, this option specifies the maximum \n            number of call-signs to encode.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-B</option> or <option>--database=</option>JSON_FILE</term>\n        <listitem>\n          <para>\n            Specifies the call-sign database to use for writing a user-db to the \n            device.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--init-codeplug</option></term>\n        <listitem>\n          <para>\n            Initializes the code-plug from scratch. If omitted (default) the \n            codeplug on the device gets updated. This maintains all settings \n            made earlier via the manufacturer CPS or on the radio itself, that \n            are not part of the codeplug.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--update-device-clock</option></term>\n        <listitem>\n          <para>\n            If set, the device clock gets updated with the transfer to the \n            radio. That is, with any codeplug or call-sign DB being written to \n            the device. Not all radios support this feature.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--auto-enable-gps</option></term>\n        <listitem>\n          <para>\n            Automatically enables GPS/APRS if at least one GPS/APRS system is\n            defined and used by any channel. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--auto-enable-roaming</option></term>\n        <listitem>\n          <para>\n            Automatically enables roaming if at least one roaming zone is \n            defined and used by any channel. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--ignore-limits</option></term>\n        <listitem>\n          <para>\n            Disables the enforcement of limits. Warnings are still shown.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-h</option> or <option>--help</option></term>\n        <listitem>\n          <para>\n            Displays a short help message.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--list-radios</option></term>\n        <listitem>\n          <para>\n            Lists all supported radios.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-v</option> or <option>--version</option></term>\n        <listitem>\n          <para>\n            Displays the version number.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-V</option> or <option>--verbose</option></term>\n        <listitem>\n          <para>\n            Enables debug messages.\n          </para>\n        </listitem>\n      </varlistentry>\n    </variablelist>\n  </refsect1>\n\n  <refsect1>\n    <title>Supported Radios</title>\n    <para>The following list contains all supported radios and their names for the\n      <option>--radio</option> option.</para>\n    <variablelist>\n      <varlistentry>\n        <term><option>opengd77</option></term>\n        <term><option>openuv380</option></term>\n        <listitem>\n          <para>\n            All radios running the Open GD77 firmware.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>d868uv</option></term>\n        <term><option>d868uve</option></term>\n        <listitem>\n          <para>\n            Anytone AT-D868UV(E).\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>d878uv</option></term>\n        <listitem>\n          <para>\n            Anytone AT-D878UV.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>d878uv2</option></term>\n        <listitem>\n          <para>\n            Anytone AT-D878UVII.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>d578uv</option></term>\n        <term><option>d578uv2</option></term>\n        <listitem>\n          <para>\n            Anytone AT-D578UV (II).\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>md390</option></term>\n        <term><option>rt8</option></term>\n        <listitem>\n          <para>\n            TYT MD-390 or Retevis RT8.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>uv390</option></term>\n        <term><option>rt3s</option></term>\n        <listitem>\n          <para>\n            TYT MD-UV390 or Retevis RT3S.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>md2017</option></term>\n        <term><option>rt82</option></term>\n        <listitem>\n          <para>\n            TYT MD-2017 or Retevis RT82.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>gd73</option></term>\n        <listitem>\n          <para>\n            Radioddity GD-73.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>gd77</option></term>\n        <listitem>\n          <para>\n            Radioddity GD-77.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>rd5r</option></term>\n        <listitem>\n          <para>\n            Baofeng/Radioddity RD-5R.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>dm1701</option></term>\n        <term><option>rt84</option></term>\n        <listitem>\n          <para>\n            Baofeng DM1701 or Retevis RT84.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>dr1801uv</option></term>\n        <listitem>\n          <para>\n            Baofeng DR1801UV-A6.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>dm32uv</option></term>\n        <listitem>\n          <para>\n            Baofeng DM-32UV.\n          </para>\n        </listitem>\n      </varlistentry>\n    </variablelist>\n  </refsect1>\n\n  <refsect1>\n    <title>Bugs</title>\n    <para>\n      This program is still under development and may contain bugs that\n      may cause harm to the radios and may even destroy them. Hence you may\n      use this software on your own risk. If you want to have guaranties,\n      consider using the CPS (code-plug programming software) supplied with\n      your radio.\n    </para>\n  </refsect1>\n</refentry>\n"
  },
  {
    "path": "doc/docbook_man.debian.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:strip-space elements=\"*\"/>\n\n  <xsl:include href=\"file:///usr/share/xml/docbook/stylesheet/docbook-xsl/manpages/docbook.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/docbook_man.fedora.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:strip-space elements=\"*\"/>\n\n  <xsl:include href=\"file:///usr/share/sgml/docbook/xsl-ns-stylesheets/manpages/docbook.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/docbook_man.macports.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:strip-space elements=\"*\"/>\n\n  <xsl:include href=\"file:///opt/local/share/xsl/docbook-xsl-nons/manpages/docbook.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/docbook_man.opensuse.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:strip-space elements=\"*\"/>\n\n  <xsl:include href=\"file:///usr/share/xml/docbook/stylesheet/nwalsh/current/manpages/docbook.xsl\"/>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/fig/autodetect.dot",
    "content": "digraph detect {\n graph [nodesep=\"0.5\", ranksep=\"0.25\"];\n rankdir = LR;\n enum [\n  label=\"Detect all devices\\nmatching VID:PID\";\n  shape=rect;\n ];\n single [\n  label=\"Only one USB\\n device found?\";\n  shape=diamond;\n ];\n save [\n  label=\"Save to assume\\n device is radio?\";\n  shape=diamond;\n ];\n userif [\n  label=\"User selects\\n USB device.\";\n  shape=parallelogram;\n ];\n ident [\n  label=\"All matching radios\\n identifiable?\";\n  shape=diamond;\n ];\n userdev [\n  label=\"User selects\\n radio.\";\n  shape=parallelogram;\n ];\n id [\n  label=\"Connect to radio\\n and request identifier.\";\n  shape=rect;\n ];\n end [\n  label=\"Radio detected\\nselected.\";\n  shape=oval;\n ];\n\n {rank = same; enum; single; save; ident; id; end; }\n {rank = same; userif; userdev; }\n\n enum:s -> single:n;\n single:s -> save:n [label=\"yes\"];\n save:e -> userif:w [label=\"no\"];\n save:s -> ident:n [label=\"yes\"];\n single:e -> userif:n [label=\"no\"];\n userif:s -> ident:n;\n ident:e -> userdev:w [label=\"no\"];\n ident:s -> id:n [label=\"yes\"];\n userdev:s -> end:e;\n id:s -> end:n;\n}\n"
  },
  {
    "path": "doc/manual/Makefile",
    "content": "SRC = manual.xml $(shell find meta/ -name \"*.*\") $(shell find intro/ -name \"*.*\") $(shell find gui/ -name \"*.*\")\\\n      $(shell find codeplug/ -name \"*.*\") $(shell find conf/ -name \"*.*\") $(shell find reveng/ -name \"*.*\") $(shell find cli/ -name \"*.*\") \n\nall: images manual.pdf manual.epub html \n\nimages:\n\tmake -C intro/fig\n\tmake -C gui/fig\n\t\n\t\nmanual.pdf: manual.fo\n\tfop -fo manual.fo -pdf manual.pdf\nmanual.fo: manual_combined.xml manual_fo.debian.xsl\n\tsaxon-xslt -o manual.fo manual_combined.xml manual_fo.debian.xsl xslthl.config=\"/usr/share/xslthl/highlighters/xslthl-config.xml\"\nmanual_combined.xml: $(SRC) \n\txmllint --nonet --noent --xinclude manual.xml > manual_combined.xml\n\n\nmanual.epub: images-epub manual_combined.xml \n\tcp manual_combined.xml epub/manual.xml \n\tmake -C epub \nimages-epub: images epub/fig\n\tinstall intro/fig/*.png epub/fig \n\tinstall gui/fig/*.png epub/fig \nepub/fig:\n\tmkdir epub/fig \n\n\nhtml: images-html\n\tcp manual_combined.xml html/manual.xml \n\tmake -C html \nimages-html: images html/fig manual_combined.xml\n\tinstall intro/fig/*.png html/fig \n\tinstall gui/fig/*.png html/fig \nhtml/fig:\n\tmkdir html/fig\n\n\nclean: \n\tmake -C intro/fig clean\n\tmake -C epub clean\n\tmake -C html clean\n\trm -f manual.pdf manual.fo manual_combined.xml\n"
  },
  {
    "path": "doc/manual/cli/callsign.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"cmdWriteDB\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Writing the call-sign DB</title>\n</info>\n\n<para>\n  The command line interface also allows to write a call-sign DB (also known as <glossterm>Talker \n  Alias</glossterm>) to the radio if the radio supports it. This can be done with the \n  <command>write-db</command> command. This command behaves similar to the call-sign DB upload in \n  <application>qdmr</application>. That is, it tries to select the call-signs being written \n  automatically.\n</para>\n\n<para>\n  Although many radios provide a huge amount of memory for the call-sign DB, they cannot hold the \n  entire list of assigned DMR IDs. Therefore, a selection of relevant call-signs must be done. \n  <application>qdmr</application> and <command>dmrconf</command> do that based on the DMR ID of\n  the radio. DMR IDs are not random. They follow a pattern similar to land-line phone numbers. DMR\n  IDs within a certain continent, country and region share a common prefix. This way, it is \n  possible to select DMR IDs that are <emphasis>close</emphasis> by selecting IDs that share a \n  common prefix. \n</para>\n\n<para>\n  <application>qdmr</application> and <command>dmrconf</command> use the default DMR ID\n  of the radio to select the call-signs to be written. This ID is obtained by \n  <application>qdmr</application> from the codeplug. <command>dmrconf</command> does not require a\n  codeplug to be present for the upload. Consequently, it requires the explicit specification \n  of the ID to base the selection on. The ID can be specified using the <option>--id</option> or \n  <option>-I</option> options. It is also possible to just specify the prefix instead of an ID.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf write-db --id=2621]]></programlisting>\n</informalexample>\n\n<para>\n  This example writes a call-sign DB to the connected radio using the DMR prefix 2621 (for the \n  region Berlin/Brandenburg in Germany). The radio will fill the available space with as many \n  call-signs as possible. So for the previous example, <command>dmrconf</command> will not only\n  program all IDs with the prefix 2621 but as many as possible starting with those \n  <emphasis>close</emphasis> to that prefix. It is possible to limit the number of call-signs \n  encoded using the <option>--limit</option> or <option>-n</option> option.\n</para>\n\n<para>\n  Unfortunately, there is always an exception to a rule: Some countries have several prefixes or\n  you may program the call-signs of several countries that are not necessarily \n  <emphasis>close</emphasis> in terms of their prefixes. For these cases, it is possible to \n  specify a list of prefixes to the <option>--id</option> option.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf write-db --id=262,263 --limit=10000]]></programlisting>\n</informalexample>\n\n<para>\n  This example will select and write up to 10000 (given the radio can hold that amount) \n  call-signs staring with those <emphasis>closest</emphasis> to the prefixes 262 and 263\n  (both prefixes for Germany).\n</para>\n\n<section>\n<info><title>Specifying own databases</title></info>\n<para> \n  With version 0.11.3, it is possible, to specify a user-crafted JSON database. Then, this \n  file will be used for selecting the call signs for writing the call-sign DB. \n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf write-db --id=262 --limit=10000 --database=my_db.json]]></programlisting>\n</informalexample>\n\n<para>\n  Here, up to 10000 call signs are written from the <filename>my_db.json</filename> JSON file \n  starting with those IDs closest to the prefix 262. Specifying a user-curated call-sign DB, simply\n  replaces the public one and thus all methods described above still work.\n</para>\n</section>\n\n<section xml:id=\"cmdEncodeDB\">\n<info><title>Encoding a call-sign DB</title></info>\n\n<para>\n  Like for the binary codeplug, it is also possible to generate and store the binary representation\n  of the call-sign DB using the <command>encode-db</command> command. This command is only useful \n  for debugging purposes as the binary representation of the call-sign DB cannot be written to the\n  device.\n</para>\n\n<para>\n  The <command>encode-db</command> takes the same arguments as the <command>write-db</command> \n  command but additionally needs a filename to store the encoded DB into as well as the radio\n  to encode for using the <option>--radio</option> option.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf encode-db --radio=d878uv --id=262,263 --limit=10000 callsigns.dfu]]></programlisting>\n</informalexample>\n\n<para>\n  Like the previous example, this one encodes up to 10000 call-signs starting with those closest to\n  the prefixes 262 and 263 for a AnyTone AT-D878UV radio and stores the result into \n  <filename>callsigns.dfu</filename>.\n</para>\n</section>\n</section>"
  },
  {
    "path": "doc/manual/cli/codeplug.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"cmdCodeplug\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Reading and writing codeplugs</title>\n</info>\n\n<para>\n  The major feature of the command line tool is certainly the ability to read and write codeplugs \n  from and to the device. The majority of the action happens automatically. Like the detection of\n  the radio. If something goes wrong, an error message will be written to <varname>stderr</varname>. \n  A more detailed logging can be enabled by passing the <option>--verbose</option> flag.\n</para>\n\n\n<section xml:id=\"cmdRead\">\n<info><title>Reading a codeplug</title></info>\n<para>\n  To read a codeplug, the <command>read</command> command is used. The codeplug can be stored in \n  several formats. The human readable extensible codeplug format (YAML) and as a binary memory dump\n  of the codeplug memory on the device. <command>dmrconf</command> detects the format based on the \n  file extension or by means of an additional flag. The latter is particularly important if the \n  read codeplug should be written to <varname>stdout</varname> for piping it to another program for\n  further processing.\n</para>\n\n<informalexample>\n  <programlisting language=\"bash\"><![CDATA[dmrconf read codeplug.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  Will simply read the codeplug from the detected device and stores it in the extensible codeplug \n  format in the file <filename>codeplug.yaml</filename>. The format was detected by the file \n  extension <filename>yaml</filename> which refers to the extensible codeplug format using YAML.\n</para>\n\n<para>\n  To store the memory dump of the codeplug memory of the radio, the file extension should be \n  <filename>dfu</filename>. \n</para>\n\n<para>\n  As mentioned above, it is also possible to dump the decoded codeplug to <varname>stdout</varname> \n  allowing to pipe the codeplug into another program for processing. This can be done by omitting \n  the output filename. Then, however, the output format is not specified anymore. In this case, one\n  the explicit format flags <option>--yaml</option> or <option>--bin</option> must be used to \n  specify in which format the codeplug should be written. These flags can also be used to store a \n  codeplug in a particular format in arbitrarily named files. \n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf read --yaml | python my_script.py]]></programlisting>\n</informalexample>\n<para>\n  This example reads the codeplug from the connected device and decodes it. The decoded codeplug is\n  then piped to the python script <filename>my_script.py</filename>. \n</para>\n\n\n<section xml:id=\"cmdDecode\">\n<info><title>Decoding binary codeplugs</title></info>\n<para>\n  It is also possible decode binary codeplugs that has been read from the device earlier and \n  stored as a memory dump (i.e., in a <filename>dfu</filename> file). This step is actually\n  the second step automatically performed during reading. When reading a codeplug, in a first step\n  the memory dump of the codeplug is read from the device. In a second step, the read binary \n  codeplug is then decoded and dumped in a human readable format. \n</para>\n\n<para>\n  The <command>decode</command> command performs that second step. To do that, it needs\n  two additional information: The radio type, from which this codeplug was read and the format\n  to write the decoded format to. Like for the <command>read</command> command, the latter\n  can be passed by the output filename extension or via an additional flag.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf read codeplug.dfu\ndmrconf decode --radio=uv390 codeplug.dfu codeplug.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  This example performs the same actions like a simple <command>read</command> command (assuming a\n  TyT MD-UV390 is connected). It first downloads the binary codeplug. This time, the memory dump \n  is stored in a binary form (<filename>dfu</filename> file). The second command then decodes \n  the binary codeplug into the extensible codeplug format (<filename>yaml</filename> file), assuming \n  that the binary codeplug stems from a TyT MD-UV390.\n</para>\n\n<para>\n  Since version 0.8.1, it is now also possible to decode some manufacturer binary codeplug files as\n  they are produced by the manufacturer CPS. To signal the decode command to treat the file as a\n  manufacturer CPS file, you need to pass the <option>-m</option> or <option>--manufacturer</option>\n  and the <option>--radio</option> option. The latter tells the <command>decode</command> command \n  the format of the file. That is, the call\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf decode -m --radio=uv390 manufacturer_cps_file.rdt codeplug.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  Will decode the manufacturer CPS file <filename>manufacturer_cps_file.rdt</filename> assuming it \n  is a file generated by the CPS for the TyT MD-UV390. Like for the <quote>normal</quote> decoding\n  the output format must be specified either by file extension or flag.\n</para>\n\n\n<section>\n<info><title>Debugging the codeplug decoding</title></info>\n<para>\n  Under normal circumstances, it makes no sense to first read the binary codeplug from the device \n  and then decoding it in a separate step as the read command will do that for you.\n</para> \n\n<para>\n  However, if there is a bug in <command>dmrconf</command> that gets triggered by your codeplug on \n  the device, the binary codeplug is an invaluable resource for debugging the application. Consider \n  filing an issue at the <link xlink:href=\"https://github.com/hmatuschek/qdmr/issues\">bug \n  tracker</link> and include the binary codeplug as an attachment. \n</para>\n\n<para>\n  If you like, you can also send me your codeplug directly. I'll keep it confidentially.\n</para>\n</section>\n</section>\n</section>\n\n\n<section>\n<info><title>Writing a codeplug</title></info>\n\n<para>\n  To write a codeplug into the device, the <command>write</command> command is used. The codeplug\n  can be read from several formats. The extensible codeplug format (<filename>yaml</filename> \n  file) as well as the old table based format (<filename>conf</filename> file). It is not possible\n  to write binary codeplugs without decoding them first. Like for the <command>read</command> \n  command, <command>dmrconf</command> will detect the format based on the file extension or by \n  passed flags.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf write codeplug.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  This example will write the codeplug stored in the extensible codeplug format in \n  <filename>codeplug.yaml</filename> to the connected device. Before writing the codeplug to the \n  device, the connected device gets detected and the codeplug gets verified.\n</para>\n\n<para>\n  If the verification step fails, one or more error messages are written to \n  <varname>stderr</varname> describing the issue with the codeplug. One verification step is the\n  check whether all channel frequencies are within the frequency limits specified by the \n  manufacturer. The latter check can be disabled using the \n  <option>--ignore-frequency-limits</option> flag.\n</para>\n\n<para>\n  There are also some flags controlling the assembly of the binary codeplug. When the  \n  <option>--init-codeplug</option> flag is set, the codeplug will be generated from scratch using\n  default values for all options not explicitly specified in the codeplug file. This might be used\n  to initialize a brand new radio. However, any changes made to the radio are lost. \n</para>\n\n<para>\n  When this option is not set, the codeplug gets encoded and written in a two-step process. First\n  the current binary codeplug is downloaded from the radio. Then the codeplug file is used to \n  update the binary codeplug. The result is then written back to the device. This ensures that all\n  settings made in the radio are kept, unless they are explicitly set in the codeplug file.\n</para>\n\n<para>\n  The <option>--auto-enable-gps</option> and <option>--auto-enable-roaming</option> flags will \n  tell <command>dmrconf</command> to enable the GPS or roaming feature whenever any of the \n  programmed channels use the GPS or a roaming zone. (This depends also on the ability of the \n  radio.)\n</para>\n\n<section xml:id=\"cmdVerify\">\n<info><title>Verify a codeplug</title></info>\n<para>\n  The aforementioned verification of the codeplug file can also be performed separately using the\n  <command>verify</command>. This command also needs to know against which radio the codeplug \n  should be verified. The radio must be specified using the <option>--radio</option> option.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf verify --radio=d878uv codeplug.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  This command will verify the codeplug stored in <filename>codeplug.yaml</filename> in the \n  extensible codeplug format against an AnyTone AT-D878UV. Like for the <command>write</command>, \n  any issues are written to <varname>stderr</varname>.\n</para>\n\n<para>\n  Like for the <command>write</command> command, the verification can be altered using the\n  <option>--ignore-frequency-limits</option> flag.\n</para>\n</section>\n\n\n<section xml:id=\"cmdEncode\">\n<info><title>Encoding codeplugs</title></info>\n\n<para>\n  Is is also possible to perform the encoding step of the codeplug separately. This can be done\n  with the <command>encode</command> command. Like for the <command>verify</command> command,\n  the <command>encode</command> command also needs the radio for which the codeplug should be \n  encoded. The input format of the codeplug is again specified by either the file extension \n  of the codeplug file or by flags.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf encode --radio=opengd77 codeplug.yaml codeplug.dfu]]></programlisting>\n</informalexample>\n\n<para>\n  This call will encode the codeplug <filename>codeplug.yaml</filename> specified in the extensible\n  codeplug format for a radio running the OpenGD77 firmware and stores the resulting binary \n  codeplug in <filename>codeplug.dfu</filename>.\n</para>\n\n<para>\n  Like for the <command>write</command> command, the encoding can be controlled using the \n  <option>--auto-enable-gps</option> and <option>--auto-enable-roaming</option> flags.\n</para>\n</section>\n</section>\n</section>"
  },
  {
    "path": "doc/manual/cli/commandline.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<chapter xml:id=\"commandLine\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>The <command>dmrconf</command> command line tool</title>\n</info>\n\n<para>\n  Beside the graphical user interface provided by <application>qdmr</application> (see \n  <xref linkend=\"gui\"/>), there is also a command line tool allowing to read/write codeplugs from \n  and to the radios. It is based on the same library called \n  <link xlink:href=\"https://dm3mat.darc.de/qdmr/libdmrconf\">libdmrconf</link> and thus provides the\n  same features like <application>qdmr</application>.\n</para>\n\n<para>\n  This chapter will briefly describe the command line tool and how it can be used to handle \n  codeplugs from the command line.\n</para>\n\n<para>\n  The command line tool might be helpful in cases, where the codeplug file (see \n  <xref linkend=\"codeplug\"/>) gets assembled by a script. Then the same script may\n  upload the codeplug to the radio using the command line tool.\n</para>\n\n<para>\n  Additionally to the the features of the GUI (see <xref linkend=\"gui\"/>), the command line tool\n  provides some features to analyze the memory representation of the binary codeplugs as well as \n  debugging their implementation.\n</para>\n\n<xi:include href=\"codeplug.xml\"/>\n<xi:include href=\"callsign.xml\"/>\n<xi:include href=\"various.xml\"/>\n<xi:include href=\"dangerzone.xml\"/>\n<!--<xi:include href=\"dmrconf.xml\"/>-->\n<!--<xi:include href=\"qdmr.xml\"/>-->\n</chapter>"
  },
  {
    "path": "doc/manual/cli/dangerzone.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"cmdDangerZone\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Danger zone</title>\n</info>\n\n<warning>\n  <para>\n    You are about to enter the land of pain. Continue on your own risk.\n  </para>\n</warning>\n\n<para>\n  Some radios are actually identical to others. They also identify themselves as a different radio.\n  An example for such a radio is the Retevis RT3S, this radio is simply a relabeled TyT MD-UV390. \n  The RT3S actually identifies itself as a MD-UV390. From the perspective of the CPS, these two \n  radios are indistinguishable. Consequently, <application>qdmr</application> and \n  <command>dmrconf</command> will always identify the RT3S as a MD-UV390.\n</para>\n\n<para>\n  There are, however, virtually identical radios. These are radios that actually identify themselves\n  as different models but the firmware, communication protocol and codeplug is basically identical \n  to another model. An example for such virtually identical models are the AnyTone AT-D868UV and \n  the BTech DMR-6X2. Each model identifies itself correspondingly and thus is distinguished by the\n  CPS. Some of these relationships between virtually identical models are known to \n  <application>qdmr</application> and <command>dmrconf</command>. In these cases, the CPS will \n  treat these radios as identical.\n</para>\n\n<para>\n  Some of these close relationships between models are not known to <command>dmrconf</command>. In \n  these cases, <command>dmrconf</command> will stop with an error that a radio is unknown although\n  it actually supported as a different model. In these rare cases, it is possible to override the\n  radio detection using the <option>--radio</option> option. \n</para>\n\n<para>\n  This option is usually used to specify the type whenever the radio model is not detected. This \n  option also overrides the model detection and thus allows to handle virtually identical radios.\n  For example, if the relation ship between the AT-D868UV and the DMR-6X2 would have not been \n  known to <command>dmrconf</command>, a codeplug could read anyway from the device by calling\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf read --radio=d868uv codeplug_6x2.yaml]]></programlisting>\n</informalexample>\n\n<para>\n  Here the radio detection (resulting the detection of a DMR-6X2) gets overridden and the radio is\n  handled as a AT-D868UV. \n</para>\n\n<para>\n  If you know of such virtually identical radios that <command>dmrconf</command> does not \n  recognize, consider filing an issue at \n  <link xlink:href=\"https://github.com/hmatuschek/qdmr/issues\">the bug tracker</link>.\n</para>\n\n<warning>\n  <para>\n    Of cause, handling a radio differently as it identifies itself may cause permanent damage to\n    the radio. So you should be very sure that the radios are actually identical when overriding\n    the radio detection routines.\n  </para>\n</warning>\n</section>\n"
  },
  {
    "path": "doc/manual/cli/various.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"cmdVarious\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Various features of <command>dmrconf</command></title>\n</info>\n\n<para>\n  Beside reading and writing codeplugs or writing the call-sign DB, there are some more commands \n  and features that mainly concern the debugging of the codeplug and call-sign DB encoding and \n  decoding. If you are interested in the codeplug internals, you may use these commands to study\n  them.\n</para>\n\n\n<section xml:id=\"cmdHelp\">\n<info><title>Getting help</title></info>\n\n<para>\n  As usual for command line tools, a brief help text about the commands and options gets written\n  to <varname>stdout</varname> when the <option>--help</option> or <option>-h</option> option is \n  passed. No other commands passed are executed then.\n</para>\n\n<para>\n  Similar to the <option>--help</option> option, it is possible to print the version number of \n  <command>dmrconf</command> using the <option>--version</option> or <option>-v</option> option.\n  Like for the <option>--help</option>, no other commands passed get executed.\n</para>\n\n<para>\n  To get a list of keys identifying radio models when specified using the <option>--radio</option>\n  option, <option>--list-radios</option> can be passed. This will print a small table to \n  <varname>stdout</varname> that lists the keys for each known radio as well as the model and\n  manufacturer name.\n</para>\n</section>\n\n\n<section xml:id=\"cmdDetect\">\n<info><title>Detecting the radio type</title></info>\n<para>\n  The command <command>detect</command> solely detects the radio. No data is written or read \n  from the device (except of the radio model information). This command can be used to check \n  whether a radio is detected correctly.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf detect]]></programlisting>\n</informalexample>\n\n<para>\n  This command will try to detect the connected radio. If a known radio is found, the model and\n  manufacturer name is written to <varname>stdout</varname>. If no radio is detected or if the \n  model is unknown or unsupported, an error message is written to <varname>stderr</varname>.\n</para>\n</section>\n\n\n<section xml:id=\"cmdInfo\">\n<info><title>Inspecting binary codeplugs</title></info>\n\n<para>\n  The <command>encode</command>, <command>encode-db</command> and <command>read</command> commands\n  can store the codeplug and call-sign DB in binary form in a DFU file. The generated file is a \n  valid DFU (device firmware update) file, that can be handled with other DFU tools.\n</para>\n\n<para>\n  A DFU file may contains several so-called images. Each image may contains several so-called \n  elements. The latter represents a segment of memory with an associated memory address. \n</para>\n\n<para>\n  The <command>info</command> command produces a hex-dump of the DFU file that is written to \n  <varname>stdout</varname>. It can then be inspected using <command>more</command> or \n  <command>less</command>.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[dmrconf info codeplug.dfu | less]]></programlisting>\n</informalexample>\n\n<para>\n  This example will generate a hex dump of the encoded codeplug in the specified DFU file\n  <filename>codeplug.dfu</filename>. The result is piped to <command>less</command> for easy \n  reading. The hex dump also prints some information about the file structure as well as\n  memory addresses. It also collapses repetitive memory sections (similar to \n  <command>hexdump -C</command>). To this end, this command is a helpful tool for debugging\n  the encoding of codeplugs and call-sign DBs.\n</para>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/anytone/aprs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extAnytoneFMAPRS\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>FM APRS settings extension</title>\n</info>\n\n<para>\n  This extensions allows to specify some additional settings for the FM APRS System. As AnyTone \n  devices allow only for one FM APRS system, these settings can be considered radio wide.\n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[positioning:\n  ...\n  - aprs:\n    ...\n    anytone:\n      txDelay: 60 ms\n      preWaveDelay: 0\n      passAll: false\n      reportPosition: false\n      reportMicE: false\n      reportObject: false\n      reportItem: false\n      reportMessage: false\n      reportWeather: false\n      reportNMEA: false\n      reportStatus: false\n      reportOther: false\n      frequencies:\n        - id: af1\n          name: APRS US\n          frequency: 144.39 MHz\n]]></programlisting>\n</example>\n\n<para>\n  The <trademark>AnyTone</trademark> FM APRS extension is a mapping named <token>anytone</token>. It\n  contains the device specific settings for that FM APRS system. AnyTone devices only allow for a \n  single FM APRS system.\n</para>\n\n\n<section>\n<info>\n  <title>FM APRS system attributes</title>\n</info>\n\n<variablelist>\n  <title>FM APRS system extension fields</title>\n  <varlistentry>\n    <term><token>txDelay</token></term>\n    <listitem>\n      <para>\n        A delay before the start of the transmission. Specified with a resolution of 20ms. The \n        default value is 60ms.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>preWaveDelay</token></term>\n    <listitem>\n      <para>\n        A delay between the start of the carrier and the actual APRS transmission. Default 0ms.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>passAll</token></term>\n    <listitem>\n      <para>\n        Basically disables the CRC check on received APRS messages.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>reportPosition</token><token>reportMicE</token><token>reportObject</token>\n    <token>reportItem</token><token>reportMessage</token><token>reportWeather</token>\n    <token>reportNMEA</token><token>reportStatus</token><token>reportOther</token></term>\n    <listitem>\n      <para>\n        A bunch of flags indicating what is being reported (?!?).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>frequencies</token></term>\n    <listitem>\n      <para>\n        While AnyTone only allows for a single set of parameters for the FM APRS transmission and \n        reception, it allows for several frequencies. The default frequency is specified by the \n        revert channel of the FM APRS system. \n      </para>\n      <para>\n        Seven additional frequencies might be specified through this list. Each entry is a mapping, \n        specifying a unique ID for the frequency, a name and the actual frequency. The id can then \n        be used to reference this FM APRS frequency in the AnyTone channel extension.\n      </para>\n    </listitem>\n  </varlistentry>\n  \n</variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/anytone/channel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extAnytoneChannel\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Channel extensions</title>\n</info>\n\n<para>\n  This extensions allows to set some device specific channel settings for \n  <trademark>AnyTone</trademark> devices. There are some general settings, valid for FM and DMR \n  channels as well some FM and DMR specific settings.\n</para>\n\n<example>\n  <title>Examples of the <trademark>AnyTone</trademark> extensions for FM and DMR channels.</title>\n  <programlisting><![CDATA[channels:\n  - dmr:\n    name: Example DMR channel \n    # ...\n    anytone:\n      frequencyCorrection: 0\n      handsFree: false\n      adaptiveTDMA: false\n      throughMode: false\n  - fm:\n    name: Example FM channel \n    # ...\n    anytone:\n      frequencyCorrection: 0\n      handsFree: false\n      rxCustomCTCSS: false\n      txCustomCTCSS: false\n      customCTCSS: 0.0\n      squelchMode: Carrier\n      scrambler: false\n]]></programlisting>\n</example>\n\n<section>\n<info>\n  <title>Common channel attributes</title>\n</info>\n\n<para>\n  This section describes the common channel attributes. These attributes are shared between FM\n  and DMR channels.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>frequencyCorrection</token></term>\n    <listitem>\n      <para>Specifies some sort of correction to the frequency in some unknown units. Consider \n        contacting me, if you know more details about it.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>handsFree</token></term>\n    <listitem>\n      <para>If <token>true</token>, the hands-free feature is enabled for this channel. The actual\n        configuration of the hands-free feature happens in the general settings extension. This \n        setting is only applicable to the <productname>AT-D578UV</productname> device.</para>\n    </listitem>\n  </varlistentry>  \n  <varlistentry>\n    <term><token>fmAPRSFrequency</token></term>\n    <listitem>\n      <para>Optional alternative FM APRS frequency specified in the AnyTone extension to the APRS\n        settings.</para>\n    </listitem>\n  </varlistentry>  \n  <varlistentry>\n    <term><token>aprsPTT</token></term>\n    <listitem>\n      <para>Enables/specifies if and when the position is send via APRS whenever the PTT is \n        pressed. Possible values are <token>Off</token>, <token>Start</token> and \n        <token>End</token>. </para>\n      <para>If a DMR APRS system is associated with this channel, \n        <token>Start</token> and <token>End</token> are synonymous. If and FM APRS system is \n        associated with this channel, the position is send at the beginning \n        (<token>Start</token>) or end (<token>End</token>) of a transmission. If \n        <token>Off</token> is set, the position will not be send along with every transmission.\n        In this case, the position might be send periodically, depending on the settings of the\n        position reporting system. </para>\n    </listitem>\n  </varlistentry>  \n</variablelist>\n</section>\n\n<section>\n  <info>\n    <title>FM channel attributes</title>\n  </info>\n\n  <para>\n    These attributes are only valid for FM channels.\n  </para>\n\n  <variablelist>\n    <varlistentry>\n      <term><token>rxCustomCTCSS</token></term>\n      <listitem>\n        <para>If <token>true</token>, the custom CTCSS frequency (see <token>customCTCSS</token>) \n          is used to control the squelch.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>txCustomCTCSS</token></term>\n      <listitem>\n        <para>If <token>true</token>, the custom CTCSS frequency (see <token>customCTCSS</token>) \n          is transmitted.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>customCTCSS</token></term>\n      <listitem>\n        <para>Specifies a custom CTCSS frequency in Hz. The frequency can be specified with a \n          resolution of 0.1Hz.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>squelchMode</token></term>\n      <listitem>\n        <para>Specifies the squelch mode. Must be one of <token>Carrier</token>, \n          <token>SubTone</token>, <token>OptSig</token>, <token>SubToneAndOptSig</token> or \n          <token>SubToneOrOptSig</token>.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>scrambler</token></term>\n      <listitem>\n        <para>If <token>true</token>, the analog scrambler is enabled. It is not legal to use \n          scramblers in HAM radio.</para>\n      </listitem>\n    </varlistentry>\n  </variablelist>\n</section>  \n\n<section>\n  <info><title>DMR channel attributes</title></info>\n\n  <para>This section describes the attributes, applicable to DMR channels on \n    <trademark>AnyTone</trademark> devices.</para>\n\n  <variablelist>\n    <varlistentry>\n      <term><token>adaptiveTDMA</token></term>\n      <listitem>\n        <para>If <token>true</token>, the adaptive TDMA mode is enabled. This makes only sense, if \n          <token>simplexTDMA</token> is enabled too. In this case, the radio is able to receive both\n          simplex TDMA as well as normal simplex DMR on the channel.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>throughMode</token></term>\n      <listitem>\n        <para>If <token>true</token>, the through mode is enabled. What ever that means. If you \n          know more about it, consider extending this section.</para>\n      </listitem>\n    </varlistentry>\n  </variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/anytone/contact.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extAnytoneContact\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>DMR contact extension</title>\n</info>\n\n<para>\n  This extensions allows to specify some DMR contact attributes for <trademark>AnyTone</trademark> devices. This extension\n  is only applicable to DMR (digital) contacts. \n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[contacts:\n  - dmr:\n    name: Example contact \n    number: 1234567\n    anytone:\n      alertType: Online\n]]></programlisting>\n</example>\n\n<para>\n  The <trademark>AnyTone</trademark> contact extension is a mapping named <token>anytone</token>. It contains the \n  device specific settings for that DMR contact.\n</para>\n\n\n<section>\n<info>\n  <title>Contact attributes</title>\n</info>\n\n<para>\n  For now, there is only one attribute allowing to override the alert type for a DMR contact.\n</para>\n\n<variablelist>\n  <title>Channel extension fields</title>\n  <varlistentry>\n    <term><token>alertType</token></term>\n    <listitem>\n      <para>\n        The alert type specifies the notification type when a call from that contact is received.\n        To this end, this field overrides the <token>ring</token> setting of the common  contact\n        settings. This attribute is either <token>None</token>, <token>Ring</token> or \n        <token>Online</token>. If <token>None</token> is set, the notification is disabled.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/anytone/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"extAnyTone\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title><trademark>AnyTone</trademark> Codeplug Extensions</title>\n</info>\n\n<para>\n  This chapter documents the extensions and settings specific to <trademark>AnyTone</trademark> radios. Please note, that\n  not all attributes are applicable to all devices.\n</para>\n\n<xi:include href=\"contact.xml\"/>\n<xi:include href=\"channel.xml\"/>\n<xi:include href=\"zone.xml\"/>\n<xi:include href=\"settings.xml\"/>\n<xi:include href=\"aprs.xml\"/>\n</section>"
  },
  {
    "path": "doc/manual/codeplug/anytone/settings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extAnytoneSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Settings extension</title>\n</info>\n\n<para>\n  This extensions allows to specify some device specific settings for\n   <trademark>AnyTone</trademark> devices. As there are a myriad of settings for these devices, the\n   settings extension is split into several sub-extensions, grouping the settings by domain.\n</para>\n\n<para>\n  The <trademark>AnyTone</trademark> settings extension is a mapping named <token>anytone</token>. \n  It contains the device specific settings for AnyTone devices.\n</para>\n\n<section>\n<info>\n  <title>General settings attributes</title>\n</info>\n\n<variablelist>\n  <title>General settings extension fields</title>\n  <varlistentry>\n    <term><token>subChannel</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the sub-channel is enabled.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>selectedVFO</token></term>\n    <listitem>\n      <para>\n        Specifies the currently selected VFO. Must be one of <token>A</token> or <token>B</token>,\n        selecting VFO A and B, respectively.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>modeA</token>, <token>modeB</token></term>\n    <listitem>\n      <para>\n        Specifies the VFO mode for VFOs A and B. Must be one of <token>Memory</token> or \n        <token>VFO</token>. The former selects the memory/channel mode while the latter selects the \n        VFO mode.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>zoneA</token>, <token>zoneB</token></term>\n    <listitem>\n      <para>\n        Specifies the current zones for VFO A and B. Must be a reference to a zone.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>vfoScanType</token></term>\n    <listitem>\n      <para>\n        Specifies scan-type for the VFO mode. Must be one of <token>Time</token>,\n        <token>Carrier</token> or <token>Stop</token>. Default is <token>Time</token>. If \n        <token>Time</token> is selected, the scan is stops for a specified period. If \n        <token>Carrier</token> is selected, the scan stops as long as a carrier is detected. If \n        <token>Stop</token> is selected, the scan stops entirely.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>minVFOScanFrequencyVHF</token>, <token>maxVFOScanFrequencyVHF</token>, \n      <token>minVFOScanFrequencyUHF</token>, <token>maxVFOScanFrequencyUHF</token></term>\n    <listitem>\n      <para>\n        Specifies the frequency ranges for the VFO scan in the VHF and UHF bands. These frequency \n        can be specified arbitrarily. If a unit-less floating point value is given, it is \n        interpreted as in MHz. If a unit-less integer value is given, it is interpreted as in Hz.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keepLastCaller</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the last caller ID is kept even, when the channel is changed. \n        Seriously, who needs that option? Just keep it or don't. Why is it so important? AnyTone\n        codeplugs are full of these unnecessary settings.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>vfoStep</token></term>\n    <listitem>\n      <para>\n        Specifies the VFO tuning step-size.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>steType</token>, <token>steFrequency</token>, <token>steDuration</token></term>\n    <listitem>\n      <para>\n        Specifies the squelch-tail elimination type, frequency and duration. The type must be one of \n        <token>Off</token>, <token>Silent</token>, <token>Deg120</token>, <token>Deg180</token> or \n        <token>Deg240</token>. The frequency is specified as a floating point number in Hz. The \n        duration is specified in ms.\n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n        <productname>DMR-6X2UV</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>tbstFrequency</token></term>\n    <listitem>\n      <para>\n        Specifies TBST/pilot-tone frequency used to enable/open a repeater. This should be one of \n        the common frequencies like 1000, 1450, 1750 and 2100 Hz.\n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n        <productname>DMR-6X2UV</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>proMode</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, enables the professional mode. If enabled, some menu options are \n        hidden. \n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname> and \n        <productname>DMR-6X2UV</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>maintainCallChannel</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, maintains the call-channel. Whatever that now means. If you find it\n        out, let it me know. \n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname> and \n        <productname>DMR-6X2UV</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>fan</token></term>\n    <listitem>\n      <para>\n        Specifies, what controls the fan of the <productname>D578UV</productname>. The must be \n        one of <token>PTT</token> (default), <token>Temperature</token> or \n        <token>Both</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bootSettings</token>, <token>powerSaveSettings</token>, <token>keySettings</token>, \n      <token>toneSettings</token>, <token>displaySettings</token>, <token>audioSettings</token>, \n      <token>menuSettings</token>, <token>autoRepeaterSettings</token>, <token>dmrSettings</token>, \n      <token>roamingSettings</token>, <token>gpsSettings</token>, \n      <token>simplexRepeaterSettings</token></term>\n    <listitem>\n      <para>\n        Many more funny settings, grouped by domain. For details, see the descriptions below.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Boot settings</title></info>\n<para>\n  Collects some settings, that control the startup of the radio. These settings are collected \n  under the <token>bootSettings</token> key within the <token>anytoneSettings</token> entry.\n</para>\n\n<variablelist>\n  <title>Boot settings extension fields</title>\n  <varlistentry>\n    <term><token>bootDisplay</token></term>\n    <listitem>\n      <para>\n        Specifies what is seen on the display during boot. Must be one of <token>Default</token>, \n        <token>CustomText</token> or <token>CustomImage</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bootPasswordEnabled</token>, <token>bootPassword</token></term>\n    <listitem>\n      <para>\n        Enables/disables and specifies the boot password. The boot password is a string of up to 8 \n        number chars.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>defaultChannel</token>, <token>zoneA</token>, <token>channelA</token>, \n      <token>zoneB</token>, <token>channelB</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, <token>defaultChannel</token> enables the specified zones and \n        channels to be set on startup. <token>zoneA</token> and <token>zoneB</token> specify the \n        startup zone for VFOs A and B, respectively, and must reference a defined zone. While\n        <token>channelA</token> and <token>channelB</token> specify the startup channels for each\n        VFO. If no channels are defined, the VFO is chosen.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>priorityZoneA</token>, <token>priorityZoneB</token></term>\n    <listitem>\n      <para>\n        Specifies the priority zones for each VFO. These must reference a defined zone. \n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>defaultRoamingZone</token></term>\n    <listitem>\n      <para>\n        Specifies the startup roaming zone. Must refer to a defined roaming zone.\n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname> and <productname>AT-D578UV</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>gpsCheck</token></term>\n    <listitem>\n      <para>\n        Enables the GPS check on boot.\n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname> and \n        <productname>AT-D878UV II</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>reset</token></term>\n    <listitem>\n      <para>\n        Enables a MCU reset on boot.\n      </para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname> and\n        <productname>AT-D878UV II</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Power-save settings</title></info>\n<para>\n  Collects some settings, that configure the power-save features. These settings are collected under\n  the <token>powerSaveSettings</token> key within the <token>anytoneSettings</token> entry.\n</para>\n\n<variablelist>\n  <title>Power-save setting extension fields</title>\n  <varlistentry>\n    <term><token>autoShutdown</token></term>\n    <listitem>\n      <para> Specifies the time-out for the automatic shut down. If <token>0</token>, the automatic \n      shut-down is disabled. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>resetAutoShutdownOnCall</token></term>\n    <listitem>\n      <para>If <token>true</token>, the automatic showdown timer is reset on every call.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerSave</token></term>\n    <listitem>\n      <para>Specifies the power-save mode. Must be one of <token>Off</token>, <token>Save50</token>\n      or <token>Save66</token>. The latter two will save 50% or 66%, respectively.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>atpc</token></term>\n    <listitem>\n      <para>Enables the ATPC (Adaptive Transmission Power Control). It reduces the transmission \n      power, if a strong signal is received.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Keypad settings</title></info>\n<para>\n  Collects some settings, that configure the keypad, lock etc. These settings are collected \n  under the <token>keySettings</token> key within the <token>anytoneSettings</token> entry.\n</para>\n<para>\n  This extension also allows to assign specific functions to the programmable function keys on the \n  radio. These are the side-keys, top-key and the front buttons labeled <keycap>P1</keycap> and \n  <keycap>P2</keycap>. The following list shows all functions, that can be assigned to each of these \n  buttons. Some function are not available on all devices.\n</para>\n\n<variablelist xml:id=\"extAnytoneSettingsKeyFunctions\">\n  <title>Key functions</title>\n  <varlistentry>\n    <term><token>Off</token></term>\n    <listitem><para>No function at all.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Voltage</token></term>\n    <listitem><para>Shows the battery voltage. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Power</token></term>\n    <listitem><para>Toggles though the power settings. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Repeater</token></term>\n    <listitem><para>Toggles the talk-around feature. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Reverse</token></term>\n    <listitem><para>Swaps the RX and TX frequencies. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Encryption</token></term>\n    <listitem><para>Shows the encryption key selection. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Call</token></term>\n    <listitem><para>Performs an FM call with the configured DMTF/two-tone/five-tone ID. Applicable \n      to <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>VOX</token></term>\n    <listitem><para>Shows the VOX level selection. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleVFO</token></term>\n    <listitem><para>Toggles between VFO and memory/channel mode. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SubPTT</token></term>\n    <listitem><para>PTT for the sub channel. Only applicable for <keycap>PF1</keycap>, \n      <keycap>PF2</keycap> and <keycap>PF3</keycap> keys. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Scan</token></term>\n    <listitem><para>Starts/stops a scan. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>WFM</token></term>\n    <listitem><para>Toggles the WFM (or Air band) receiver. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Alarm</token></term>\n    <listitem><para>Starts/stops an alarm call. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>RecordSwitch</token></term>\n    <listitem><para>Enables/disables recoding function. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Record</token></term>\n    <listitem><para>Start/stops a recoding. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SMS</token></term>\n    <listitem><para>Start writing an SMS. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Dial</token></term>\n    <listitem><para>Start manual dial. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>GPSInformation</token></term>\n    <listitem><para>Applicable to <productname>AT-D868UV</productname>, \n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para> </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Monitor</token></term>\n    <listitem><para>Enables the (FM) monitor. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleMainChannel</token></term>\n    <listitem><para>Toggles between VFOs A and B for the main channel. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>HotKey1</token>, <token>HotKey2</token>, <token>HotKey3</token>, \n      <token>HotKey4</token>, <token>HotKey5</token>, <token>HotKey6</token></term>\n    <listitem><para>Triggers one of the programmed hot-key function. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>WorkAlone</token></term>\n    <listitem><para>Toggles the lone-worker mode. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SkipChannel</token></term>\n    <listitem><para>During a scan, use this function to skip the current channel. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>DigitalMonitor</token></term>\n    <listitem><para>Toggles the digital monitor. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SubChannel</token></term>\n    <listitem><para>Enables/disables the sub-channel. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>PriorityZone</token></term>\n    <listitem><para>Change to the priority zone. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>VFOScan</token></term>\n    <listitem><para>Toggles the VFO scan. Applicable to <productname>AT-D868UV</productname>, \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n      <productname>AT-D578UV</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>MICSoundQuality</token></term>\n    <listitem><para>Toggle <quote>enhanced</quote> sound mode in DMR mode. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>LastCallReply</token></term>\n    <listitem><para>Allows to answer the last call, after the hang-time has passed. Applicable to \n      <productname>AT-D868UVE</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D578UV</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ChannelType</token></term>\n    <listitem><para>Change the channel type. Applicable to <productname>AT-D868UV</productname>, \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n      <productname>AT-D578UV</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SimplexRepeater</token></term>\n    <listitem><para>Enables/disables the simplex repeater feature. Applicable to \n      <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Ranging</token></term>\n    <listitem><para>Applicable to <productname>AT-D868UV</productname>, \n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname> </para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Roaming</token></term>\n    <listitem><para>Manually starts roaming. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>. </para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ChannelRanging</token></term>\n    <listitem><para>Applicable to <productname>AT-D868UV</productname>, \n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname> </para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>MaxVolume</token></term>\n    <listitem><para>Shows the maximum volume setting. Applicable to \n      <productname>AT-D868UV</productname>, <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Slot</token></term>\n    <listitem><para>Changes the time-slot, <productname>AT-D868UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Squelch</token></term>\n    <listitem><para>Shows squelch settings. Applicable to \n      <productname>AT-D578UV</productname> and <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>APRSTypeSwitch</token></term>\n    <listitem><para>Applicable to <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Zone</token></term>\n    <listitem><para>Shows the zone selection dialog. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>RoamingSet</token></term>\n    <listitem><para>Shows the roaming settings(?). Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>APRSSet</token></term>\n    <listitem><para>Opens APRS settings. Applicable to <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Mute</token></term>\n    <listitem><para>Mutes the radio for a specified interval. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>MuteA</token>, <token>MuteB</token></term>\n    <listitem><para>Mutes the VFO A/B for a specified interval. Applicable to \n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>XBandRepeater</token></term>\n    <listitem><para>Enables the cross-band repeater function. Applicable to \n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Speaker</token></term>\n    <listitem><para>Applicable to <productname>AT-D578UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>CtcssDcsSet</token></term>\n    <listitem><para>Shows the CTCSS/DCS settings for FM channels. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>TBSTSend</token></term>\n    <listitem><para>Sends the TBST tone. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname>. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>Bluetooth</token></term>\n    <listitem><para>Enables/disables bluetooth function. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>,\n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>GPS</token></term>\n    <listitem><para>Enables/disables GPS. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and\n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ChannelName</token></term>\n    <listitem><para>Toggle channel name and frequency display. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>,\n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>CDTScan</token></term>\n    <listitem><para>Starts CTCSS/DCS scan for FM channel. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>APRSSend</token></term>\n    <listitem><para>Transmits the positioning information. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname> and\n      <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>APRSInfo</token></term>\n    <listitem><para>Shows received APRS information. Applicable to \n      <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>,\n      <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>GPSRoaming</token></term>\n    <listitem><para>Applicable to <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>NoiseReductionTX</token></term>\n    <listitem><para>Applicable to <productname>AT-D578UV</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>DIMShut</token></term>\n    <listitem><para>Unknown function. Applicable to <productname>DMR-6X2</productname>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>SatPredict</token></term>\n    <listitem><para>Starts the prediction of passes of programmed satellites. Applicable to\n      <productname>DMR-6X2</productname>.</para></listitem>\n  </varlistentry>\n</variablelist>\n\n<para>\n  The following list contains all keypad setting fields.\n</para>\n\n<variablelist>\n  <title>Keypad settings extension fields</title>\n  <varlistentry>\n    <term><token>funcKey1Short</token>, <token>funcKey1Long</token>, <token>funcKey2Short</token>, \n      <token>funcKey2Long</token>, <token>funcKey3Short</token>, <token>funcKey3Long</token>, \n      <token>funcKey4Short</token>, <token>funcKey4Long</token>, <token>funcKey5Short</token>, \n      <token>funcKey5Long</token>, <token>funcKey6Short</token>, <token>funcKey6Long</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the function keys <keycap>P1</keycap>-<keycap>P6</keycap>\n        for a short and long press. Must be one of the items in the key function list above. The \n        function keys 1-6 are labeled <keycap>P1</keycap>-<keycap>P6</keycap> and are located either \n        below or next to the display.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKeyAShort</token>, <token>funcKeyALong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the programmable function key <keycap>PF1</keycap> or \n        <keycap>A</keycap>. This button is located right below the PTT button (on handheld radios) \n        or labeled <keycap>A</keycap> on the hand microphone (<productname>AT-D578UV</productname>).\n      </para>\n    </listitem>\n  </varlistentry>\n    <varlistentry>\n    <term><token>funcKeyBShort</token>, <token>funcKeyBLong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the programmable function key <keycap>PF2</keycap> or \n        <keycap>B</keycap>. This button is the second one below the PTT button (on handheld radios) \n        or labeled <keycap>B</keycap> on the hand microphone (<productname>AT-D578UV</productname>).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKeyCShort</token>, <token>funcKeyCLong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the programmable function key <keycap>PF3</keycap> or \n        <keycap>C</keycap>. THis button is the colorful one on the top (on handheld radios) or \n        labeled <keycap>C</keycap> on the hand microphone (<productname>AT-D578UV</productname>).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKeyDShort</token>, <token>funcKeyDLong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the programmable function key <keycap>D</keycap>. \n        This button is located on the hand microphone (<productname>AT-D578UV</productname> only).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKnobShort</token>, <token>funcKnobLong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the programmable channel/frequency knob. \n        (<productname>AT-D578UV</productname> only).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>longPressDuration</token></term>\n    <listitem>\n      <para>\n        Specifies the minimum duration of a <quote>long press</quote> on one of the programmable \n        function keys. \n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>upDownKeys</token></term>\n    <listitem>\n      <para>\n        Specifies the functions associated with the up/down buttons on the top of the handset. This\n        must either be <token>Channel</token> or <token>Volume</token>. \n        (<productname>AT-D578UV</productname> only).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>autoKeyLock</token></term>\n    <listitem>\n      <para>\n        Enables/disables the automatic key lock.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>knobLock</token>, <token>keypadLock</token>, <token>sideKeysLock</token></term>\n    <listitem>\n      <para>\n        Specifies, whether the knob, keypad and side-keys are locked, when the key-lock is enabled.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>forcedKeyLock</token></term>\n    <listitem>\n      <para>\n        Don't use. Prevents the manual key-lock release. Only applicable to commercial applications.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Tone settings</title></info>\n<para>\n  Collects some settings, that configure signal tones. These settings are collected \n  under the <token>toneSettings</token> key within the <token>anytoneSettings</token> entry.\n</para>\n\n<variablelist>\n  <title>Tone settings extension fields</title>\n  <varlistentry>\n    <term><token>keyTone</token></term>\n    <listitem><para>Enables the key-pad tones. Should be disabled.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keyToneLevel</token></term>\n    <listitem><para>Specifies the key-tone level, 0 means user adjustable.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>smsAlert</token></term>\n    <listitem><para>Enables the SMS alert tone.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callAlert</token></term>\n    <listitem><para>Enables the call alert tone. See also <token>callMelody</token>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dmrTalkPermit</token>, <token>fmTalkPermit</token></term>\n    <listitem><para>Enables the DMR talk permit tone.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dmrReset</token></term>\n    <listitem><para>Enables the DMR reset tone. See also <token>resetMelody</token>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dmrIdle</token>, <token>fmIdle</token></term>\n    <listitem><para>Enables the idle tone (for DMR and FM channels). See also <token>idleMelody</token>.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>startup</token></term>\n    <listitem><para>Enables a startup tone.</para></listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>tot</token></term>\n    <listitem>\n      <para>Transmit time-out warning tone.</para>\n      <para> Applicable to <productname>DMR-6X2 PRO</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>wxAlarm</token></term>\n    <listitem>\n      <para>Enables the weather alarm.</para>\n      <para> Applicable to <productname>DMR-6X2 PRO</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callMelody</token>, <token>idleMelody</token>, <token>resetMelody</token>, \n      <token>callEndMelody</token></term>\n    <listitem><para>Specifies the melody played on each of these occasions. These melodies can \n    contain up to 5 notes (and pauses). For the sake of simplicity, qdmr supports the LilyPond \n    musical notation for these melodies. E.g., <code>a8 b e2 des4 d</code>. </para></listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Display settings</title></info>\n<para>\n  Collects all display settings. These settings are collected under the \n  <token>displaySettings</token> key within the <token>anytoneSettings</token> entry.\n</para>\n\n<variablelist>\n  <title>Display settings extension fields</title>\n  <varlistentry>\n    <term><token>displayFrequency</token></term>\n    <listitem>\n      <para>If true, the channel frequency is shown instead of the channel name.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>brightness</token></term>\n    <listitem>\n      <para>Specifies the brightness of the backlight in a range 1-10.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>backlightDuration</token>, <token>backlightDurationRX</token>, \n      <token>backlightDurationTX</token></term>\n    <listitem>\n      <para>Specifies the back-light duration for several occasions. Not all devices support all \n        settings. None is supported by all devices. It is a mess.</para>\n      <para><token>backlightDuration</token> specifies how long the backlight stays on, after any \n        event/user interaction. Usually, after a button has been pressed. This value can be set to\n        <token>infinity</token> to specify, that the backlight stays on indefinitely. The feature\n        is supported by <productname>AT-D868UVE</productname>, <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>DMR-6X2</productname> and \n        <productname>DMR-6X2 PRO</productname></para>\n      <para><token>backlightDurationRX</token> specifies, how long the backlight stays on during\n        reception. This value can be set to <token>infinity</token> to specify, that the backlight \n        stays on during the entire reception period. This feature is supported by \n        <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname>, \n        <productname>DMR-6X2</productname> and <productname>DMR-6X2 PRO</productname>.</para>\n      <para><token>backlightDurationTX</token> specifies, how long the backlight stays on during\n        transmission. This value can be set to <token>o</token> to disable the backlight during \n        transmission. This feature is supported by <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n        <productname>AT-D578UV III</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>customChannelBackground</token></term>\n    <listitem>\n      <para>If <token>true</token>, the custom channel background is enabled.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>volumeChangePrompt</token>, <token>callEndPrompt</token></term>\n    <listitem>\n      <para>If <token>true</token> the volume change and call-end prompts are shown.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>showClock</token>, <token>showCall</token>, <token>showContact</token>,\n      <token>showChannelNumber</token>, <token>showColorCode</token>, <token>showTimeSlot</token>,\n      <token>showChannelType</token>, <token>showLastHeard</token></term>\n    <listitem>\n      <para>Enables/disables various display elements.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>showGlobalChannelNumber</token></term>\n    <listitem>\n      <para>If enabled, the global channel number is shown. If disabled, the channel number within\n      the zone is shown.</para>\n      <para> Applicable to <productname>DMR-6X2 PRO</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>lastCallerDisplay</token></term>\n    <listitem>\n      <para>Specifies how the last caller is shown. Must be one of <token>Off</token>, \n      <token>ID</token>, <token>Call</token> or <token>Both</token></para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callColor</token></term>\n    <listitem>\n      <para>Specifies the call color. Must be one of <token>White</token>, <token>Black</token>, \n      <token>Orange</token>, <token>Red</token>, <token>Yellow</token>, <token>Green</token>, \n      <token>Turquoise</token>, <token>Blue</token>. Please note, that not all devices allow for \n      all colors. Especially the <productname>AT-D868UV</productname> has only limited colors.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>standbyTextColor</token>, <token>standbyBackgroundColor</token></term>\n    <listitem>\n      <para>Specifies the text or background color in stand-by. Must be one of <token>White</token>, \n      <token>Black</token>, <token>Orange</token>, <token>Red</token>, <token>Yellow</token>, \n      <token>Green</token>, <token>Turquoise</token>, <token>Blue</token>. Please note, that not all\n      devices allow for all colors. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channelNameColor</token>, <token>channelBNameColor</token></term>\n    <listitem>\n      <para>Specifies the color of the channel name. Must be one of <token>White</token>, \n      <token>Black</token>, <token>Orange</token>, <token>Red</token>, <token>Yellow</token>, \n      <token>Green</token>, <token>Turquoise</token>, <token>Blue</token>. Please note, that not all\n      devices allow for all colors. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname> and <productname>AT-D578UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>zoneNameColor</token>, <token>zoneBNameColor</token></term>\n    <listitem>\n      <para>Specifies the color of the channel name. Must be one of <token>White</token>, \n      <token>Black</token>, <token>Orange</token>, <token>Red</token>, <token>Yellow</token>, \n      <token>Green</token>, <token>Turquoise</token>, <token>Blue</token>. Please note, that not all\n      devices allow for all colors. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname> and <productname>AT-D578UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>language</token></term>\n    <listitem>\n      <para>Specifies the UI language. Must be one of <token>English</token> or \n      <token>German</token>. Applicable to <productname>AT-D878UV</productname>, \n      <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n      <productname>DMR-6X2UV</productname>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dateFormat</token></term>\n    <listitem>\n      <para>Specifies the date format. Must be one of <token>YearFirst</token> (i.e., yyyy/mm/dd) or \n        <token>DayFirst</token> (i.e., dd/mm/yyyy). Obviously, they got everything covered.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Audio settings</title></info>\n<para>\n  This entry collects all audio-related settings. These settings are collected under the \n  <token>audioSettings</token> key within the <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n<title>Audio settings extension fields</title>\n<varlistentry>\n  <term><token>voxDelay</token></term>\n  <listitem>\n    <para>An interval specifying the delay to activate the VOX. Stored in ms resolution. </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>voxSource</token></term>\n  <listitem>\n    <para>Specifies the source for the VOX. Must be one of <token>Internal</token>, \n    <token>External</token> or <token>Both</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>recording</token></term>\n  <listitem>\n    <para>If <token>true</token>, recording is enabled.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>enhance</token></term>\n  <listitem>\n    <para>If <token>true</token>, enabled some TX audio enhancement. I don't know what it does nor \n    if it is reasonable to enable.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>muteDelay</token></term>\n  <listitem>\n    <para>Specifies some muting delay in minutes.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>maxVolume</token>, <token>maxHeadPhoneVolume</token></term>\n  <listitem>\n    <para>Specifies the maximum speaker and headphone volume.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>enableFMMicGain</token>, <token>fmMicGain</token></term>\n  <listitem>\n    <para>Enables and sets a separate microphone gain for FM. If disabled, the global mic gain \n    is used for both, DMR and FM.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>speaker</token></term>\n  <listitem>\n    <para>\n      Specifies, which speaker are enabled. Must be one of <token>Handset</token>, \n      <token>Radio</token> (default) or <token>Both</token>. This only applies to\n      <productname>AT-D578UV</productname>.\n    </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>handsetSpeaker</token></term>\n  <listitem>\n    <para>\n      Specifies the source for the handset speaker, if it is enabled. Must be one of \n      <token>MainChannel</token> or <token>SubChannel</token>. This only applies to\n      <productname>AT-D578UV</productname>\n    </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>handsetType</token></term>\n  <listitem>\n    <para>\n      Specifies the type of handset. This must be one of <token>Anytone</token> or \n      <token>Generic</token>. This only applies to <productname>AT-D578UV</productname>.\n    </para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Menu settings</title></info>\n<para>\n    This entry collects all menu-related settings (there aren't many). These settings are collected \n    under the <token>menuSettings</token> key within the <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n<title>Menu settings extension fields</title>\n<varlistentry>\n  <term><token>duration</token></term>\n  <listitem>\n    <para>Specifies the duration, the menu is shown.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>separator</token></term>\n  <listitem>\n    <para>If <token>true</token>, the menu items are separated by a line. Again WTF AnyTone? Why \n    the hack is an option needed for this? For me, this is just another useless option to check on \n    every firmware update.</para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Auto-Repeater settings</title></info>\n<para>\n  The auto-repeater settings of anytone-devices is quiet complex. It allows to specify rules, in \n  which frequency ranges some auto-repeater settings are applied referencing a particular offset\n  for that rule. These offsets are specified within a separate list of frequency offsets. \n</para>\n<para>\n  These settings are needlessly complicated. It would be sufficient to specify a range and offset. \n  AnyTone, however, decided that this would be way to simple and user friendly. However, fitting \n  their concept into qdmr's concept of references to objects, makes it even worse. You will see.\n</para>\n<para>\n  The auto-repeater settings are specified within the <token>autoRepeaterSettings</token> item\n  within the <token>anytoneSettings</token> item.\n</para>\n\n<variablelist>\n<title>Auto-repeater extension fields</title>\n<varlistentry>\n  <term><token>directionA</token>, <token>directionB</token></term>\n  <listitem>\n    <para>Specifies the direction of the repeater offset for VFO A and B. I have absolutely no \n    clue, why this depends on the VFO and not on the BAND (VHF/UHF) or even per offset. Only the \n    wise people at AnyTone will know the answer.</para>\n    <para>However, must be one of <token>Off</token>, <token>Positive</token> or \n    <token>Negative</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>vhfMin</token>, <token>vhfMax</token>, <token>uhfMin</token>, <token>uhfMax</token>,\n    <token>vhf2Min</token>, <token>vhf2Max</token>, <token>uhf2Min</token>, <token>uhf2Max</token></term>\n  <listitem>\n    <para>Specifies the VHF and UHF frequency range for which the auto-repeater feature is defined. \n    These can be any frequencies by the way.</para>\n    <para>The <token>vhf2</token> and <token>uhf2</token> \n    variants are only present for <productname>AT-D878UV</productname>, \n    <productname>AT-D878UV II</productname> and <productname>AT-D578UV</productname> radios and \n    allow for a second pair of frequency ranges with a different offset.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>vhf</token>, <token>uhf</token>, <token>vhf2</token>, <token>uhf2</token></term>\n  <listitem>\n    <para>References the offset for the VHF and UHF frequency range specified above. This must be \n    a reference to one of the auto-repeater offsets defined below. </para>\n    <para>The <token>vhf2</token> and <token>uhf2</token> references are only present for \n    <productname>AT-D878UV</productname>, <productname>AT-D878UV II</productname> and \n    <productname>AT-D578UV</productname> radios and allow for a different offset.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>offset</token></term>\n  <listitem>\n    <para>A list of auto-repeater offsets. As qdmr only allows references to named objects, they \n    must be relatively complex. Each entry has an ID (to reference the offset), name (because qdmr \n    objects have names) and the actual offset frequency. See below for a description of these \n    elements.</para>\n  </listitem>\n</varlistentry>\n</variablelist>\n\n<section>\n<info><title>Auto-repeater offsets</title></info>\n<para>\n  As mentioned above, qdmr requires any object referenced within the codeplug to be a proper\n  object. That is with an ID and name. This makes sense, as almost all referenced elements within \n  the codeplug are complex objects and have a display name. However, AnyTone decided to reference a \n  mere offset frequency. Consequently, you have to give names to them in qdmr. Sorry.\n</para>\n<para>\n  The below you find a list with the properties of the offset frequencies.\n</para>\n\n<variablelist>\n<title>Offset frequency fields</title>\n<varlistentry>\n  <term><token>id</token></term>\n  <listitem>\n    <para>Specifies the unique ID of the offset frequency. Used to reference it.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>name</token></term>\n  <listitem>\n    <para>Specifies the name of the offset frequency. Any string is valid and not needed. However, \n    references are shown in qdmr with their name. So don't keep that field empty.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>offset</token></term>\n  <listitem>\n    <para>Specifies the actual frequency offset. </para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<example>\n<title>An example for two auto-repeater frequency ranges active only on VFO A.</title>\n <programlisting><![CDATA[anytoneSettings:\n  # ...  \n  autoRepeaterSettings:\n    directionA: Negative   # <- Only on VFO A. Because.\n    directionB: Off\n    # VHF range \n    vhfMin: 145.569 MHz\n    vhfMax: 145.794 MHz\n    vhf: offsetVHF\n    # UHF range\n    uhfMin: 438.55 MHz\n    uhfMax: 439.4375 MHz\n    uhf: offsetUHF\n    # Some generic but disabled ranges. \n    vhf2Min: 136 MHz\n    vhf2Max: 174 MHz\n    vhf2: ~\n    uhf2Min: 400 MHz\n    uhf2Max: 480 MHz\n    uhf2: ~\n    # The offsets\n    offset:\n      - id: offsetVHF\n        name: VHF\n        offset: 600 kHz\n      - id: offsetUHF\n        name: UHF\n        offset: 7.6 Mhz\n  # ...  \n]]></programlisting>\n</example>\n</section>\n\n<section>\n<info><title>DMR related settings</title></info>\n<para>\n  Some DMR related settings. Some of these should not be touched.\n</para>\n\n<variablelist>\n<varlistentry>\n  <term><token>groupCallHangTime</token>, <token>privateCallHangTime</token>, \n    <token>manualGroupCallHangTime</token>, <token>manualPrivateCallHangTime</token></term>\n  <listitem>\n    <para>Specifies the group and private call hang time. That is, the time, within you could answer \n    a call by pressing the PTT, even if the group or private call is not the default transmit \n    contact of the channel.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>preWaveDelay</token>, <token>wakeHeadPeriod</token></term>\n  <listitem>\n    <para>Don't touch. Should both be 100ms.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>filterOwnID</token></term>\n  <listitem>\n    <para>If <token>true</token>, your own ID is not shown in the call lists.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>smsFormat</token></term>\n  <listitem>\n    <para>Specifies the SMS format. Must be one of <token>Motorola</token>, <token>Hytera</token>, \n    <token>DMR</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>monitorSlotMatch</token></term>\n  <listitem>\n    <para>Specifies if and how the DMR monitor will match the time slot of the current channel. Must\n    be one of <token>Off</token>, <token>Single</token>, <token>Both</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>monitorColorCodeMatch</token></term>\n  <listitem>\n    <para>If <token>true</token>, the DMR monitor will match the color code of the current \n    channel.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>monitorIDMatch</token></term>\n  <listitem>\n    <para>If <token>true</token>, the DMR monitor will match the DMR IDs to the group list of the \n    current channel.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>monitorTimeSlotHold</token></term>\n  <listitem>\n    <para>If <token>true</token>, the DMR monitor will hold the time slot first received on.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>sendTalkerAlias</token></term>\n  <listitem>\n    <para>If enabled, sends the radio name as talker alias over the air.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>takerAliasSource</token></term>\n  <listitem>\n    <para>Specifies the source for the talker alias display. Must be one of <token>Off</token>, \n      <token>UserDB</token> or <token>Air</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>talkerAliasEncoding</token></term>\n  <listitem>\n    <para>Specifies the encoding of the taker alias. Must be one of <token>ISO8</token>,\n      <token>ISO7</token> or <token>Unicode</token>. </para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>GPS settings</title></info>\n<para>\n  This section collects settings concerning GPS position reporting. These settings are \n  collected under the <token>gpsSettings</token> key within the <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n<varlistentry>\n  <term><token>timeZone</token></term>\n  <listitem>\n    <para>Specifies the time-zone for the device. Any IANA time-zone ID is valid here. E.g., \n    <code>Europe/Paris</code> or <code>UTC+5</code>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>reportPosition</token></term>\n  <listitem>\n    <para>If <token>true</token>, GPS position reporting is enabled.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>updatePeriod</token></term>\n  <listitem>\n    <para>Specifies the update/reporting interval in seconds. Any interval specification is valid \n    here. E.g., <code>5 min</code> or <code>360 s</code>.</para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Roaming settings</title></info>\n<para>\n  This section collects settings concerning the roaming. These settings are collected \n  under the <token>roamingSettings</token> key within the <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n<varlistentry>\n  <term><token>autoRoam</token></term>\n  <listitem>\n    <para>If <token>true</token>, the auto-roaming is enabled. If <token>false</token>, the roaming\n    must be started manually.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>autoRoamPeriod</token></term>\n  <listitem>\n    <para>Specifies the auto-roaming period in minutes. Any interval specification is valid here. \n    E.g., <code>30 min</code> or <code>1 h</code>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>autoRoamDelay</token></term>\n  <listitem>\n    <para>Specifies some additional delay in seconds before the auto-roaming is started. Any \n    interval specification is valid here like <code>60 s</code> or <code>1 min</code>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>roamStart</token>,<token>roamReturn</token></term>\n  <listitem>\n    <para>Specifies the roaming start/end condition. Must be one of <token>Periodic</token> or \n    <token>OutOfRange</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>rangeCheck</token></term>\n  <listitem>\n    <para>If <token>true</token>, the periodic range check is enabled.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>checkInterval</token></term>\n  <listitem>\n    <para>Specifies the range check period in seconds. Any interval specification is valid here. \n    E.g., <code>3 min</code> or <code>180 s</code>. </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>retryCount</token></term>\n  <listitem>\n    <para>Number of retries to connect to a repeater before giving up and starting the roaming.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>outOfRangeAlert</token></term>\n  <listitem>\n    <para>Specifies the alert type for the out-of-range notification. Must be one of \n    <token>Off</token>, <token>Bell</token> or <token>Voice</token></para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>notification</token></term>\n  <listitem>\n    <para>If <token>true</token>, the repeater check notification is enabled.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>notificationCount</token></term>\n  <listitem>\n    <para>The number of repeater check notifications.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>defaultZone</token></term>\n  <listitem>\n    <para>Specifies the default roaming zone. Must be a roaming zone ID or \n    <token>null</token>.</para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>gpsRoaming</token></term>\n  <listitem>\n    <para>If <token>true</token>, GPS based roaming is enabled.</para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Bluetooth settings</title></info>\n<para>\n  This section collects settings concerning bluetooth feature. These settings are \n  collected under the <token>bluetoothSettings</token> key within the \n  <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>enabled</token></term>\n    <listitem>\n      <para>If <token>true</token>, the bluetooth feature is enabled.</para>\n      <para>\n        This option is applicable to <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>pttLatch</token></term>\n    <listitem>\n      <para>If <token>true</token>, the bluetooth PTT button latches.</para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n        <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>pttSleepTimer</token></term>\n    <listitem>\n      <para>Specifies the timeout for the sleep mode of the bluetooth PTT button.</para>\n      <para>\n        This option is applicable to <productname>AT-D878UV</productname>, \n        <productname>AT-D878UV II</productname>, <productname>AT-D578UV</productname> and \n        <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>internalSpeaker</token>, <token>internalMic</token></term>\n    <listitem>\n      <para>If <token>true</token>, the internal speaker and or mic stays active, if Bluetooth is \n        enabled.</para>\n      <para>\n        This option is applicable to <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>micGain</token>, <token>speakerGain</token></term>\n    <listitem>\n      <para>\n        Specifies the bluetooth mic and speaker gain. The actual range may depend on the device.\n        For the DMR-6X2 PRO valid values are 0-4.\n      </para>\n      <para>\n        This option is applicable to <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>hold</token>, <token>holdDelay</token></term>\n    <listitem>\n      <para>\n        Specifies the bluetooth hold duration or delay. Unfortunately, I have absolutely no \n        clue, that this setting is all about.\n      </para>\n      <para>\n        This option is applicable to <productname>DMR-6X2UV PRO</productname> devices.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n<section>\n<info><title>Bluetooth Handset <productname>BT-01</productname></title></info>\n<para>\n  The <productname>AT-D578UV</productname> supports a bluetooth handset, that can control the \n  radio. It is called <productname>BT-01</productname> and looks a bit like a small\n  <productname>AT-D878UV</productname>. There are plenty of settings for this device, stored in \n  the <productname>AT-D578UV</productname> codeplug. They are collected as a sub-setting under \n  <token>handset</token> within the <token>bluetooth</token> settings.\n</para>\n<variablelist>\n  <varlistentry>\n    <term><token>volumeLevelA</token>, <token>volumeLevelB</token></term>\n    <listitem>\n      <para>\n        Specifies the volume for VFO A and B respectively. Must be a value in [0, 10], where 0 \n        means off.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>micGain</token></term>\n    <listitem>\n      <para>\n        Specifies the microphone gain setting for the handset. Must be a value in [1,10].\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>squelch</token></term>\n    <listitem>\n      <para>\n        Specifies the squelch sensitivity. Must be a value in [0,10], where 0 means open.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>txNoiseReduction</token></term>\n    <listitem>\n      <para>\n        Specifies the transmit noise reduction (whatever that means). Must be a value in [0, 10], \n        where 0 means disabled.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>voxLevel</token></term>\n    <listitem>\n      <para>\n        Specifies the VOX sensitivity level. Must be a value in [0,10], where 0 disables the VOX.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>voxDelay</token></term>\n    <listitem>\n      <para>\n        Specifies the VOX delay between triggering the VOX and start of transmission in arbitrary \n        units. Default 0ms.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKeyAShort</token>, <token>funcKeyBShort</token>, <token>funcKeyCShort</token>,\n      <token>funcKeyALong</token>, <token>funcKeyBLong</token>, <token>funcKeyCLong</token>, \n      <token>funcKeyAVeryLong</token>, <token>funcKeyBVeryLong</token>, \n      <token>funcKeyCVeryLong</token></term>\n    <listitem>\n      <para>\n        Specifies the functions for the programmable side keys for short, long and very long key \n        presses. The function must be one of the functions described in \n        <xref linkend=\"extAnytoneSettingsKeyFunctions\"/>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>shutdown</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the handset will shutdown along with the radio.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>backlight</token></term>\n    <listitem>\n      <para>\n        Specifies the duration, the backlight remains on after any event (keypress, call, etc.). \n        This can be set in arbitrary units an also to <token>infinity</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n</section>\n\n<section>\n<info><title>Repeater settings</title></info>\n<para>\n  Collects all settings related to the <productname>DMR-6X2UV</productname> and \n  <productname>AT-D578UV</productname> repeater feature. These settings are collected under \n  the <token>repeaterSettings</token> key within the <token>anytoneSettings</token> entry. \n</para>\n\n<variablelist>\n<varlistentry>\n  <term><token>enabled</token></term>\n  <listitem>\n    <para>\n      If <token>true</token>, the simplex repeater function is enabled.\n    </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>monitor</token></term>\n  <listitem>\n    <para>\n      If <token>true</token>, the traffic can be monitored on the speaker. This only applies to \n      <productname>DMR-6X2UV</productname>.\n    </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>timeSlot</token>, <token>secTimeSlot</token></term>\n  <listitem>\n    <para>\n      Specifies the time-slot of the repeater. This must be one of <token>Any</token>, \n      <token>TS1</token>, <token>TS2</token> or <token>Channel</token>. The latter implies, \n      that the repeater uses the time-slot of the channel.\n    </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>colorCode</token></term>\n  <listitem>\n    <para>\n      Specifies, how color codes are matched. If <token>Ignore</token> ignores the color code,\n      <token>VFOA</token> and <token>VFOB</token> specifies that the color code must match the one\n      of VFO A or B, respectively. This only applies to <productname>AT-D578UV</productname>.\n    </para>\n  </listitem>\n</varlistentry>\n</variablelist>\n\n<section>\n  <info><title><productname>DMR-6X2UV</productname> Simplex Repeater Mode</title></info>\n  <para>\n    The BETECH <productname>DMR-6X2UV</productname> series provides a simplex repeater feature. \n    Enabling the repeater feature, will record any transmission received and retransmit it on \n    the same channel. Consequently, the record feature must be enabled.\n  </para>\n  <para>\n    The only relevant options for the <productname>DMR-6X2UV</productname> are \n    <token>enabled</token>, <token>monitor</token> and <token>timeSlot</token>.\n    The latter specifies, which time slot to listen and transmit on. This can either be \n    <token>TS1</token>, <token>TS2</token> or <token>Channel</token>. The latter requires any \n    transmission to match the CC of the channel. \n  </para>\n</section>\n\n<section>\n  <info><title><productname>AT-D578UV</productname> Repeater</title></info>\n  <para>\n    The AnyTone <productname>AT-D578UV</productname> provides a proper cross-band or \n    cross-timeslot repeater. That is, in contrast to the <productname>DMr-6X2UV</productname>\n    the received audio is re-transmitted (almost) immediately.\n  </para>\n  <para>\n    In principle, the repeater works using VFO A for reception and VFO B for transmission or \n    vice-versa. If the <productname>AT-D578UV</productname> is set up as an FM repeater, the two \n    channels must be on different bands. \n  </para>\n  <para>\n    If set up as a DMR repeater, VFO A and VFO B must be on different time slots. This is \n    controlled via the <token>timeSlot</token> setting. If set to <token>TS1</token>, the \n    repeater receives on time slot 1 and transmits on time slot 2. If set set to\n    <token>TS2</token> the reverse it true. If set to <token>Any</token>, the radio switches\n    automatically between the two schemes.\n  </para>\n</section>\n</section>\n\n\n<section>\n<info><title>Satellite mode settings</title></info>\n<para>\n  Collects all settings related to the satellite mode. This extension is only relevant for the \n  <productname>DMR-6X2UV</productname> series.\n</para>\n\n<variablelist>\n<varlistentry>\n  <term><token>power</token></term>\n  <listitem>\n    <para>Specifies the transmit power setting in satellite mode. This must be one of \n    <token>Min</token>, <token>Low</token>, <token>Mid</token>, <token>High</token> or \n    <token>Max</token>. </para>\n  </listitem>\n</varlistentry>\n<varlistentry>\n  <term><token>squelch</token></term>\n  <listitem>\n    <para>Specifies the FM squelch level in satellite mode. This must be an integer in [0,1-10],\n      where 0 means an open squelch.</para>\n  </listitem>\n</varlistentry>\n</variablelist>\n</section>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/anytone/zone.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extAnytoneZone\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Zone extension</title>\n</info>\n\n<para>\n  This extensions allows to specify some zone attributes for <trademark>AnyTone</trademark> devices. \n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[zones:\n    - id: zone1\n      name: Zone Name\n      A: [ch1, ...]\n      B: [ch2, ...]\n      anytone:\n        hidden: false\n]]></programlisting>\n</example>\n\n<para>\n  The <trademark>AnyTone</trademark> zone extension is a mapping named <token>anytone</token>. It contains the \n  device specific settings for that zone.\n</para>\n\n\n<section>\n<info>\n  <title>Zone attributes</title>\n</info>\n\n<para>\n  For now, there is only one attribute allowing to hide the zone from the zone list in the radio.\n</para>\n\n<variablelist>\n  <title>Zone extension fields</title>\n  <varlistentry>\n    <term><token>hidden</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the zone is hidden in the zone list on the radio.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/aprs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpAPRS\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Positioning Systems</title>\n</info>\n\n<para>\n  Some radios allow to send the current position using DMR or analog APRS. Consequently, there are \n  two types of positioning systems <token>dmr</token> and <token>aprs</token>. \n</para>\n\n<para>\n  As an example, consider these two position reporting systems:\n</para>\n\n<example pgwide=\"1\">\n  <title></title>\n  <programlisting><![CDATA[positioning:\n  - dmr:\n      id: aprs1\n      name: BM ARPS\n      period: 300\n      contact: cont21\n  - aprs:\n      id: aprs2\n      name: APRS APAT81\n      period: 300\n      revert: ch104\n      icon: Jogger\n      message: Y07, QRG 144.675\n      destination: APAT81-0\n      source: DM3MAT-7\n      path: [WIDE1-1, WIDE2-1] \n]]></programlisting>\n</example>\n\n<para>\n  The first specifies a digital (DMR) positioning system while the latter defines a APRS system.\n</para>\n\n\n<section>\n<info>\n  <title>Common attributes</title>\n</info>\n\n<para>\n  The following attributes apply to both, analog APRS and digital DMR position reporting systems.\n</para>\n\n<variablelist>\n  <title>Common fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>\n        Specifies the ID of the system. This can be used reference this system in \n        <xref linkend=\"cpChannels\"/>. Any unique string is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>\n        Specifies the name of the position reporting system. Any string is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>period</token></term>\n    <listitem>\n      <para>\n        Specifies the update period in seconds. If omitted or set to <token>0</token>, the \n        system will not send any updates periodically.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info>\n  <title>DMR position reporting system attributes</title>\n</info>\n\n<para>\n  The following attributes apply only to digital (DMR) position reporting systems.\n</para>\n\n<variablelist>\n  <title>DMR position reporting system fields</title>\n  <varlistentry>\n    <term><token>contact</token></term>\n    <listitem>\n      <para>\n        Specifies the digital contact, the GPS information is sent to. This must be a \n        reference to a digital contact.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>revert</token></term>\n    <listitem>\n      <para>\n        Specifies the revert channel. That is, the channel the data is sent on. If set, \n        it must be a reference to a digital channel. If omitted or set to <token>!selected</token>,\n        the currently active channel will be used to send the GPS information.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info>\n  <title>APRS attributes</title>\n</info>\n\n<para>\n  The following attributes apply only for APRS position reporting systems.\n</para>\n\n<variablelist>\n  <title>Analog position reporting system (APRS) fields</title>\n  <varlistentry>\n    <term><token>revert</token></term>\n    <listitem>\n      <para>\n        Specifies the revert channel. That is, the channel the APRS information is sent on.\n        This must be a reference to an analog channel. Usually a dedicated analog APRS channel is \n        defined and referenced here. \n      </para>\n      <para>\n        If omitted or set to <token>!selected</token>, the currently active channel will be used to\n        send the position information. This, however, is not supported by all radios. Usually, an \n        explicit APRS revert channel must be specified.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>destination</token></term>\n    <listitem>\n      <para>\n        Specifies the destination call and SSID, the information is sent to. This must be a\n        string in the form <token>CALL-SSID</token>. Please note that the call must match the \n        format defined by the AX.25 protocol. That is, it may only contain letters and numbers and\n        cannot exceed the length of 6.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>source</token></term>\n    <listitem>\n      <para>\n        Specifies the source call and SSID. See <token>destination</token> for details.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>path</token></term>\n    <listitem>\n      <para>\n        Specifies the optional packet path. If set, this must be a list of <token>CALL-SSID</token> \n        strings.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>icon</token></term>\n    <listitem>\n      <para>\n        Specifies the icon name to use. The icon name will be fuzzy matched. See \n        <link xlink:href=\"https://dm3mat.darc.de/qdmr/libdmrconf/classAPRSSystem.html\">\n        for a complete list</link> of icon names.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>message</token></term>\n    <listitem>\n      <para>\n        Specifies an optional message sent along with the position information.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/channels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpChannels\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Channels</title>\n</info>\n\n<para>\n  The <token>channels</token> list contains all channels defined within the codeplug. Usually, \n  DMR radios support at least two different channel types. DMR channels and analog \n  FM channels. Some radios (e.g., running OpenRTX) also support M17 channels. To distinguish \n  these three types, each entry of the channel list contains a \n  map with a single entry. The key specifies the type (either <token>dmr</token>,\n  <token>fm</token> or <token>m17</token>) while the value contains the actual channel definition.\n</para>\n\n<para>\n  As an example consider the following two channel definitions:\n</para>\n\n<example pgwide=\"1\">\n  <title>Channel definition</title>\n  <programlisting><![CDATA[channels:\n  - dmr:\n      id: ch5\n      name: BB DB0LDS TS2\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      power: High\n      timeout: 0\n      rxOnly: false\n      vox: !default\n      scanList: scan1\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp2\n      contact: cont7\n      radioID: !default\n      roaming: !default\n  - fm:\n      id: ch76\n      name: DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      power: High\n      timeout: 0\n      rxOnly: false\n      vox: !default\n      squelch: !default\n      admit: Always\n      bandwidth: Narrow\n      rxTone: {ctcss: 67}\n      txTone: {ctcss: 67} \n]]></programlisting>\n</example>\n\n<para>\n  The first channel is a DMR channel. Its ID is <emphasis>ch5</emphasis> and its name \n  <emphasis>BB DB0LDS TS2</emphasis>. The second channel is an analog FM channel with ID <emphasis>ch76</emphasis> \n  and name <emphasis>DB0LDS</emphasis>. Please note, that there are some common attributes like \n  <token>rxFrequency</token>, <token>txFrequency</token>, <token>power</token>, <token>timeout</token>,\n  <token>rxOnly</token>, <token>vox</token> and <token>scanList</token> but also type-specific settings like \n  <token>timeSlot</token> or <token>rxTone</token> which apply only to digital and analog \n  channels respectively.\n</para>\n\n\n<section>\n<info>\n  <title>Common attributes</title>\n</info>\n\n<para>\n  The following attributes are common for all channel types.\n</para>\n\n<variablelist>\n  <title>Common channel fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>\n        Specifies the ID of the channel, this can be used later to reference this channel in \n        <xref linkend=\"cpZones\"/> and <xref linkend=\"cpScanLists\"/>. Any unique string is \n        valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>\n        Specifies the name of the channel. Any string is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>rxFrequency</token>, <token>txFrequency</token></term>\n    <listitem>\n      <para>\n        Specifies the RX or TX frequency in MHz. Any floating point number is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>power</token></term>\n    <listitem>\n      <para>\n        Specifies the transmit power of the channel. Must be one of <token>Min</token>, \n        <token>Low</token>, <token>Mid</token>, <token>High</token> or <token>Max</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>timeout</token></term>\n    <listitem>\n      <para>\n        Specifies the transmit timeout in seconds. Any integer is valid here. Omitting this \n        field or setting it to <code>0</code> will disable the timeout. Setting it to \n        <token>!default</token>, the global transmit timeout will be used. See \n        <xref linkend=\"yamlRadioSettings\"/>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>rxOnly</token></term>\n    <listitem>\n      <para>\n        If set to <token>true</token>, the channel can only receive. Omitting it or setting it\n        to <token>false</token> will allow transmission on the channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>vox</token></term>\n    <listitem>\n      <para>\n        Specifies the VOX sensitivity for this channel. If set to <token>!default</token>, \n        the global VOX sensitivity will be used. See <xref linkend=\"yamlRadioSettings\"/>. \n        Omitting this value or setting it to 0, will disable the VOX for this channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>scanList</token></term>\n    <listitem>\n      <para>\n        Specifies the optional scan list for this channel. If set, this must be a reference to a \n        <xref linkend=\"cpScanLists\"/>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info>\n  <title>DMR channel attributes</title>\n</info>\n\n<para>\n  The following attributes apply only to DMR channels.\n</para>\n\n<variablelist>\n  <title>DMR channel fields</title>\n  <varlistentry>\n    <term><token>admit</token></term>\n    <listitem>\n      <para>\n        Specifies the admit criterion for the channel. This must be one of <token>Always</token>, \n        <token>Free</token> or <token>ColorCode</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>colorCode</token></term>\n    <listitem>\n      <para>\n        Specifies the color code of the channel, any number between 0-16 is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>timeSlot</token></term>\n    <listitem>\n      <para>Specifies the time slot of the channel. Must be one of <token>TS1</token> or \n        <token>TS2</token>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>groupList</token></term>\n    <listitem>\n      <para>\n        Specifies the RX group list for this channel. This must be a reference to a group list.\n        See <xref linkend=\"cpGroupLists\"/> above.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>contact</token></term>\n    <listitem>\n      <para>\n        Specifies the default transmit contact. This must be a reference to a digital contact.\n        See <xref linkend=\"cpContacts\"/> above. If omitted, no default transmit contact is \n        associated with the channel. Some radios require all channels to have a transmit contact.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>aprs</token></term>\n    <listitem>\n      <para>\n        Specifies the positioning system for this channel. If set, this must be a reference \n        to a positioning system (DMR or APRS). See <xref linkend=\"cpAPRS\"/> below.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>roaming</token></term>\n    <listitem>\n      <para>\n        Specifies the roaming zone for this channel. If set it must be a reference to a roaming\n        zone. See <xref linkend=\"cpRoaming\"/> below. To use the default roaming zone here, consider\n        using the <code>!default</code> tag instead of a direct reference to a specific zone. If \n        omitted, no roaming zone (also not the default one) is associated with the channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>radioID</token></term>\n    <listitem>\n      <para>\n        Specifies the radio ID for this channel. If set, it must be a reference to one of the radio\n        DMR IDs. See <xref linkend=\"cpRadioIDs\"/>. To use the default radio ID here, consider using \n        the <code>!default</code> tag instead of a direct reference to a specific ID. If omitted \n        the default radio ID is assumed.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n  <info>\n    <title>M17 channel attributes</title>\n  </info>\n  \n  <para>\n    The following attributes apply only to M17 digital channels.\n  </para>\n  \n  <variablelist>\n    <title>M17 channel fields</title>\n    <varlistentry>\n      <term><token>mode</token></term>\n      <listitem>\n        <para>\n          Specifies the channel mode. That is, one of <token>Voice</token>, <token>Data</token> and\n          <token>VoiceAndData</token>.\n        </para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>accessNumber</token></term>\n      <listitem>\n        <para>\n          Specifies the channel access number. Similar to the color code of DMR channels, this must\n          match the repeater access number. A value between 0-15.\n        </para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>contact</token></term>\n      <listitem>\n        <para>\n          Specifies the M17 transmit contact. This must be a reference to a M17 contact.  \n        </para>\n      </listitem>\n    </varlistentry>\n  </variablelist>\n  <varlistentry>\n    <term><token>gpsEnabled</token></term>\n    <listitem>\n      <para>\n        Specifies wether position information is send along with voice and data.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>encryptionMode</token></term>\n    <listitem>\n      <para>\n        Specifies the encryption mode. That is, one of <token>None</token>, \n        <token>Scrambled</token> and <token>AES256</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</section>  \n\n\n<section>\n<info>\n  <title>FM channel attributes</title>\n</info>\n\n<para>\n  The following attributes apply only to FM channels.\n</para>\n\n<variablelist>\n  <title>FM channel fields</title>\n  <varlistentry>\n    <term><token>admit</token></term>\n    <listitem>\n      <para>\n        Specifies the admit criterion for the channel. Must be one of <token>Always</token>,\n        <token>Free</token> or <token>Tone</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>squelch</token></term>\n    <listitem>\n      <para>\n        Specifies the squelch level for the channel. Must be an integer in the range of \n        [1-10]. If set to 0, the squelch gets disabled. If set to <token>!default</token>, the \n        global squelch setting is used. See <xref linkend=\"yamlRadioSettings\"/> above.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bandwidth</token></term>\n    <listitem>\n      <para>\n        Specifies the bandwidth of the channel. Must either be <token>Narrow</token> or \n        <token>Wide</token> for 12.5kHz or 25kHz respectively.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>rxTone</token></term>\n    <listitem>\n      <para>Specifies the receive sub-tone setting for this channel. That is, the squelch will\n        only open when a certain subtone is received along with the signal. As there are two \n        common subtone standards, this attribute is a map with a single entry. The key specifies\n        the type (either <token>ctcss</token> or <token>dcs</token>) while the value specifies the \n        actual subtone. For CTCSS tones, the value is the subtone frequency in Hz. For DCS it is\n        the code number as an integer. For inverted DCS codes, use negative numbers.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>txTone</token></term>\n    <listitem>\n      <para>Specifies the transmit sub-tone setting for this channel. For details see \n        <token>rxTone</token> above.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>aprs</token></term>\n    <listitem>\n      <para>Specifies the APRS positioning system of this channel. If set, it must be a \n        reference to an analog APRS system.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info><title>Extended channel settings</title></info>\n\n<para>\n  All channels have some extended settings section. This section allows to specify usually unused \n  settings that are common among many of the supported radios. The extended settings are stored \n  under the <token>extended</token> key.\n</para>\n\n<section>\n<info><title>Common extended channel settings</title></info>\n\n<para>\n  There is only one setting common to all channel types. The talkaround flag.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>talkaround</token></term>\n    <listterm>\n      <para>For a repeater channel, if enabled, the radio transmits on the RX frequency. This \n        allows to bypass the repeater and answer directly on the RX frequency. </para>\n    </listterm>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info><title>Extended FM channel settings</title></info>\n<para>\n  Additional to the common extended channel settings, this section describes all extended settings\n  applicable to FM channels.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>reverseBurst</token></term>\n    <listitem>\n      <para>Enables the CTCSS phase-reversal at the end of the transmission. This can be used \n        to mitigate an issue with the CTCSS squelch, where a short burst of noise may heard at\n        the end of the transmission. </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info><title>Extended DMR channel settings</title></info>\n<para>\n  Additional to the common extended channel settings, this section describes all extended settings\n  applicable to FM channels.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>privateCallConfirm</token>, <token>smsConfirm</token>, <token>dataConfirm</token></term>\n    <listitem>\n      <para>Enables confirmation of SMS, data and private call reception.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dcdm</token></term>\n    <listitem>\n      <para>Enables the dual-capacity direct mode. For simplex operation, allows to utilize both \n        time-slots. This requires one radio to dictate a clock to define TS1 and TS2. Not used in \n        amateur radio.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>sms</token></term>\n    <listitem>\n      <para>Enables SMS reception.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>loneWorker</token></term>\n    <listitem>\n      <para>Enables lone-worker feature of some radios.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/commercial/channel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extComChannel\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Channel extension</title>\n</info>\n\n<para>\n  This extension allows to specify some channel settings specific for commercial applications\n  of DMR. For now, it only allow for specifying the encryption key for a digital channel.\n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[channels:\n  - digital:\n      id: ch1\n      name: Example channel \n      # all the other channel settings\n      commercial:\n        encryptionKey: KEY_LABEL\n]]></programlisting>\n</example>\n\n<para> \n  The commercial channel extension is a mapping named <token>commercial</token>. It contains the \n  settings for commercial features for that channel. \n</para>\n\n\n<section>\n<info>\n  <title>Channel attributes</title>\n</info>\n\n<para>\n  For now, there is only one attribute specifying the encryption key associated with the channel.\n</para>\n\n<variablelist>\n  <title>Channel extension fields</title>\n  <varlistentry>\n    <term><token>encryptionKey</token></term>\n    <listitem>\n      <para>References a encryption key defined within the commercial codeplug extension. See <xref linkend=\"extComEncryption\"/>.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n</section>\n        \n"
  },
  {
    "path": "doc/manual/codeplug/commercial/encryption.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extComEncryption\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Encryption extension</title>\n</info>\n\n<para>\n  This extension allows to configure the commercial encryption features of DMR. Almost all DMR \n  radios implement means for encrypting the traffic. This feature however, is usually illegal \n  when used within amateur radio.\n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[commercial:\n  encryptionKeys:\n    - dmr: \n      id: key1\n      name: Example key 1\n      key: ABCD\n    - rc4:\n      id: key2\n      name: Example key 2\n      key: 0123456789\n    - aes:\n      id: key3\n      name: Example key 3\n      key: 0123456789ABCDEF0123456789ABCDEF\n]]></programlisting>\n</example>\n\n<para> \n  This extension is a simple list of keys held in the global <token>commercial</token> extension.\n  Each key must either be a DMR (basic), RC4 (enhanced) or AES (advanced) key. \n  To differentiate these key types, each list entry must be a mapping with a single entry. The name\n  specifies the type (i.e., either <token>dmr</token>, <token>rc4</token> or <token>aes</token>). \n  The value then specifies the properties of the key.\n</para>\n\n\n<section>\n<info>\n  <title>Common key attributes</title>\n</info>\n\n<para>\n  As only the key size differs between the different key types, there are common attributes.\n  Specifically, the ID and name of the key.\n</para>\n\n<variablelist>\n  <title>Common key fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>\n        Specifies the ID of the key. This ID can then be used to reference the key within the \n        commercial digital channel extension. See <xref linkend=\"extComChannel\"/>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>\n        Specifies the name of the key. This property is usually not encoded in the binary \n        codeplug.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info>\n  <title>DMR (basic) key attributes</title>\n</info>\n    \n<variablelist>\n  <title>DMR key fields</title>\n  <varlistentry>\n    <term><token>key</token></term>\n    <listitem>\n      <para>\n        Specifies the key as a HEX string. It must be at least 8bit but can be of variable size. \n        The actual size depends on the device. Usually, a fixed size of 16 or 32bit is supported. \n        Some devices, however, support variable sized keys.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n  \n\n<section>\n  <info>\n    <title>RC4 (enhanced) key attributes</title>\n  </info>\n      \n  <para>\n  </para>\n      \n  <variablelist>\n    <title>RC4 key fields</title>\n    <varlistentry>\n      <term><token>key</token></term>\n      <listitem>\n        <para>\n          Specifies the key as a HEX string. This key is fixed to a size of 40bit. That is, the hex \n          string must be of length 10.\n        </para>\n      </listitem>\n    </varlistentry>\n  </variablelist>\n  </section>\n  \n  \n  <section>\n<info>\n  <title>AES (advanced) key attributes</title>\n</info>\n    \n<para>\n</para>\n    \n<variablelist>\n  <title>AES key fields</title>\n  <varlistentry>\n    <term><token>key</token></term>\n    <listitem>\n      <para>\n        Specifies the key as a HEX string. Also this key can be of variable size. Usually, these \n        keys are 128 or 256bit. The actual size depends on the device.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n  \n</section>\n        \n"
  },
  {
    "path": "doc/manual/codeplug/commercial/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<chapter xml:id=\"extCom\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Commercial Codeplug Extensions</title>\n</info>\n\n<para>\n  This section collects all extensions that configure commercial features present in all radios.\n</para>\n\n<para>\n  DMR was developed as a digital replacement of the analog trunked radio systems used in commercial\n  context. To this end, there are many features of that are irrelevant or outright forbidden in an\n  amateur radio context. However, some HAM operators use their hand-held radios also for their \n  day job and thus may need to configure some features that are only applicable in a commercial\n  context. To this end, qdmr allows to set some of the commercial features through extensions.\n</para>\n\n<warning>\n  <para>\n    Some commercial features defined within the DMR standard are illegal to use in an amateur \n    radio context. Check your local regulations!\n  </para>  \n</warning>\n\n<example>\n  <title>Example commercial extension defining an encryption key.</title>\n  <programlisting><![CDATA[commercial:\n  encryptionKeys:\n    - aes:\n      id: key1\n      name: Example key 1\n      key: 01234567890123456789012345678901\n]]></programlisting>\n</example>\n\n<para>\n  All commercial extensions are held under the <token>commercial</token> key within the codeplug.\n  For now, there is only one extension, the encryption extension listing the encryption keys \n  described below.\n</para>\n\n<xi:include href=\"channel.xml\"/>\n<xi:include href=\"encryption.xml\"/>\n\n</chapter>\n"
  },
  {
    "path": "doc/manual/codeplug/contacts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpContacts\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n         \n<info>\n  <title>Contacts</title>\n</info>\n\n<para>\n  The <token>contacts</token> element specifies a list of all contacts. Each contact is a map \n  that contains a single entry to specify the contact type. The key specifies the type name and \n  the value specifies the actual contact definition. Currently there are three possible contact \n  types.\n</para>\n\n<para>\n  As an example consider this contact list, containing 4 contact definitions. One for each \n  type.\n</para>\n\n<example pgwide=\"1\">\n  <title>Contact definitions</title>\n  <programlisting language=\"yaml\"><![CDATA[contacts:\n  - dmr: {id: ww, name: WW, type: GroupCall, number: 91}\n  - dmr: {id: cont24, name: DM3MAT Hannes, type: PrivateCall, ring: true, number: 2621370}\n  - dmr: {id: cont12, name: All Call, type: AllCall, number: 16777215}\n  - dtmf: {id: dtmf1, name: DTMF example, number: \"#123*\"} \n]]></programlisting>\n</example>\n\n\n<section>\n<info>\n  <title>DMR Contacts (<token>dmr</token>)</title>\n</info>\n\n<para>\n  A DMR contact is a simple object and is usually defined in one line. Each contact contains\n  an optional <token>id</token> that will be used to reference this contact throughout the\n  codeplug (e.g., in channels, group lists, etc.). \n</para>\n\n<variablelist>\n  <title>DMR Contact Fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the identifier of the contact. This identifier can later be used to \n        reference the contact. Any unique string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the contact. Any string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>type</token></term>\n    <listitem>\n      <para>Specifies the type of the contact. Must be one of <token>PrivateCall</token>,\n        <token>GroupCall</token> or <token>AllCall</token>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>number</token></term>\n    <listitem>\n      <para>Specifies the DMR ID for this contact. That is, any integer between 0 and \n        16777215. This element is mandatory for all types except for the \n        <token>all</token>-call. For the all-call, the default number 16777215 will be \n        used.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ring</token></term>\n    <listitem>\n      <para>If <token>true</token>, the radio will ring whenever a call from this contact \n        is received (if supported by the radio). Optional, if omitted set to \n        <token>false</token>.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n  <info>\n    <title>M17 Contacts (<token>m17</token>)</title>\n  </info>\n  \n  <para>\n    An M17 contact is a simple object and is usually defined in one line. Each contact contains\n    an optional <token>id</token> that will be used to reference this contact throughout the\n    codeplug (e.g., in M17 channels). For now, M17 is only supported by radios running the \n    <ulink url=\"https://openrtx.org\"><citetitle>OpenRTX firmware</citetitle></ulink>.\n  </para>\n\n  <variablelist>\n    <title>M17 Contact Fields</title>\n    <varlistentry>\n      <term><token>id</token></term>\n      <listitem>\n        <para>Specifies the identifier of the contact. This identifier can later be used to \n          reference the contact. Any unique string is valid.</para>\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>call</token></term>\n      <listitem>\n        <para>Specifies the call (M17 ID) of the contact. This must be a string of not more than 9\n          chars, containing only <token>A-Z</token>, <token>0-9</token>, <token>.</token>, \n          <token>/</token> &amp; <token>-</token>. See <ulink \n          url=\"https://m17-protocol-specification.readthedocs.io/en/latest/address_encoding.html\">\n          <citetitle>M17 specification</citetitle></ulink></para> for details.\n      </listitem>\n    </varlistentry>\n    <varlistentry>\n      <term><token>broadcast</token></term>\n      <listitem>\n        <para>Specifies whether the contact is the M17 broadcast destination. Default is \n          <token>false</token>. If <token>true</token>, the <token>call</token> is ignored.</para>\n      </listitem>\n    </varlistentry>\n  </variablelist>\n</section>\n\n\n<section>\n<info>\n  <title>Analog DMTF Contacts (<token>dtmf</token>)</title>\n</info>\n\n<para>\n  An analog DTMF contact can be used to store commonly used DTMF sequences. For example, it may be\n  used to control the <emphasis>EchoLink</emphasis> feature of a repeater.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the identifier of the contact. This identifier can later be used to \n        reference the contact. Any unique string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the contact. Any string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>number</token></term>\n    <listitem>\n      <para>Specifies the DTMF ID for this contact. That is any combination of numbers \n        <token>0-9</token> and symbols <token>A-D</token>, <token>*</token>, <token>#</token>.\n        used.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ring</token></term>\n    <listitem>\n      <para>If <token>true</token>, the radio will ring whenever a call from this contact \n        is received (if supported by the radio). Optional, if omitted set to \n        <token>false</token>.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section> \n  "
  },
  {
    "path": "doc/manual/codeplug/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<chapter xml:id=\"cpExtensions\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Device specific extensions</title>\n</info>\n  \n<para>\n  The sole reason for introducing a new YAML based codeplug format was the ability to extend the\n  file format with device specific settings without breaking the format. This was simply impossible \n  with the <emphasis>old</emphasis> table based text files.\n</para>\n\n<para>\n  The present YAML based codeplug file format is extensible at almost any level. That is, device \n  specific elements can be added to single codeplug elements like channels, zones or contacts but \n  also to the codeplug itself. The latter allows to extend the codeplug with new elements. \n</para>\n\n<xi:include href=\"opengd77/extensions.xml\"/>\n<xi:include href=\"radioddity/extensions.xml\"/>\n<xi:include href=\"tyt/extensions.xml\"/>\n<xi:include href=\"anytone/extensions.xml\"/>\n<xi:include href=\"smsextension.xml\"/>\n</chapter>"
  },
  {
    "path": "doc/manual/codeplug/format.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<chapter xml:id=\"codeplug\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Extensible Codeplug File Format</title>\n</info>\n\n<tip>\n  <para>\n    This document describes the extensible codeplug file format using YAML. If you are unfamiliar\n    with YAML, consider reading a \n    <link xlink:href=\"https://learn.getgrav.org/16/advanced/yaml\">YAML introduction</link> first.\n    The documentation for the <emphasis>old</emphasis> table based <filename>conf</filename>-file format, can\n    be found in section <xref linkend=\"confFormat\"/>.\n  </para>\n</tip>\n\n<para>\n  The introduction of device specific settings (with version 0.9.0) required an extensible codeplug\n  file format. The <emphasis>old</emphasis> table based format did not allow for any extension without braking \n  backward compatibility. The new YAML based format allows for exactly that: Some means to extend \n  the format for device specific settings without breaking the format while maintaining some degree \n  of readability. (Yes, some users use the command line tool and edit their codeplug in a text \n  editor.)\n</para>\n\n<para>\n  There are several levels, at which device specific extensions may appear within the codeplug. \n  There are global extensions that apply to the entire codeplug. These extensions are located at \n  the top level. There might also be extensions to single channels, contacts, zones, etc. These \n  extensions are then located under the specific element that gets extended. \n</para>\n\n<xi:include href=\"radiosettings.xml\"/>\n<xi:include href=\"radioids.xml\"/>\n<xi:include href=\"contacts.xml\"/>\n<xi:include href=\"grouplists.xml\"/>\n<xi:include href=\"channels.xml\"/>\n<xi:include href=\"zones.xml\"/>\n<xi:include href=\"scanlists.xml\"/>\n<xi:include href=\"aprs.xml\"/>\n<xi:include href=\"roaming.xml\"/>\n\n</chapter>\n"
  },
  {
    "path": "doc/manual/codeplug/grouplists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpGroupLists\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Group lists</title>\n</info>\n\n<para>\n  A group list collects several digital (DMR) contacts that should be received on a channel \n  associated with this group list. Consequently, a group list consists of a name and a list \n  of contact IDs. The <token>groupLists</token> element is then a list of several group list \n  definitions.\n</para>\n\n<para>\n  As an example, consider the two group lists below:\n</para>\n\n<example pgwide=\"1\">\n  <title>Group list definition</title>\n  <programlisting><![CDATA[groupLists:\n  - {id: grp1, name: DL, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: Berlin/Brand, contacts: [cont6, cont5, cont7]}\n]]></programlisting>\n</example>\n\n<para>\n  The first group list has the internal ID <emphasis>grp1</emphasis>. This ID can then be used later\n  to reference this group list. The name is set to <emphasis>DL</emphasis>. The list of contacts of the \n  group list is then defined as a list containing the IDs of the referenced contacts.\n</para>\n\n<variablelist>\n  <title>Group list fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the ID of the group list. This ID can then be used to refer to the group list\n        within digital channels. Any unique string is possible here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the group list. Any string is possible here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>contacts</token></term>\n    <listitem>\n      <para>A list of digital contact IDs. See <xref linkend=\"cpContacts\"/> above.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/opengd77/channel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extOpenGD77Channel\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Channel extension</title>\n</info>\n\n<para>\n  This extension allows to specify some channel settings specific for devices running the \n  OpenGD77 firmware. This extension can be added to any channel, analog and digital.\n  For now, this extension only allows to specify the power for the channel in more detail.\n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[channels:\n  - digital:\n      id: ch1\n      name: Example channel \n      # all the other channel settings\n      openGD77:\n        scanZoneSkip: false\n        scanAllSkip: false\n        beep: true\n        powerSave: true\n        location: JO62jl\n        talkerAliasTS1: APRS\n        talkerAliasTS2: Both\n]]></programlisting>\n</example>\n\n<para> \n  The OpenGD77 channel extension is a mapping named <token>openGD77</token>. It contains the \n  device specific settings for that channel. \n</para>\n\n\n<section>\n<info>\n  <title>Channel attributes</title>\n</info>\n\n<para>\n  For now, there are only few attributes specifying scan behavior for the channel.\n</para>\n\n<variablelist>\n  <title>Channel extension fields</title>\n  <varlistentry>\n    <term>\n      <token>scanZoneSkip</token>\n      <token>scanAllSkip</token>\n    </term>\n    <listitem>\n      <para>\n        In contrast to the original GD-77 firmware, OpenGD77 does not implement scanning by scan \n        lists. Instead, either all channels defined in the radio or all channels within the current\n        zone can be scanned. To exclude some channels from these scans, the \n        <token>scanZoneSkip</token> and <token>scanAllSkip</token> flags can be used. When enabled,\n        the channel will be excluded from the respective scan.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term>\n      <token>beep</token>\n    </term>\n    <listitem>\n      <para>\n        If enabled (default), the channel beeps are played.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term>\n      <token>powerSave</token>\n    </term>\n    <listitem>\n      <para>\n        Enables the power save feature for this channel (default).\n      </para>\n    </listitem>\n  </varlistentry>\n    <varlistentry>\n    <term>\n      <token>location</token>\n    </term>\n    <listitem>\n      <para>\n        Allows to specify a fixed location to be used for this channel and the associated APRS system.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term>\n      <token>talkerAliasTS1</token>\n      <token>talkerAliasTS2</token>\n    </term>\n    <listitem>\n      <para>\n        Specifies what to be send as the talker alias, depending on the current timeslot. This must\n        be one of <token>None</token>, <token>APRS</token>, <token>Text</token> or\n        <token>Both</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n</section>\n        \n"
  },
  {
    "path": "doc/manual/codeplug/opengd77/contact.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extOpenGD77Contact\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>DMR contact extension</title>\n</info>\n\n<para>\n  This extensions allows to specify some DMR contact attributes for devices running the OpenGD77 \n  firmware. This extension is only applicable to DMR (digital) contacts. \n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[contacts:\n  - dmr:\n    name: Example contact \n    number: 1234567\n    openGD77:\n      timeSlotOverride: TS1\n]]></programlisting>\n</example>\n\n<para>\n  The OpenGD77 contact extension is a mapping named <token>openGD77</token>. It contains the \n  device specific settings for that DMR contact.\n</para>\n\n\n<section>\n<info>\n  <title>Contact attributes</title>\n</info>\n\n<para>\n  For now, there is only one attribute allowing to override the time slot of a channel whenever \n  this contact is selected as the destination contact for that channel.\n</para>\n\n<variablelist>\n  <title>Channel extension fields</title>\n  <varlistentry>\n    <term><token>timeSlotOverride</token></term>\n    <listitem>\n      <para>\n        OpenGD77 allows to override the time slot settings for each channel on the bases of\n        the selected transmit contact. If the contact has a time slot override set, this time \n        slot is used instead of the channel time slot. This attribute is either <token>None</token>,\n        <token>TS1</token> or <token>TS2</token>. If <token>None</token> is set, the channel time slot \n        will not be overridden.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/opengd77/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"extOpenGD77\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>OpenGD77 Codeplug Extensions</title>\n</info>\n\n<para>\n  This chapter documents the extensions and settings specific to radios running the OpenGD77 \n  firmware.\n</para>\n\n<xi:include href=\"channel.xml\"/>\n<xi:include href=\"contact.xml\"/>\n\n</section>"
  },
  {
    "path": "doc/manual/codeplug/radioddity/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"extRadioddity\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title><trademark>Radioddity</trademark> Codeplug Extensions</title>\n</info>\n\n<para>\n  This section collects all device specific extensions to the codeplug for \n  <trademark>Radioddity</trademark> and some <trademark>Baofeng</trademark> devices.\n</para>\n\n<para>\n  These extensions are applicable to <trademark>Radioddity</trademark> \n  <productname>GD77</productname>, <productname>GD73</productname> and Baofeng/Radioddity \n  <productname>RD-5R</productname> radios.\n</para>\n\n<xi:include href=\"radiosettings.xml\"/>\n\n</section>"
  },
  {
    "path": "doc/manual/codeplug/radioddity/generalradiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"exRadioddityGeneralRadioSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>General radio settings</title>\n</info>\n\n<para>\n  This section describes the top-level generic radio settings for Radioddity devices. \n</para>\n\n<example>\n  <title>Radio settings extension example showing default values.</title>\n  <programlisting><![CDATA[settings:\n  [...]\n  radioddity:\n    monitorType: Open\n    loneWorkerResponseTime: 1min\n    loneWorkerReminderPeriod: 10s\n    groupCallHangTime: 3000ms\n    privateCallHangTime: 3000ms\n    downChannelModeVFO: false\n    upChannelModeVFO: false\n    powerSaveMode: true\n    preambleDuration: 360ms\n    wakeupPreamble: true\n    allLEDsDisabled: false\n    quickKeyOverrideInhibited: false\n    txOnActiveChannel: false\n    animation: false\n    scanMode: Time\n    repeaterEndDelay: 0s\n    repeaterSTE: 0s    \n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Radio settings fields</title>\n  <varlistentry>\n    <term><token>monitorType</token></term>\n    <listitem>\n      <para>\n        Specifies the monitor type. Must be either <token>Open</token> or <token>Silent</token>. Default <token>Open</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>loneWorkerResponseTime</token></term>\n    <listitem>\n      <para>\n        When lone worker is enabled, specifies the time in minutes before the radio will start to \n        remind the user. Default 1min. See also <xref linkend=\"glLoneWorker\">lone worker</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>loneWorkerReminderPeriod</token></term>\n    <listitem>\n      <para>\n        Specifies the period in seconds for the lone worker reminder. Default 10s. See also \n        <xref linkend=\"glLoneWorker\">lone worker</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>groupCallHangTime</token></term>\n    <listitem>\n      <para>        \n        Specifies the group-call hang time in milliseconds. This is the time-period the user can \n        answer a received group call by pressing PTT. After this time has passed, a press on the\n        PTT button will call the default contact on the selected channel. Default 3000ms. See also\n        <xref linkend=\"glHangTime\">hang time</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>privateCallHangTime</token></term>\n    <listitem>\n      <para>        \n        Specifies the private-call hang time in milliseconds. This is the time-period the user can \n        answer a received private call by pressing PTT. After this time has passed, a press on the\n        PTT button will call the default contact on the selected channel. Default 3000ms. See also\n        <xref linkend=\"glHangTime\">hang time</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>downChannelModeVFO</token></term>\n    <listitem>\n      <para>  \n        If <token>true</token>, the channel-up button will tune the VFO. If <token>false</token>, \n        it will step through the channels. Default <token>false</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>upChannelModeVFO</token></term>\n    <listitem>\n      <para>  \n        If <token>true</token>, the channel-donw button will tune the VFO. If <token>false</token>, \n        it will step through the channels. Default <token>false</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerSaveMode</token></term>\n    <listitem>\n      <para>\n        Puts the radio is a sleep mode when in idle state (no traffic on the channels). This \n        allows for some power saving. However, the radio will need some time to wake up. \n        Consequently, all other radios in the network need to transmit a wake-up preamble. \n        Default <token>true</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>wakeupPreamble</token></term>\n    <listitem>\n      <para>\n        Enables the transmission of a short wake-up preamble allowing receiving radios to \n        wake-up in time for the actual transmission. Default <token>true</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>preambleDuration</token></term>\n    <listitem>\n      <para>\n        This sets the preamble duration in milliseconds. Default 360ms.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerSaveDelay</token></term>\n    <listitem>\n      <para>\n        Specifies the delay, before an idle radio enters power save mode. \n        (<productname>GD-73</productname> only)\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>allLEDsDisabled</token></term>\n    <listitem>\n      <para>  \n        If <token>true</token>, all LEDs are disabled. Default <token>false</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>quickKeyOverrideInhibited</token></term>\n    <listitem>\n      <para>        \n        If <token>true</token>, allows the user to transmit on a busy channel irrespective of the \n        channels admit criterion by double pressing the PTT. Default <token>false</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>txInterrupt</token></term>\n    <listitem>\n      <para>\n        WTF?!?\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>txOnActiveChannel</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the radio will transmit on the currently active channel (if \n        double-wait) is enabled. Default <token>false</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>scanMode</token></term>\n    <listitem>\n      <para>\n        Specifies the scan mode. Must be one of <token>Time</token>, <token>Carrier</token> or \n        <token>Search</token>. Default <token>Time</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>repeaterEndDelay</token></term>\n    <listitem>\n      <para>\n        Specifies the delay after the end of a repeater transmission in seconds. Default 0s, off.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>repeaterSTE</token></term>\n    <listitem>\n      <para>\n        Specifies the repeater STE (what ever that means) in seconds. Default 0s, off.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>language</token></term>\n    <listitem>\n      <para>\n        Specifies the UI language. Must be one of <token>Chinese</token> or <token>English</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/radioddity/radiobootsettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"exRadioddityRadioBootSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Boot settings</title>\n</info>  \n\n<para>\n  This section collects some settings related to booting the radio.\n</para>\n\n<example>\n  <title>Boot settings extension example showing default values.</title>\n  <programlisting><![CDATA[settings:\n  [...]\n  radioddity:\n    [...]\n    boot:\n      displayMode: Text\n      bootPassword: \"\"\n      progPassword: \"\"\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Boot settings fields</title>\n  <varlistentry>\n    <term><token>display</token></term>\n    <listitem>\n      <para>\n        Specifies what to display during boot. Must be one of <token>None</token>, \n        <token>Text</token> or <token>Image</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bootPassword</token>, <token>progPassword</token></term>\n    <listitem>\n      <para>\n        Specifies the boot and programming passwords. The former (usually only numbers) must be \n        entered, when the radio boots. The latter must be entered in the CPS to program or\n        read the codeplug.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/radioddity/radiobuttonsettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"exRadioddityRadioButtonSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Button settings</title>\n</info>  \n\n<para>\n  This section describes how the buttons are configured for Radioddity devices.\n</para>\n\n<example>\n  <title>Button settings extension example showing default values.</title>\n  <programlisting><![CDATA[settings:\n  [...]\n  radioddity:\n    [...]\n    buttons:\n      longPressDuration: 1 s\n      funcKey1Short: ZoneSelect\n      funcKey1Long: ToggleFMRadio\n      funcKey2Short: ToggleMonitor\n      funcKey2Long: ToggleFlashLight\n      funcKey3Short: BatteryIndicator\n      funcKey3Long: ToggleVox\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Button settings fields</title>\n  <varlistentry>\n    <term><token>longPressDuration</token></term>\n    <listitem>\n      <para>\n        Sets the duration, a button must be pressed, to be considered as a\n        <quote>long press</quote>. This interval is usually expressed in ms. E.g., 1000ms.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKey1Short</token>, <token>funcKey1Long</token></term>\n    <listitem>\n      <para>Short and long-press functions for the programmable function key 1. This is the side key \n        1 on the <productname>GD77</productname> and <productname>RD-5R</productname> and the \n        <keycap>P1</keycap> button on the <productname>GD73</productname>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKey2Short</token>, <token>funcKey2Long</token></term>\n    <listitem>\n      <para>Short and long-press functions for the programmable function key 2. This is the side key \n        2 on the <productname>GD77</productname> and <productname>RD5R</productname> and the \n        <keycap>P2</keycap> button on the <productname>GD73</productname>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>funcKey3Short</token>, <token>funcKey3Long</token></term>\n    <listitem>\n      <para>Short and long-press functions for the programmable function key 3. This is the top key \n        on the <productname>GD77</productname> and <productname>RD5R</productname> This button is \n        not present on the <productname>GD73</productname>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n    \n<variablelist>\n  <title>Button functions</title>\n  <varlistentry>\n    <term><token>None</token></term>\n    <listitem>\n      <para>\n        Disables the button. No function is associated with it.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleAllAlertTones</token></term>\n    <listitem>\n      <para>\n        Enables or disables all alert tones. Only present in <productname>GD77</productname> and \n        <productname>RD5R</productname> radios.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>EmergencyOn</token>, <token>EmergencyOff</token></term>\n    <listitem>\n      <para>\n        Why not toggle? Either enables or disables an emergency.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleMonitor</token></term>\n    <listitem>\n      <para>\n        Enables/toggles the monitor. This is device specific, on some radios the monitor function\n        latches, on most not. Then, the monitor is enabled, as long as the button is pressed.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>OneTouch1</token>, <token>OneTouch2</token>, <token>OneTouch3</token>,\n      <token>OneTouch4</token>, <token>OneTouch5</token>, <token>OneTouch6</token></term>\n    <listitem>\n      <para>\n        Triggers one specific one-touch action. Not all radios have 6 of these. The GD-77 and RD-5R\n        have 6 one-touch actions, while the GD-73 has only 5.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleTalkaround</token></term>\n    <listitem>\n      <para>\n        Enables/disables the talkaround feature for repeater channels. The radio then also transmits\n        on the RX frequency. Consequently bypassing the repeater.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleScan</token></term>\n    <listitem>\n      <para>\n        Enables/disables the scan.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleEncryption</token></term>\n    <listitem>\n      <para>\n        Enables/disables the encryption for the channel, if configured.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleVox</token></term>\n    <listitem>\n      <para>\n        Enables/disables the VOX for the channel, if configured.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ZoneSelect</token></term>\n    <listitem>\n      <para>\n        Brings up the zone selection dialog.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>BatteryIndicator</token></term>\n    <listitem>\n      <para>\n        Shows the battery charge indicator.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleLoneWorker</token></term>\n    <listitem>\n      <para>\n        Enables/disables lone-worker feature, if configured. \n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>PhoneExit</token></term>\n    <listitem>\n      <para>\n        WTF?!?\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleFlashLight</token></term>\n    <listitem>\n      <para>\n        Enables/Disables the flash light. Not all devices have one.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ToggleFMRadio</token></term>\n    <listitem>\n      <para>\n        Enables/disables the FM broadcast radio.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>RadioCheck</token>,<token>RadioDisable</token>, <token>RadioEnable</token></term>\n    <listitem>\n      <para>\n        If configured, the radio will transmit tones, that cause other radios --- if configured \n        to do so --- to either response, disable or re-enable themselves. This allows to control \n        other radios remotely.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>TBST</token></term>\n    <listitem>\n      <para>\n        Sends the TBST tone (usually 1750Hz). Some radios have a fixed button combo for that.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>CallSwell</token></term>\n    <listitem>\n      <para>\n        WTF?!?\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/radioddity/radiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"exRadioddityRadioSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Radio settings extension</title>  \n</info>\n\n<para>\n  This extension allows to set the device-specific general settings for \n  <trademark>Radioddity</trademark> radios. It extends the \n  <token>settings</token> section of the codeplug and is split into several sub-extensions. \n</para>\n\n<xi:include href=\"generalradiosettings.xml\"/>\n<xi:include href=\"radiobuttonsettings.xml\"/>\n<xi:include href=\"radiotonesettings.xml\"/>\n<xi:include href=\"radiobootsettings.xml\"/>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/radioddity/radiotonesettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"exRadioddityRadioToneSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Tone settings</title>\n</info>  \n\n<para>\n  This section collects some settings relates to tones and other audio stuff.\n</para>\n\n<example>\n  <title>Tone settings extension example showing default values.</title>\n  <programlisting><![CDATA[settings:\n  [...]\n  radioddity:\n    [...]\n    tone:\n      lowBatteryWarn: false\n      lowBatteryWarnInterval: 30 s\n      lowBatteryWarnVolume: true\n      keyTone: false\n      keyToneVolume: true\n      callAlertDuration: 2 min\n      resetTone: false\n      unknownNumberTone: false\n      artsToneMode: Once\n      digitalTalkPermitTone: false\n      analogTalkPermitTone: false\n      selftestTone: true\n      channelFreeIndicationTone: false\n      allTonesDisabled: false\n      txExitTone: false\n      fmMicGain: 1\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Tone settings fields</title>\n  <varlistentry>\n    <term><token>lowBatteryWarn</token></term>\n    <listitem>\n      <para>\n        Enables the low battery-charge warning. This can either be a notification on the screen or \n        a warning tone. The warning interval and tone-volume might be set by \n        <token>lowBatteryInterval</token> and <token>lowBatteryWarnVolume</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>lowBatteryWarnInterval</token></term>\n    <listitem>\n      <para>\n        Specifies the interval, at which low battery warning are issued.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>lowBatteryWarnVolume</token></term>\n    <listitem>\n      <para>\n        Specifies the volume of the low-battery warning tone in a range from 1 to 10.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keyTone</token>, <token>keyToneVolume</token></term>\n    <listitem>\n      <para>\n        If <token>true</token>, the key-pad tones are enabled. Don't do it. The volume of these \n        tones might be set using <token>keyToneVolume</token> in a range from 1 to 10.\n      </para>\n    </listitem>\n  </varlistentry>\n\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/radioids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpRadioIDs\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Radio IDs</title>\n</info>\n\n<para>\n  The <token>radioIDs</token>-element specifies a list of radio IDs. Each radio ID is a map that \n  contains a single entry to specify the type. The key of the entry specifies the type name and \n  the value specifies the actual radio ID definition. Currently only the type <token>dmr</token> \n  is supported.\n</para>\n\n<para>\n  As an example consider this radio ID list, containing only a single ID.\n</para>\n\n<example pgwide=\"1\">\n  <title>Radio ID definitions</title>\n  <programlisting language=\"yaml\"><![CDATA[radioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370} \n]]></programlisting>\n</example>\n\n<para> \n  This DMR radio ID got the identifier <token>id1</token>, name <token>DM3MAT</token> \n  and number <token>2621370</token>.\n</para>\n\n\n<section>\n<info><title>DMR Radio IDs (<token>dmr</token>)</title></info>\n\n<para>\n  The DMR radio ID definition consist of an optional <token>id</token> (necessary to reference \n  that ID later), a <token>name</token> and the DMR ID <token>number</token>. \n</para>\n\n<variablelist>\n  <title>DMR Radio ID Fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the identifier of the radio ID. This identifier can later be used to \n      associate the radio ID to channels. Any unique string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the radio ID. This name may also be used as the radio name. \n        Any non-empty string is valid.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>number</token></term>\n    <listitem><para>Specifies the DMR ID for this radio ID. That is any integer between 0 and 16777215.</para></listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n</section>\n  "
  },
  {
    "path": "doc/manual/codeplug/radiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"yamlRadioSettings\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<info>\n  <title>Radio settings</title>\n</info>         \n\n<para>\n  The radio settings section contains all radio-wide settings. For example the microphone level, \n  boot text etc. For sake of readability, they are organized in groups. E.g., boot options, audio settings, ...\n</para>\n\n<para>\n  As an example, consider the following general settings\n</para>\n\n<example pgwide=\"1\">\n  <title>General radio-wide settings.</title>\n  <programlisting language=\"yaml\"><![CDATA[settings:\n  defaultID: id1\n  power: High\n  boot:\n    display: Logo\n    introLine1: qDMR\n    introLine2: DM3MAT\n    enablePassword: false\n    password: 00000815\n    defaultChannel: true\n    zoneA: zone1\n    channelA: ch1\n    zoneB: zone1\n    channelB: ch2\n    allowFactoryReset: true\n  audio:\n    squelch: 1\n    dmrSquelch: none\n    micGain: 6\n    fmMicGain: none\n    m17MicGain: none\n    maxSpeakerVolume: 10\n    maxHeadphoneVolume: 10\n    vox: off\n    voxDelay: 500ms\n    speech: false\n  gnss:\n    fixedLocationEnabled: false\n    fixedLocation: JO62jl45\n    systems: [GPS, Beidou]\n    units: Metric\n  dmr:\n    privateCallMatch: true\n    groupCallMatch: true\n    privateCallHangTime: 5s\n    groupCallHangTime: 3s\n    sendTalkerAlias: false\n    talkerAliasEncoding: Iso8\n    smsFormat: DMR\n    preamble: 100ms\n]]></programlisting>\n</example>\n\n<para>\n  Here, the microphone amplification is set to 6, the speech synthesis is disabled, the two boot\n  text lines are set to <quote>qDMR</quote> and <quote>DM3MAT</quote> respectively and the default \n  DMR radio ID is set to <quote>id1</quote>. The latter is the id of a radio ID defined below. \n</para>\n\n<para>\n  Also, the radio-wide default squelch, VOX and power level is set. These values can be referenced\n  later in channels. Also, some radios do not allow for these settings to be applied on a \n  per-channel basis. For these radios, these values are used. \n</para>\n\n<variablelist>\n  <title>Radio-wide Setting Fields</title>\n  <varlistentry>\n    <term><token>introLine1</token></term>\n    <term><token>introLine2</token></term>\n    <listitem>\n      <para>Sets the two boot text lines (if supported by the radio). These text lines will show up \n        on the boot of the radio. If the radio is set to show a picture during boot, these lines are \n        not shown.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>defaultID</token></term>\n    <listitem>\n      <para>Specifies which radio ID will be used as the default DMR ID (see \n        <xref linkend=\"cpRadioIDs\"/> below). If none is specified, the first \n        defined DMR radio ID will be used.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>power</token></term>\n    <listitem>\n      <para>Specifies the default transmit power. This value may be referenced in channels or \n        represent the radio-wide power setting. Possible values are <token>Min</token>, \n        <token>Low</token>, <token>Mid</token>, <token>High</token> and <token>Max</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n\n<section>\n<info><title>Boot settings</title></info>\n\n<para>\nSome settings relevant for the boot process.\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>display</token></term>\n    <listitem>\n      <para>Specifies what is shown during the boot process. This must be one of <token>Logo</token>,\n        <token>Image</token> or <token>text</token>. Where <token>Logo</token> is usually the manufacturer logo and the\n        default</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>passwordEnabled</token>, <token>password</token></term>\n    <listitem>\n      <para>Enables and specifies the boot password. This is usually a 8-digit number that must be entered into the\n        radio to boot it.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>defaultChannel</token>, <token>zoneA</token>, <token>channelA</token>, <token>zoneB</token>,\n      <token>channelB</token></term>\n    <listitem>\n      <para>Enables and specifies the channels and zones that are selected after boot. If disabled, usually the last\n        used channels/zones are selected. If specified, the channel must be member of the zone. To select a\n        zone/channel assign the corresponding zone/channel id.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>allowFactoryReset</token></term>\n    <listitem>\n      <para>If <token>true</token>, allows to factory reset the device during reboot. This can be used to clear an\n        incompatible codeplug after the firmware has been updated. Sometime, this must be enabled to enter the firmware\n        programming mode.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info><title>Audio settings</title></info>\n\n<para>\n  This section groups all global audio settings. Some of these can be overridden by channel settings.\n</para>\n\n\n<variablelist>\n  <varlistentry>\n    <term><token>squelch</token></term>\n    <listitem>\n      <para>Specifies the default squelch level. This value may be referenced in channels or\n        represent the radio-wide squelch setting. Any value in [0-10] is valid, where 0 implies\n        an open squelch (if supported by the radio).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dmrSquelch</token></term>\n    <listitem>\n      <para>\n        Specifies a separate squelch level for DMR channels. If set to <token>none</token>, the default squelch is used.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>micGain</token></term>\n    <listitem>\n      <para>Specifies the microphone amplification. Must be an integer between 1 and 10.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>fmMicGain</token>, <token>m17MicGain</token></term>\n    <listitem>\n      <para>\n        Specifies a separate microphone gain for FM and DMR channels. If set to <token>none</token>, the default mic\n        gain setting will be used.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>maxSpeakerVolume</token>, <token>maxHeadphoneVolume</token></term>\n    <listitem>\n      <para>\n        Specifies the maximum speaker/headphone volume setting. This can be used to limit the maximum volume,\n        when the radio is used indoors.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>vox</token></term>\n    <listitem>\n      <para>Specifies the default VOX sensitivity. This value may be referenced in channels or\n        represent the radio-wide VOX sensitivity. Any value in [0-10] is valid here, where 0\n        disables the VOX.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>voxDelay</token></term>\n    <listitem>\n      <para>\n        Specifies the delay between voice detection and start of transmission.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>speech</token></term>\n    <listitem>\n      <para>\n        Enables/disables the speech synthesis. Some radios can announce the current\n        channel etc. for the visually impaired. To enable that feature (if supported by the radio)\n        set this field to <token>true</token>. Must be a boolean value.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n\n<section>\n<info><title>GNSS settings</title></info>\n\n<para>\n  This section collects some global GNSS settings that many radio support. Especially, it allows to\n  specify a fixed location that can be used by the radio, if it does not support GPS.\n</para>\n\n<variablelist>\n  <title>Radio-wide GNSS Setting Fields</title>\n  <varlistentry>\n    <term><token>fixedLocationEnabled</token>, <token>fixedLocation</token></term>\n    <listitem>\n      <para>Specifies and enables a fixed location. This location can be used instead of a GNSS \n        position. The location is specified in terms of a locator.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>systems</token></term>\n    <listitem>\n      <para>Specifies the GNSSs to use in terms of a list of <token>GPS</token>, \n        <token>Glonass</token>, <token>Galileo</token>, <token>Beidou</token>. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>units</token></term>\n    <listitem>\n      <para>Specifies the units to use to show distances and speeds. Must be one of \n        <token>Metric</token> or <token>Archaic</token>. </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n  <info><title>DMR settings</title></info>         \n\n<para>\n  This section collects some global DMR settings that many radio support. Like sending a talker \n  alias and specifying the SMS format.\n</para>\n\n<variablelist>\n  <title>Radio-wide DMR Setting Fields</title>\n  <varlistentry>\n    <term><token>privateCallMatch</token>, <token>groupCallMatch</token></term>\n    <listitem>\n      <para>Enable private and group call matches. Must either be <token>true</token> or \n        <token>false</token>.</para>\n      <para>If private-call match is enabled, the radio only receives private calls for the radio.\n        all other private calls are ignored. </para>\n      <para>If group-call match is enabled, the radio only receives group calls that are in the \n        group list of the current channel. All other group calls are ignored.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>privateCallHangTime</token>, <token>groupCallHangTime</token></term>\n    <listitem>\n      <para>Specifies, how long a private and group call can be answered directly by pressing PTT.\n        That is, how long the call hangs around, before the default transmit contact for the \n        current channel gets active again. </para>\n      <para>The duration must be specified with units. By default, the duration for private calls \n        is 5s while the duration for group calls is 3s. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>sendTalkerAlias</token>, <token>talkerAliasEncoding</token></term>\n    <listitem>\n      <para>Enables sending the talker alias -- that is, the radio name along with the DMR ID. \n        This enables receives to show your radio name, if the receiver does not contain your DMR \n        ID in its call-sign DB. </para>\n      <para>Current DMR networks inject the talker alias based on the global DMR ID database. \n        Consequently, this feature is of little use. It might be relevant for commercial \n        applications and simplex connections. </para>\n      <para>If used, <token>talkerAliasEncoding</token> specifies the encoding of the alias.\n        It must be one of <token>Iso7</token>, <token>Iso8</token> or \n        <token>Unicode</token>. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>preamble</token></term>\n    <listitem>\n      <para>Specifies the duration of the DMR transmit preamble. The duration must be specified \n        with units. Usually this is set to 100ms. </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n</section>\n\n  "
  },
  {
    "path": "doc/manual/codeplug/roaming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpRoaming\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n\n<info>\n  <title>Roaming</title>\n</info>\n\n<para>\n  The roaming configuration consists of a list of roaming channels and roaming zones. While \n  roaming channels specify those channel settings, that might be overridden during roaming, \n  the roaming zone specifies a collection of roaming channels, that can be used to maintain a\n  connection to a particular talk group.\n</para>\n\n<section>\n<info><title>Roaming Channels</title></info>\n<para>\n  Roaming channels contain those settings of a DMR channel, that are specific for a particular \n  repeater. This allows to override these settings in a channel, when the repeater of that channel \n  gets out of range. Then, the radio may switch to a different repeater to maintain the connection \n  to the current talk group. \n</para>\n\n<example pgwide=\"1\">\n  <title></title>\n  <programlisting><![CDATA[roamingChannels:\n  - id: rch1\n    name: R DB0LDS\n    rxFrequency: 439.5625\n    txFrequency: 431.9625\n  - id: rch2\n    name: R DB0AFZ\n    rxFrequency: 439.97500\n    txFrequency: 430.57500\n    colorCode: 1\n    timeSlot: 1\n]]></programlisting>\n</example>\n\n<para>\n  The example above shows the definition of two roaming channels. Both are used to stay in contact \n  with a regional talk group within the Brandmeister network. While the repeater DB0LDS is located\n  within this region, DB0AFZ is not. Consequently, the time-slot of the channel needs to be \n  overridden. \n</para>\n\n<variablelist>\n  <title>Roaming channel fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the ID of the roaming channel. This ID can then be used to refer to this \n        channel within a roaming zone. Any unique string is valid here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the roaming channel. Any string is valid here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>rxFrequency</token>, <token>txFrequency</token></term>\n    <listitem>\n      <para>Specifies the receive and transmit frequencies of the channel.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>colorCode</token></term>\n    <listitem>\n      <para>Specifies the color code of the channel. If set, overrides the color code of the \n        current channel. If omitted, the color code of the selected channel is used during \n        roaming. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>timeSlot</token></term>\n    <listitem>\n      <para>Specifies the time slot of the channel. If set, overrides the time slot of the current\n        DMR channel. If omitted, the time slot of the selected channel is used during \n        roaming. </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section> \n\n<section>\n<info><title>Roaming Zones</title></info>\n<para>\n  Roaming zones are collections of roaming channels, that are scanned for the strongest signal to \n  maintain connection to a particular network or talk group.\n</para>\n\n<para>\n  As an example, consider the following roaming zone:\n</para>\n\n<example pgwide=\"1\">\n  <title></title>\n  <programlisting><![CDATA[roamingZones:\n  - id: roam1\n    name: Berlin/Brand\n    channels: [rch1, rch2, rch3] \n]]></programlisting>\n</example>\n\n<para>\n  This zone has the ID <emphasis>roam1</emphasis> and the name <emphasis>Berlin/Brand</emphasis>. \n  This group collects all roaming channels that provide access to the \n  <emphasis>Berlin/Brandenburg</emphasis> talk group.\n</para>\n<variablelist>\n  <title>Roaming zone fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the ID of the roaming zone. This ID can then be used to refer to this \n        zone. Any unique string is valid here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the roaming zone. Any string is valid here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channels</token></term>\n    <listitem>\n      <para>Specifies the member channels for this roaming zone. This must be a list of \n        references to roaming channels.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section> \n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/scanlists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpScanLists\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Scan lists</title>\n</info>\n\n<para>\n  Scan lists are simple lists of channels to scan. A scan list might be associated with a channel.\n</para>\n\n<para>\n  As an example, consider the following scan list:\n</para>\n\n<example pgwide=\"1\">\n  <title></title>\n  <programlisting><![CDATA[scanLists:\n  - id: scan1\n    name: KW\n    channels: [ch4, ch5, ch76, ch77, ch97, ch102, ch68, ch112] \n]]></programlisting>\n</example>\n\n<para>\n  This scan list has the ID <emphasis>scan1</emphasis>, the name <emphasis>KW</emphasis> and contains several channels \n  (both analog and digital).\n</para>\n\n<variablelist>\n  <title>Scan list fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>\n        Specifies the ID of the scan list. This ID can then be used to reference this scan \n        list in <xref linkend=\"cpChannels\"/>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>\n        Specifies the name of the scan list. Any string is valid here.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>primary</token></term>\n    <listitem>\n      <para>\n        Specifies the primary priority channel. Usually this channel is scanned very frequently. \n        If set, this must be a reference to a channel. If the tag <token>!selected</token> is used here,\n        the channel from which the scan got started is used as the primary priority channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>secondary</token></term>\n    <listitem>\n      <para>\n        Specifies the secondary priority channel. Usually this channel is scanned frequently. \n        If set, this must be a reference to a channel. If the tag <token>!selected</token> is used here,\n        the channel from which the scan got started is used as the secondary priority channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>revert</token></term>\n    <listitem>\n      <para>\n        Specifies the revert channel. That is, the channel to transmit on irrespective of the \n        current channel being scanned. If set, this must be a reference to a channel. If the tag \n        <token>!selected</token> is used here, the channel from which the scan got started is used \n        as the transmit channel. If omitted the radio will transmit on the currently scanned \n        channel.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channels</token></term>\n    <listitem>\n      <para>Specifies the list of channels to scan. Must be a list of channel IDs.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/smsextension.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extMessages\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>SMS Extension</title>\n</info>\n\n<para>\n  This extension allows for configuring the SMS (text message) feature of all DMR radios. For now, \n  it is implemented as an extension, but might be changed into a common core setting in the future.\n</para>\n\n<para>\n  This extension only has two elements: the SMS format and a list of predefined text messages. The \n  latter is important for devices that do not have a keypad and thus do not allow for entering any \n  messages. E.g., the Radioddity GD-73.\n</para>\n\n<para>\n  As this extension configures a common feature, the extension is always present.\n</para>\n\n<example>\n  <title>Example of a single SMS pre-defined text or template.</title>\n  <programlisting><![CDATA[sms:\n  format: Motorola\n  templates:\n    - id: sms1\n      name: RSSI\n      message: RSSI\n]]></programlisting>\n</example>\n\n<section>\n<info> <title>Common SMS settings</title> </info>\n\n<variablelist>\n  <varlistentry>\n    <term><token>format</token></term>\n    <listitem>\n      <para>\n        Specifies the SMS format to be used. This must be one of <token>Motorola</token>, \n        <token>Hytera</token> or <token>DMR</token>. If an incompatible format is chosen, you cannot \n        receive or send text messages.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n\n<section>\n<info> <title>Message templates</title> </info>\n  \n<para>\n  The <token>templates</token> key defines a list of SMS templates to be programmed onto the radio. \n  Each template is a config object with a name and ID. The latter can be used to reference the \n  template somewhere else in the codeplug. \n</para>\n\n<para>\n  Each template has the following properties:\n</para>\n\n<variablelist>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>\n        Specifies the unique ID of the message. This can be used to reference the message.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>\n        Specifies the display name of the message. Please note, that not all radios store \n        descriptive names alongside with the preset messages.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>message</token></term>\n    <listitem>\n      <para>\n        The actual preset message. The length might differ but is usually limited to 144 chars.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>\n</section>"
  },
  {
    "path": "doc/manual/codeplug/tyt/buttonsettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extTyTButtonSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Button settings extension</title>  \n</info>\n\n<para>\n  This extension to the codeplug allows to specify the function for each of the programmable \n  buttons on the radio. Please note that not all <trademark>TyT</trademark> radios have all \n  buttons described \n  here. \n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[tytExtension:\n  buttonSettings:\n    longPressDuration: 1000 \n    sideButton1Short: Disabled\n    sideButton1Long: Tone1750Hz\n    sideButton2Short: MonitorToggle\n    sideButton2Long: Disabled\n    sideButton3Short: Disabled\n    sideButton3Long: Disabled\n    progButton1Short: Disabled\n    progButton1Long: Disabled\n    progButton2Short: Disabled\n    progButton2Long: Disabled\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Button settings fields</title>\n  <varlistentry>\n    <term><token>sideButton1Short</token></term>\n    <term><token>sideButton1Long</token></term>\n    <term><token>sideButton2Short</token></term>\n    <term><token>sideButton2Long</token></term>\n    <term><token>sideButton3Short</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <term><token>sideButton3Long</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <term><token>progButton1Short</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <term><token>progButton1Long</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <term><token>progButton2Short</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <term><token>progButton2Long</token> (Retevis RT84, Baofeng DM-1701)</term>\n    <listitem>\n      <para>Specifies the different functions for each button press. Must be one of:\n        <token>Disabled</token>, <token>ToggleAllAlertTones</token>, <token>EmergencyOn</token>, \n        <token>EmergencyOff</token>, <token>PowerSelect</token>, <token>MonitorToggle</token>, \n        <token>OneTouch1</token>-<token>OneTouch6</token>, <token>RepeaterTalkaroundToggle</token>, \n        <token>ScanToggle</token>, <token>SquelchToggle</token>, <token>PrivacyToggle</token>,\n        <token>VoxToggle</token>, <token>ZoneIncrement</token>, <token>BatteryIndicator</token>,\n        <token>LoneWorkerToggle</token>, <token>RecordToggle</token>, <token>RecordPlayback</token>,\n        <token>RecordDeleteAll</token>, <token>Tone1750Hz</token>, <token>SwitchUpDown</token>,\n        <token>RightKey</token>, <token>LeftKey</token> or <token>ZoneDecrement</token>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>longPressDuration</token></term>\n    <listitem>\n      <para>Specifies the long-press duration in milliseconds.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/tyt/channel.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extTyTChannelSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Channel extension</title>  \n</info>\n\n<para>\n  This extension to the codeplug allows to set device specific channel settings for many \n  <trademark>TyT</trademark> radios (and therefore also many <trademark>Retevis</trademark> \n  radios). Not all settings are present in all radios. Unsupported settings are ignored \n  during encoding or set to the default value during decoding.\n</para>\n\n<example>\n  <title>Example channel settings specifying the default values for <trademark>TyT</trademark> \n    <trademark>Retevis</trademark> devices.</title>\n  <programlisting><![CDATA[tyt:\n  autoScan: false\n  emergencyAlarmConfirmed: false\n  displayPTTId: true\n  rxRefFrequency: Low\n  txRefFrequency: Low\n  tightSquelch: false\n  compressedUDPHeader: false\n  killTone: Off\n  inCallCriterion: Always\n  allowInterrupt: false\n  dcdmLeader: false\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Common channel setting fields</title>\n  <varlistentry>\n    <term><token>autoScan</token></term>\n    <listitem>\n      <para>If <token>true</token>, the auto-scan feature is enabled.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>emergencyAlarmConfirmed</token></term>\n    <listitem>\n      <para>\n        Enables the confirmation of emergency calls. These fields are usually\n        disabled as the radio will first establish the connection before the actual call can be \n        started.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>displayPTTId</token></term>\n    <listitem>\n      <para>If <token>true</token>, the received analog PTT will be shown.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>rxRefFrequency</token></term>\n    <term><token>rxRefFrequency</token></term>\n    <listitem>\n      <para>\n        Specifies some weird reference frequency setting for RX and TX. By default the value <token>Low</token> is used. Possible values are \n        <token>Low</token>, <token>Medium</token> and <token>High</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n<variablelist>\n  <title>Channel settings for <trademark>TyT</trademark> <productname>MD-390</productname> and \n    <trademark>Retevis</trademark> <productname>RT8</productname></title>\n  <varlistentry>\n    <term><token>tightSquelch</token></term>\n    <listitem>\n      <para>If set to <token>true</token>, the silent squelch is used. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>compressedUDPHeader</token></term>\n    <listitem>\n      <para>Some unknown flag. Usually disabled.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n<variablelist>\n  <title>Channel settings for <trademark>TyT</trademark> <productname>MD-UV390</productname> and \n    <trademark>Retevis</trademark> <productname>DM-2017</productname></title>\n  <varlistentry>\n    <term><token>killTone</token></term>\n    <listitem>\n      <para>\n        Specifies the kill tone. Possible values are <token>Off</token>, <token>Tone259_2Hz</token>\n        and <token>Tone55_2Hz</token>. Disabling or setting the kill tone to 259.2Hz or 55.2Hz, \n        respectively.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>inCallCriterion</token></term>\n    <listitem>\n      <para>Specifies the in-call criterion. Possible values are <token>Always</token>, \n      <token>AdmitCriterion</token> and <token>TXInterrupt</token>.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>allowInterrupt</token></term>\n    <listitem>\n      <para>\n        Enables/disables interrupts if <token>inCallCriterion</token> is set to \n        <token>TXInterrupt</token>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dcdmLeader</token></term>\n    <listitem>\n      <para>If DCDM is enabled, this flag specifies whether this radio is the channel leader. That \n        is, if this radio provides the clock for the time-slots.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>dmrSquelch</token></term>\n    <listitem>\n      <para>Allows to set the squelch level for DMR channels. This prevents the LED to light all \n      the time. The radio, however, remains silent irrespective of this setting. </para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n</section>"
  },
  {
    "path": "doc/manual/codeplug/tyt/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<section xml:id=\"extTyT\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title><trademark>TyT</trademark> Codeplug Extensions</title>\n</info>\n\n<para>\n  This section collects all device specific extensions to the codeplug for <trademark>TyT</trademark>, \n  <trademark>Retevis</trademark> and some <trademark>Baofeng</trademark> devices.\n</para>\n\n<para>\n  These extensions are applicable to <trademark>TyT</trademark> <productname>MD-390</productname>,\n  <productname>MD-UV380</productname>, <productname>MD-UV390</productname>, \n  <productname>MD-2017</productname>, <trademark>Retevis</trademark> \n  <productname>RT8</productname>, <productname>RT3S</productname>, <productname>RT82</productname>,\n  <productname>RT84</productname> and <trademark>Baofeng</trademark> \n  <productname>DM-1701</productname> radios.\n</para>\n\n<xi:include href=\"channel.xml\"/>\n<xi:include href=\"scanlist.xml\"/>\n<xi:include href=\"buttonsettings.xml\"/>\n<xi:include href=\"menusettings.xml\"/>\n<xi:include href=\"radiosettings.xml\"/>\n\n</section>"
  },
  {
    "path": "doc/manual/codeplug/tyt/menusettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extTyTMenuSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Menu settings extension</title>  \n</info>\n\n<para>\n  This extension to the codeplug allows to specify which menu items are enabled. This feature is \n  frequently used in commercial applications to restrict the user to change certain settings in\n  the radio.\n</para>\n\n<example>\n  <title></title>\n  <programlisting><![CDATA[tytExtension:\n  menuSettings:\n    hangtimeIsInfinite: false\n    hangTime: 10\n    textMessage: true\n    callAlert: true\n    contactEditing: true\n    manualDial: true\n    remoteRadioCheck: true\n    remoteMonitor: true\n    remoteRadioEnable: true\n    remoteRadioDisable: true\n    scan: true\n    scanListEditing: true\n    callLogMissed: true\n    callLogAnswered: true\n    callLogOutgoing: true\n    talkaround: true\n    alertTone: true\n    power: true\n    backlight: true\n    bootScreen: true\n    keypadLock: true\n    ledIndicator: true\n    squelch: true\n    vox: true\n    password: true\n    displayMode: true\n    radioProgramming: true\n    gpsInformation: true\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Menu settings fields</title>\n  <varlistentry>\n    <term><token>hangtimeIsInfinite</token></term>\n    <term><token>hangTime</token></term>\n    <listitem>\n      <para>\n        Specify the menu hang time. That is the duration, the menu is shown without any user \n        action. If <token>hangtimeIsInfinite</token> is <token>true</token>, the menu is shown \n        indefinitely, irrespective of the <token>hangTime</token> setting. If \n        <token>hangtimeIsInfinite</token>  is <token>false</token>, the <token>hangTime</token> \n        specifies the menu hang time in seconds.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>textMessage</token></term>\n    <listitem>\n      <para>Enables the text-messaging menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callAlert</token></term>\n    <listitem>\n      <para>Enables the call alert menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>contactEditing</token></term>\n    <listitem>\n      <para>Enables the contact editing menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>manualDial</token></term>\n    <listitem>\n      <para>Enables manual dial.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>remoteRadioCheck</token></term>\n    <listitem>\n      <para>Enables the remote radio-check menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>remoteMonitor</token></term>\n    <listitem>\n      <para>Enables the remote monitor menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>remoteRadioEnable</token></term>\n    <term><token>remoteRadioDisable</token></term>\n    <listitem>\n      <para>Enables the remote radio enable and disable menu items.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>scan</token></term>\n    <term><token>scanListEditing</token></term>\n    <listitem>\n      <para>Enables the scan and scan list editing menu items.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callLogMissed</token></term>\n    <term><token>callLogAnswered</token></term>\n    <term><token>callLogOutgoing</token></term>\n    <listitem>\n      <para>Enables the menu items for the list of missed, answered and outgoing calls.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>talkaround</token></term>\n    <listitem>\n      <para>Enables the talkaround menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>alertTone</token></term>\n    <listitem>\n      <para>Enables the alert tone settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>power</token></term>\n    <listitem>\n      <para>Enables the power settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>backlight</token></term>\n    <listitem>\n      <para>Enables the backlight settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bootScreen</token></term>\n    <listitem>\n      <para>Enables the boot-screen settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keypadLock</token></term>\n    <listitem>\n      <para>Enables the keypad lock settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>ledIndicator</token></term>\n    <listitem>\n      <para>Enables the LED indicator settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>squelch</token></term>\n    <listitem>\n      <para>Enables the squelch settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>vox</token></term>\n    <listitem>\n      <para>Enables the VOX settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>password</token></term>\n    <listitem>\n      <para>Enables the password settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>displayMode</token></term>\n    <listitem>\n      <para>Enables the display mode settings menu item.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>radioProgramming</token></term>\n    <listitem>\n      <para>Enables radio programming from the keypad. E.g., editing channels etc.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>gpsInformation</token></term>\n    <listitem>\n      <para>Enables the GPS information menu item. This setting has only an effect on radios \n        supporting GPS.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/tyt/radiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extTyTRadioSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Radio settings extension</title>  \n</info>\n\n<para>\n  This extension allows to set the device-specific general settings for <trademark>TyT</trademark>\n  and <trademark>Retevis</trademark> radios. It extends the <token>settings</token> section of \n  the codeplug. \n</para>\n\n<example>\n  <title>Radio settings extension example showing default values.</title>\n  <programlisting><![CDATA[settings:\n  [...]\n  tyt:\n    montitorType: Open\n    allLEDsDisabled: false\n    talkPermitToneDigital: false\n    talkPermitToneAnalog: false\n    passwordAndLock: false\n    channelFreeIndicationTone: true\n    allTonesDisabled: false\n    powerSaveMode: true\n    wakeupPreamble: true\n    bootPicture: true\n    channelMode: true\n    channelModeA: true\n    channelModeB: true\n    txPreambleDuration: 600\n    channelHangTime: 3000\n    groupCallHangTime: 3000\n    privateCallHangTime: 3000\n    lowBatteryWarnInterval: 120\n    callAlertToneContinuous: false\n    callAlertToneDuration: 0\n    loneWorkerResponseTime: 1\n    loneWorkerReminderTime: 10\n    digitalScanHangTime: 1000\n    analogScanHangTime: 1000\n    backlightAlwaysOn: false\n    backlightDuration: 10\n    keypadLockManual: true\n    keypadLockTime: 60\n    powerOnPasswordEnabled: false\n    powerOnPassword: 0\n    radioProgPasswordEnabled: false\n    radioProgPassword: 0\n    pcProgPassword: null\n    privateCallMatch: true\n    groupCallMatch: true\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Radio settings fields</title>\n  <varlistentry>\n    <term><token>montitorType</token></term>\n    <listitem>\n      <para>\n        Specifies the monitor type. Possible values are <token>Open</token> and \n        <token>Silent</token>. This setting only affects analog channels. If \n        <token>Open</token> is specified, the squelch will open if monitoring\n        is enabled.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>allLEDsDisabled</token></term>\n    <listitem>\n      <para>If enabled, all LEDs are disabled.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>talkPermitToneDigital</token></term>\n    <listitem>\n      <para>If enabled, a talk-permit tone will sound on digital channels.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>talkPermitToneAnalog</token></term>\n    <listitem>\n      <para>If enabled, a talk-permit tone will sound on analog channels.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>passwordAndLock</token></term>\n    <listitem>\n      <para>Enables and disables the passwords and keypad locks globally. They can also be \n        enabled/disabled individually below.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channelFreeIndicationTone</token></term>\n    <listitem>\n      <para>If enabled, a tone will sound after a transmission has ended to indicate that the \n        channel is free again.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>allTonesDisabled</token></term>\n    <listitem>\n      <para>If enabled, all tones (talk permit, channel free, etc.) are disabled globally.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerSaveMode</token></term>\n    <listitem>\n      <para>Puts the radio is a sleep mode when in idle state (no traffic on the channels). This \n        allows for some power saving. However, the radio will need some time to wake up. \n        Consequently, all other radios in the network need to transmit a wake-up preamble. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>wakeupPreamble</token></term>\n    <listitem>\n      <para>Enables the transmission of a short wake-up preamble allowing receiving radios to \n        wake-up in time for the actual transmission. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>bootPicture</token></term>\n    <listitem>\n      <para>\n        If enabled, a picture is shown during boot rather than the boot text (see \n        <token>introLine1</token> and <token>introLine2</token> in the general settings section).\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channelMode, channelModeA, channelModeB</token></term>\n    <listitem>\n      <para>\n        Controls the mode of the radio, VFO A and VFO B. If <token>channelMode</token>\n        is <token>true</token>, the entire radio operates in channel mode (memory mode). This also\n        overrides the <token>channelModeA</token> and <token>channelModeB</token> settings. If \n        <token>channelMode</token> is set to <token>false</token>, the <token>channelModeA</token> \n        and <token>channelModeB</token> specify the mode for the VFO A and B, respectively.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>txPreambleDuration</token></term>\n    <listitem>\n      <para>Specifies the transmit preamble duration in milliseconds. If this preamble is the \n        wake-up preamble, is unknown. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>channelHangTime</token></term>\n    <listitem>\n      <para>\n        Specifies the channel hang time in milliseconds.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>groupCallHangTime</token>, <token>privateCallHangTime</token></term>\n    <listitem>\n      <para>\n        Specifies the group-call hang time in milliseconds. This is the time a group or private \n        call can be answered directly even if that group call is not the default contact of the \n        channel. See also <xref linkend=\"glHangTime\">hang time</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>lowBatteryWarnInterval</token></term>\n    <listitem>\n      <para>Specifies the period of the low-battery warn tone in seconds.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callAlertToneContinuous</token></term>\n    <listitem>\n      <para>If enabled, a call-alert tone will sound until the operator reacts. This setting \n        overrides the <token>callAlertToneDuration</token> setting.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>callAlertToneDuration</token></term>\n    <listitem>\n      <para>\n        Specifies the call alert-tone duration in seconds. If \n        <token>callAlertToneContinuous</token> is enabled, the alert tone will be continuous \n        irrespective of this setting. \n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>loneWorkerResponseTime</token></term>\n    <listitem>\n      <para>\n        Sets the lone-worker response time in minutes. See also \n        <xref linkend=\"glLoneWorker\">lone worker</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>loneWorkerReminderTime</token></term>\n    <listitem>\n      <para>\n        Sets the lone-worker reminder time in minutes. See also \n        <xref linkend=\"glLoneWorker\">lone worker</xref>.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>digitalScanHangTime</token>, <token>analogScanHangTime</token></term>\n    <listitem>\n      <para>Specifies the time in milliseconds, the radio will continue monitoring a DMR or FM \n        channel after the transmission on that channel ended. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>backlightAlwaysOn</token></term>\n    <listitem>\n      <para>If enabled, the backlight will stay on.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>backlightDuration</token></term>\n    <listitem>\n      <para>Specifies the backlight duration in seconds.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keypadLockManual</token></term>\n    <listitem>\n      <para>If enabled, the keypad lock is enabled manually. If not, the keypad lock gets enabled \n        automatically after a specified period (see <token>keypadLockTime</token>). </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>keypadLockTime</token></term>\n    <listitem>\n      <para>Specifies the time, after which the keypad lock is engaged automatically unless \n        <token>keypadLockManual</token> is enabled.\n      </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerOnPasswordEnabled</token></term>\n    <listitem>\n      <para>Enables the power-on password. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>powerOnPassword</token></term>\n    <listitem>\n      <para>Specifies the power-on password. An 8-digit number. This password must then be entered \n        when booting the radio.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>radioProgPasswordEnabled</token></term>\n    <listitem>\n      <para>Enables the radio-programming password.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>radioProgPassword</token></term>\n    <listitem>\n      <para>Sets the radio-programming password. An 8-digit number. This password must then be \n        entered when making any changes to the radio/channel settings through the keypad. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>pcProgPassword</token></term>\n    <listitem>\n      <para>Specifies the PC programming password. This password is then needed when programming \n        the radio through the CPS. An empty string disables this password. </para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>privateCallMatch</token>, <token>groupCallMatch</token></term>\n    <listitem>\n      <para>If <token>true</token>, the private and group call IDs must match.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/tyt/scanlist.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"extTyTScanListSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n\n<info>\n  <title>Scan-list settings extension</title>  \n</info>\n\n<para>\n  This extension to the codeplug allows to specify device specifics settings for scan-lists. \n</para>\n\n<example>\n  <title>Scan-list settings example showing the default values.</title>\n  <programlisting><![CDATA[tyt:\n  holdTime: 500\n  prioritySampleTime: 2000\n]]></programlisting>\n</example>\n\n<variablelist>\n  <title>Scan-list settings fields</title>\n  <varlistentry>\n    <term><token>holdTime</token></term>\n    <listitem>\n      <para>Specifies the hold time in ms.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>prioritySampleTime</token></term>\n    <listitem>\n      <para>Specifies the sample-time in ms for priority channels.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n"
  },
  {
    "path": "doc/manual/codeplug/zones.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"cpZones\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Zones</title>\n</info>\n\n<para>\n  The <token>zones</token> element collects all zones defined within the codeplug. It is just a \n  list of zone definitions. Each zone has an ID, name and one or two lists of channels. \n  One for VFO A and one for VFO B. Depending on the radio, a zone will be split automatically into\n  two zones if the radio handles separate zones for each VFO. \n</para>  \n\n<para>\n  As an example, consider the following zone:\n</para>\n\n<example pgwide=\"1\">\n  <title>Zone definition</title>\n  <programlisting><![CDATA[zones:\n  - id: zone1\n    name: KW\n    A: [ch1, ch4, ch5, ch2, ch6, ch11, ch12, ch13, ch15, ch16, ch76, ch77, ch78, ch79]\n    B: [ch97, ch101, ch102, ch103, ch68, ch69, ch70, ch71, ch72, ch73, ch74, ch75]\n]]></programlisting>\n</example>\n\n<para>\n  This zone has the name <emphasis>KW</emphasis> and contains two lists of channels. One for each VFO. On radios, \n  where each VFO is assigned a zone individually, this zone will be split into two: <emphasis>KW A</emphasis> and \n  <emphasis>KW B</emphasis> to match the radios configuration. \n</para>\n\n<variablelist>\n  <title>Zone fields</title>\n  <varlistentry>\n    <term><token>id</token></term>\n    <listitem>\n      <para>Specifies the ID of the zone. For now, there are no codeplug elements that refer to zones.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>name</token></term>\n    <listitem>\n      <para>Specifies the name of the zone. Any string is valid here.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>A</token></term>\n    <listitem>\n      <para>Specifies the channel list for VFO A. This must be a list of references to channels.</para>\n    </listitem>\n  </varlistentry>\n  <varlistentry>\n    <term><token>B</token></term>\n    <listitem>\n      <para>Optional channel list for VFO B. If present, must be a list of references to channels.</para>\n    </listitem>\n  </varlistentry>\n</variablelist>\n\n</section>\n"
  },
  {
    "path": "doc/manual/conf/analogchannels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confAnalogChannels\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Analog channel table</title>\n</info>\n\n<para>\n  The analog channel table collects all analog (FM) channels. As digital channels have some\n  different options compared to analog channels, they are not defined within the same table.\n  However, they share the same IDs. So be careful not to assign the same identifier to analog and\n  digital channels.\n</para>\n\n<para>\n  The analog channel table has the form\n</para>\n\n<informalexample pgwide=\"1\">\n  <programlisting><![CDATA[Analog  Name             Receive   Transmit Power Scan TOT RO Admit  Squelch RxTone TxTone Width APRS\n1       \"Y07\"            144.6750  144.6750  High  1    -   -  Free   1       -      -     12.5  -\n2       \"S20\"            145.3000  145.3000  High  -    -   -  Free   1       -      -     12.5  -\n3       \"Mobil 2m\"       145.5000  145.5000  High  -    -   -  Free   1       -      -     12.5  2\n4       \"DB0RAG\"         439.3000  -7.6000   High  1    -   -  Free   3       -      -     12.5  -\n5       \"DB0LOS\"         438.7750  -7.6000   High  1    -   -  Free   1       -      -     12.5  -\n6       \"DB0LUD\"         438.5750  -7.6000   High  1    -   -  Free   1       -      67    12.5  -\n19      \"DB0BLO\"         439.2750  -7.6000   High  1    -   -  Free   3       -      -     12.5  -\n20      \"DB0SP-2\"        145.6000  -0.6000   High  1    -   -  Free   3       -      -     12.5  -\n21      \"DB0SP-70\"       439.4250  -7.6000   High  1    -   -  Free   3       -      -     12.5  -\n]]></programlisting>\n</informalexample>\n\n<para>\n  The analog channel table starts with the \"Analog\" keyword and ends with an empty line. The\n  remaining keywords right after \"Analog\" (i.e., \"Name\", \"Receive\", \"Transmit\", \"Power\", \"Scan\",\n  \"TOT\", \"RO\", \"Admit\", \"Squelch\", \"RxTone\", \"TxTone\" and \"Width\") are ignored but are part of the\n  self-documentation of the config file.\n</para>\n\n<para>\n  Each line within the table specifies a single channel. The first column specifies the unique ID\n  of the channel. This ID can by any number that is unique among analog AND digital channels. The\n  second (Name) column specifies the name of the channel as a string. Any string can be used here.\n</para>\n<para>\n  The third (Receive) column specifies the RX frequency of the channel in MHz. The fourth\n  (Transmit) column specifies the TX frequency in MHz or alternatively, an offset relative to the\n  receive frequency in MHz by prefixing \"+\" or \"-\". \n</para>\n<para>\n  The 5th (Power) column specifies the transmit\n  power. This must be either the \"High\" or \"Low\" keyword. The 6th (Scan) column specifies the\n  scan-list ID for this channel or \"-\" if there is no scan-list assigned to the channel. A scan-list\n  (see below) is just a collection of channels that gets scanned whenever scanning is started on a\n  particular channel. \n</para>\n<para>\n  The 7th (TOT) column specifies the transmit time-out in seconds or \"-\" if\n  disabled. The 8th (RO) column specifies whether this channel is receive-only with either \"-\"\n  meaning disabled and \"+\" enabled. If enabled, it is impossible to transmit on that channel. \n</para>\n<para>\n  The 9th column specifies the admit criterion on that channel. This must be either \"-\" meaning that\n  there is no restriction when to send on that channel, the keyword \"Free\" meaning that the channel\n  must be free to transmit or the keyword \"Tone\" meaning that the channel must be free and the\n  RxTone must match. \n</para>\n<para>\n  The 10th (Squelch) column specifies the squelch level for the channel. This\n  must be a number between [0-10]. The larger the value, the stronger the signal must be to open\n  the squelch. The value 0 disables the squelch. \n</para>\n<para>\n  The 11th (RxTone) specifies the receive CTCSS tone\n  frequency in Hz. The squelch will then only open, if the signal is strong enough (see previous\n  column) and the specified tone is received. If set \"-\" the RX tone is disabled and the squelch\n  will open if the signal is strong enough. The 12th (TxTone) column specifies the CTCSS tone\n  to transmit in Hz or \"-\" if disabled. This feature is used by some repeaters to open their\n  squelch and to start repeating to avoid conflicts between repeaters operating on the same\n  frequency (e.g., in case of DX conditions). \n</para>\n<para>\n  The 13th (Width) column specifies the\n  bandwidth of the channel in kHz. This can be 12.5kHz narrow-band or 25kHz wide-band.\n  Finally, the 14th column specifies the APRS system ID to use or \"-\" for <emphasis>APRS \n  disabled</emphasis>.\n</para>\n\n</section>\n"
  },
  {
    "path": "doc/manual/conf/aprssystems.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confAPRS\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>APRS Systems</title>\n</info>\n\n<para>\n  The APRS system list specifies the various information for transmitting your position using analog \n  APRS. As digital channels may use either DMR or analog APRS for position reporting, this list shares \n  a namespace with the GPS system list. That is, the ID must be unique across both lists.\n</para>\n\n<informalexample pgwide=\"1\">\n  <programlisting><![CDATA[APRS Name                Channel Period Source      Destination Path                   Icon        Message\n2    \"APRS APAT81\"       103     300    DM3MAT-7    APAT81-0    \"WIDE1-1WIDE2-1\"       \"Jogger\"    \"Y07\" \n]]></programlisting>\n</informalexample>\n\n<para>\n  The first column specifies the ID of the APRS positioning system. This must be unique across APRS and DMR \n  position reporting systems. The second column specifies the name of the system as a string. the third \n  column specifies the revert channel. That is, the analog channel the APRS information is transmitted on.\n  The 4th column specifies the period with which the position gets reported. The 5th and 6th columns specify \n  the source and destination calls and SSIDs respectively. The 7th column specifies the path string. This is \n  list of calls and SSIDs stored as a string without any separators. The 8th column specifies the map \n  <link xlink:href=\"http://www.aprs.org/symbols/symbols-new.txt\">icon name</link>. The name does not \n  need to match the official icon name exactly. The icon is identified as the closes matching icon name with \n  respect to the <link xlink:href=\"https://en.wikipedia.org/wiki/Levenshtein_distance\">Levenshtein \n  distance</link> between the given and all icon names. That is, <emphasis>jogger</emphasis> and \n  <emphasis>jogging</emphasis> will select the same icon. Finally, the 9th column specifies a freely \n  selectable text to be sent with the position report.\n</para>\n\n</section>"
  },
  {
    "path": "doc/manual/conf/contacts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confContacts\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Contact table</title>\n</info>\n\n<para>\n  The contact table is a list of DMR contacts like\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Contact Name             Type    ID       RxTone\n1       \"DM3MAT\"         Private 2621370  +\n2       \"DMR All Call\"   All     16777215 -\n3       \"Simplex TG99\"   Group   99       -\n4       \"Global\"         Group   91       -\n5       \"EU\"             Group   92       -\n6       \"Regional\"       Group   8        -\n7       \"Local\"          Group   9        -\n8       \"Deutschland\"    Group   262      -\n9       \"Berlin/Brand\"   Group   2621     -\n10      \"Berlin City\"    Group   26212    -\n11      \"Brandenburg\"    Group   26209    -\n12      \"Sachs/Thu\"      Group   2629     -\n13      \"R Brandenburg\"  Private 4044     -  \n]]></programlisting>\n</informalexample>\n\n<para>\n  These contacts can be personal contacts like DM3MAT, so-called all-calls and group calls.\n  The contact table starts with the \"Contact\" keyword and ends with an empty line. The remaining\n  keywords (\"Name\", \"Type\", \"ID\", \"RxTone\") are ignored, however, they are part of the\n  self-documentation of the config file.\n</para>\n\n<para>\n  Following the \"Contact\" keyword, each line\n  represents a single contact in the contact list. The first column represents a unique\n  internal ID for the contact. It must not necessarily be in ascending order, any unique number\n  will do. The second column is the name of the contact. Any string can be used here. The third\n  column specifies the type of the contact. This must be one of the keywords \"Private\", \"Group\"\n  or \"All\", meaning private, group or all-calls, respectively. The fourth column specifies the DMR ID\n  for the contact. Please note, that an all-call requires the specific DMR ID 16777215 to work\n  as an all-call. The last column specifies, whether an incoming call from this contact will cause a\n  ring-tone. Here \"+\" means enabled/yes and \"-\" disabled/no.\n</para>\n\n</section>\n"
  },
  {
    "path": "doc/manual/conf/digitalchannels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confDigitalChannels\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Digital channel table</title>\n</info>\n\n<para>\n  The digital channel table defines all digital DMR channels. As digital channels have some\n  different options compared to analog channels, they are not defined within the same table.\n  However, they share the same IDs. So be careful not to assign the same identifier to analog and\n  digital channels.\n</para>\n\n<para>\n  The digital channel table has the form\n</para>\n\n<informalexample pgwide=\"1\">\n  <programlisting><![CDATA[Digital Name             Receive   Transmit  Power Scan TOT RO Admit  CC TS RxGL TxC GPS Roam ID\n11      \"DM0TT Ref\"      439.0870  -7.6000   High  1    -   -  Free   1  1  7    12  -   +    -  \n12      \"DM0TT BB\"       439.0875  -7.6000   High  -    -   -  Free   1  2  7    15  1   +    -  \n84      \"DMR_S0\"         433.4500  433.4500  High  2    -   -  Free   1  1  6    9   -   +    -  \n85      \"DMR_S1\"         433.6120  433.6120  High  2    -   -  Free   1  1  6    9   -   +    -  \n86      \"DMR_S2\"         433.6250  433.6250  High  2    -   -  Free   1  1  6    9   -   +    -  \n87      \"DMR_S3\"         433.6380  433.6380  High  2    -   -  Free   1  1  6    9   -   +    -  \n88      \"DMR_S4\"         433.6500  433.6500  High  2    -   -  Free   1  1  6    9   -   +    -  \n89      \"DMR_S5\"         433.6630  433.6630  High  2    -   -  Free   1  1  6    9   -   +    -  \n90      \"DMR_S6\"         433.6750  433.6750  High  2    -   -  Free   1  1  6    9   -   +    -  \n91      \"DMR_S7\"         433.6880  433.6880  High  2    -   -  Free   1  1  6    9   -   +    -  \n]]></programlisting>\n</informalexample>\n\n<para>\n  The digital-channel table starts with the keyword \"Digital\" and ends with an empty line. The\n  next keywords (Name, Receive, Transmit, Power, Scan, TOT, RO, Admit, CC, TS, RxGL and\n  TxC, GPS, Roam, ID) are ignored and are maintained for the self-documentation of the configuration file.\n</para>\n\n<para>\n  Each channel is defined within a single line. The first column is the unique channel\n  identifier (any unique number among analog AND digital channels). The second column specifies the\n  channel name as a string. \n</para>\n<para>\n  The third column specifies the RX frequency in MHz and the fourth\n  column the TX frequency in MHz. Alternatively, a TX frequency can also be specified in terms of\n  an offset relative to the RX frequency. In this case, the offset must be prefixed with either\n  \"+\" or \"-\". \n</para>\n<para>\n  The 5th (Power) column specifies the power level to use. Here, either the \"High\" or \"Low\"\n  keyword must be used. The 6th (Scan) column specifies the ID of the scan-list (see below) attached to\n  the channel. This list will be used whenever a scan is started on this channel. \n</para>\n<para>\n  The 7th column\n  (TOT) column specifies the TX time-out-timer in seconds or \"-\", if disabled. The 8th column\n  (RO) specifies whether the channel is RX only (\"+\") or not (\"-\"). If enabled, you cannot transmit\n  on that particular channel. \n</para>\n<para>\n  The 9th (Admit) column specifies the TX admit criterion for the\n  channel. This must be either \"-\" or one of the keywords \"Free\" and \"Color\". \"-\" indicates that\n  there is no restriction in transmitting on that channel. The radio will transmit whenever PTT is\n  pressed. The \"Free\" keyword indicates that the radio will only transmit if the channel is free.\n  The \"Color\" keyword indicates that the radio will only transmit if the channel is free and the\n  color-code of the repeater matches the specified color-code of the channel (see next column). \n</para>\n<para>\n  The 10th (CC) column specifies the\n  color-code of the channel. To avoid interference between neighboring radios and repeaters on\n  the same frequency (in case of DX conditions), the repeater and radio will only react to\n  transmissions on a channel with the matching color-code. The color-code can be any number between\n  0 and 15. \n</para>\n<para>\n  The 11th (TS) column specifies the time-slot for this channel. Due to the audio\n  compression used in DMR, it is possible to operate two independent channels on a single\n  frequency by using time-slicing. DMR uses two time-slots. This option specifies which of the\n  two time-slots is used for the channel. On simplex channels, this time-slicing is irrelevant, as\n  there is no central instance (the repeater) that defines what time-slot 1 or 2 is. \n</para>\n<para>\n  The 12th (GPS)\n  column specifies the GPS or APRS system (see below) to use on that channel. The 13th (Roam) column \n  specifies the roaming zone. This can either be '-' meaning <emphasis>roaming disabled</emphasis>\n  or an ID of a roaming zone specified below. Finally, the 14th column (ID) specifies the \n  DMR ID to use with this channel. That is either '-' for <emphasis>default ID</emphasis> or an index \n  (0-based) of the ID list above.\n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/conf/format.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<chapter xml:id=\"confFormat\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n         \n<info>\n  <title>Table Based Codeplug Format</title>\n</info>        \n\n<warning>\n  <para>\n    This chapter describes the old and deprecated table based <filename>conf</filename> text-file\n    format. This format is already incomplete and will remains so. This documentation is maintained\n    for the sake of completeness but you should not use that format in future.\n  </para> \n  <para>\n    <application>qdmr</application> will still be able to import this format but it will not save \n    codeplugs in that format anymore. The new codeplug format is documented in \n    <xref linkend=\"codeplug\"/>.\n  </para>\n</warning>\n\n<para>\n  This configuration file format represents a generic configuration \n  for a wide variety of radios. It is a simple text file containing simple key-value\n  definitions like the DMR ID as well as tables like the table of channels, contacts, etc.\n</para>\n\n<para>\n  The aim of this config format is to be human-readable and writable. This would allow\n  users to write config file by hand and share them easily, as well as enable users to modify shared\n  configurations using a text editor. To this end, the format must be intuitive and to some\n  degree self-documenting.\n</para>\n\n<para>\n  Within the following sections, I will describe that text format in some detail.\n</para>\n\n\n<section>\n<info>\n  <title>Line comments</title>\n</info>\n  \n<para>\n  To document your configuration, you may use so-called line-comments. These comments start with the\n  character <code>#</code> and end at the end-of-line.\n</para>\n\n<informalexample>\n  <programlisting language=\"ini\"><![CDATA[# A comment, everything in this line is ignored\nID: 12345678   # Another comment\n]]></programlisting>\n</informalexample>\n</section>\n\n\n<xi:include href=\"radiosettings.xml\"/>\n<xi:include href=\"contacts.xml\"/>\n<xi:include href=\"grouplists.xml\"/>\n<xi:include href=\"digitalchannels.xml\"/>\n<xi:include href=\"analogchannels.xml\"/>\n<xi:include href=\"zones.xml\"/>\n<xi:include href=\"scanlists.xml\"/>\n<xi:include href=\"gpssystems.xml\"/>\n<xi:include href=\"aprssystems.xml\"/>\n<xi:include href=\"roaming.xml\"/>\n\n</chapter>"
  },
  {
    "path": "doc/manual/conf/gpssystems.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confGPS\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>GPS Systems</title>\n</info>\n\n<para>\n  The GPS system list just specifies the contact to which some positional information is sent to\n  (which usually gets forwarded to the APRS system) and at which period this information is sent.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[GPS  Name                Dest Period Revert\n1    \"BM APRS\"           20   300    -\n]]></programlisting>  \n</informalexample>\n\n<para>\n  The first column specifies the ID of the GPS system. This can be any number >0. The second column\n  (Name) specifies the name of the GPS system. The third column specifies the destination contact\n  ID (see Contacts above), the position information is sent to. The fourth column (Period) specifies\n  the update period in seconds. The fifth column (Revert) specifies the revert channel. In amateur\n  radio, this can be left blank (\"-\").\n</para> \n\n</section>"
  },
  {
    "path": "doc/manual/conf/grouplists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confGroupLists\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Group list table</title>\n</info>\n\n<para>\n  Group lists are simple named lists of one or more contacts. These lists may include group,\n  all or even private calls. Group lists are assigned to channels. They form a group of\n  contacts (e.g., talk groups) you may want to listen to on a particular channel. Usually these\n  group lists form a collection of talk groups that are specific for a particular region.\n</para>\n\n<para>\n  Group lists are defined within the config file like\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Grouplist Name            Contacts\n1         \"Local\"          7,9\n2         \"Deutschland\"    8\n3         \"Global\"         4\n4         \"EU\"             5\n5         \"Sachs/Thu\"      13,12\n6         \"Simplex\"        2,3,6,7\n7         \"Berlin/Brand\"   9,10,11,13\n]]></programlisting>\n</informalexample>\n\n<para>\n  The group list table starts with the keyword \"Grouplist\". The following keywords (Name &amp;\n  Contacts) are ignored, but form a kind of self-documentation for the config file.\n</para>\n\n<para>\n  Following the \"Grouplist\" keyword, each group list is defined by a single line. The first\n  column specifies the internal unique ID for the group list. This can be any number as long as\n  it is unique. The second column contains the name of the group list as a string. This can be\n  any non-empty string. The third column contains the comma-separated list of contact IDs that form\n  that group list.\n</para>\n\n</section>\n"
  },
  {
    "path": "doc/manual/conf/radiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confRadioSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>General configuration</title>  \n</info>\n\n\n<para>\n  The general configuration settings of some radios can be overly complex with a huge amount of\n  options. The vast majority of these settings, however, are useless for ham-radio purposes. Thus\n  the possible settings for the general configuration of the radio are reduced to 6 key-value\n  pairs.\n</para>\n\n<para>\n  The DMR ID of cause, is absolutely necessary and specifies your personal DMR number. Keep in\n  mind, that you do NOT need to get a unique DMR ID for each radio you own! All your radios can\n  share the same DMR ID. The DMR ID is specified using the \"ID\" keyword as\n</para>\n\n<informalexample>\n  <programlisting language=\"ini\"><![CDATA[ID: 12345678\n]]></programlisting>\n</informalexample>\n\n<para>\n  In rare situations, where you actually need several different radio IDs (e.g., if you use the \n  same radio for HAM and commercial applications), you may specify them as a comma separated list.\n  The first ID in the list will be handled as the default ID for the radio. \n</para>\n\n<para>\n  The radio name is a string, that the radio may display somewhere on the screen. It does not have\n  any effect on the behavior of the radio or gets transmitted. You may set this entry to your\n  call-sign. For example:\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Name: \"DM3MAT\" \n]]></programlisting>\n</informalexample>\n\n<para>\n  The two intro lines might be shown on the screen of your radio on startup. You may set these\n  to any string you like. They are also cosmetic and don't have any effect on the behavior of\n  your radio. For example\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[IntroLine1: \"Hello\"\nIntroLine2: \"MY0CALL\"   \n]]></programlisting>\n</informalexample>\n\n<para>\n  The microphone sensitivity/amplification can also be set (on some radios) using the MicLevel\n  entry. This entry is a number between 1 and 10. The larger the level, the larger the\n  microphone amplification. This value may vary heavily from model to model.\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[MicLevel: 2\n]]></programlisting>\n</informalexample>\n\n<para>\n  The \"Speech\" option enables the speech synthesis of the radio if supported. Possible settings\n  are \"on\" and \"off\".\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[# Speech-synthesis ('On' or 'Off'):\nSpeech: Off \n]]></programlisting>\n</informalexample>\n\n</section>"
  },
  {
    "path": "doc/manual/conf/roaming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confRoaming\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Roaming Zones</title>\n</info>\n\n<para>\n  Roaming zones allow to stay in contact with a particular talk group when moving round and the current repeater \n  gets out of range. In this case, the radio will search for the strongest repeater in a list (the so-called \n  <emphasis>roaming zone</emphasis>) and switch to this repeater. \n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Roaming Name                Channels\n1       \"Berlin/Brand\"      3,7,13,17,19,23,25,29,33,37,41,49 \n]]></programlisting>\n</informalexample>\n\n<para>\n  Therefore, a roaming zone is a simple channel list. The first column specifies the ID of the zone. This \n  ID can be used in the digital channel table to associate a channel with a specific roaming zone.\n  The second column specifies the name of the zone and the third column holds the comma-separated list of \n  channel in each zone.\n</para>\n\n</section>"
  },
  {
    "path": "doc/manual/conf/scanlists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confScanLists\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Scan lists</title>\n</info>\n\n<para>\n  A scan list is list of channels, that are scanned whenever scanning is started on a\n  channel, the scan list is associated with. A single scan list might be associated with several\n  channels. For example, all channels within that scan list.\n</para>\n\n<para>\n  The list of scan lists has the following form\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Scanlist Name            PCh1 PCh2 TxCh Channels\n1        \"KW\"            1    84   Sel  1,84,2,9,11,8,14,4,5,20,21,19,6\n2        \"DMR Simplex\"   84   -    Sel  84,85,86,87,88,89,90,91\n]]></programlisting>\n</informalexample>\n\n<para>\n  The list of scan lists starts with the \"Scanlist\" keyword and ends with an empty line. The\n  remaining keywords (Name, PCh1, PCh2 &amp; Channels) are ignored but part of the self-documentation\n  of the configuration file format. A scan list is defined with every other line. The first column\n  specifies the unique identifier of the scan list. The second (Name) column specifies the name of\n  the scan list as a string. Any string will do. The third and fourth columns specify the first\n  and second priority channels for the scan list respectively. These priority channels are visited\n  more frequently during the scan. That is, the first priority channel is visited 50% of the time\n  while the second is visited 25% of the time. These channels might also be set to \"-\" indicating\n  that there is no priority channel. The 5th column specifies the transmit channel during the scan.\n  Possible options are \"Last\", \"Sel\" and any valid channel index. The \"Sel\" keyword implies that\n  the radio will transmit on the selected channel when the scan started. The \"Last\" keyword implies\n  that the radio will transmit on the channel at which the scan stopped on, while specifying any\n  channel index implies, that the radio will transmit on that channel. Finally the 6th column\n  specifies the comma-separated list of channels that form the scan list.\n</para>\n\n</section>"
  },
  {
    "path": "doc/manual/conf/zones.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"confZones\"\n         xmlns=\"http://docbook.org/ns/docbook\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Zone lists</title>\n</info>\n\n<para>\n  Zones are just collections of channels. Typical radios can hold thousands of channels. To keep\n  large numbers of channels manageable, they can be organized into zones. Usually, these zones\n  represent a geographical area and all repeaters in that area are then grouped into zones. Of\n  cause, a single channel can be added to multiple zones. Please note that for many radios,\n  channels can only be accessed via a zone. That means, a channel that is not a member of any zone\n  may not be accessible.\n</para>\n  \n<para>\n  The zone table is defined within the configuration file as\n</para>\n\n<informalexample>\n  <programlisting><![CDATA[Zone    Name             VFO Channels\n1       \"KW\"             A   1,9,11,12,14,8,55,15,4,5,6,20,21,22,19,48\n1       \"KW\"             B   1,3,2,81,82,84,85,86,87,88,89,90,91\n2       \"Berlin DMR\"     A   10,9,11,12,34,35,31,32,33,27,28,29,30,38,39\n2       \"Berlin DMR\"     B   1,3,2,81,82,84,85,86,87,88,89,90,91\n3       \"Berlin FM\"      A   20,21,19,18,22,23,24,25\n4       \"Potsdam\"        A   42,43,44,45,46,47,40,41\n7       \"Leipzig\"        A   75,76,72,71,73,70,74,77,78,80,79,69\n8       \"Simplex\"        A   1,3,2,81,82,84,85,86,87,88,89,90,91\n]]></programlisting>\n</informalexample>\n\n<para>\n  The zone table starts with the keyword \"Zone\" and ends with an empty line. The remaining\n  keywords (Name and Channels) are ignored but are part of the self-documentation of the\n  configuration file. The first column specifies an unique identifier for each zone. This can be\n  any integer as log as it is unique. The second (Name) column specifies the name of the zone as a\n  string. Any string is valid here. The third column specifies the VFO (either A or B) for that\n  zone. This allows to specify different channels for the two VFOs of the radio. For example, it\n  allows to specify a list of repeater channels for VFO A and some simplex and calling frequencies\n  on VFO B. The fourth column contains the comma-separated list of channel IDs for the zone and VFO.\n  A reference to any channel-type can be used here, analog and digital.\n</para>\n\n</section>\n"
  },
  {
    "path": "doc/manual/epub/Makefile",
    "content": "all: manual.epub \n\n%.epub: %.xml\n\tpandoc -f docbook -t epub3 -o $@ $<\n\n%.xml:\n\nclean:\n\trm -f manual.epub \n\trm -f manual.xml \n\trm -rf fig/"
  },
  {
    "path": "doc/manual/gui/aprs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiAPRS\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Setup GPS/APRS Position Reporting</title>\n</info>\n\n<para>\n  The <guilabel>GPS/APRS</guilabel> tab allows to specify several so-called positioning systems. A \n  positioning system is a collection of settings on how GPS information is sent to \n  <glossterm>APRS</glossterm> network. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-positioning.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the GPS or APRS settings.</textobject>\n  </mediaobject>\n  <title>The list GPS/APRS systems.</title> \n</screenshot>\n\n<section>\n<info>\n  <title>Edit/Create GPS Systems</title>\n</info> \n\n<para>\n  Double-clicking a GPS system or clicking on the <guibutton>Add GPS System</guibutton> button \n  will open the GPS system edit dialog. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-gps.png\" width=\"5cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n <title>The digital APRS editing dialog.</title>\n</screenshot>\n\n<para>\n  This dialog allows to specify how the GPS information is sent through the DMR network. That is\n  the <guilabel>Name</guilabel> field specifies the name of the GPS system. The \n  <guilabel>Destination</guilabel> field specifies the private-call contact, the information is \n  sent to. For example 262999 in the Brandmeister network. The <guilabel>Update Period</guilabel>\n  specifies the period in which the current GPS information is sent to the contact. The \n  <glossterm>Revert Channel</glossterm> specifies to which channel the radio should switch to, \n  to send the position information. This should always be <guilabel>Selected</guilabel>. That is,\n  the radio will always send the information on the currently selected channel. \n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the GPS system. \n</para>\n</section>\n\n\n<section>\n<info>\n  <title>Edit/Create APRS System</title>\n</info>\n\n<para>\n  Double-clicking an APRS system or clicking on the <guibutton>Add APRS System</guibutton> button \n  will open the <glossterm>APRS</glossterm> system dialog. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-aprs.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the edit APRS dialog.</textobject>\n  </mediaobject>\n  <title>The analog APRS editing dialog.</title>\n</screenshot>\n\n<para>\n  This dialog allows to specify how the GPS information is sent through the APRS network. That is\n  the <guilabel>Name</guilabel> field specifies the name of the APRS system. The \n  <guilabel>Channel</guilabel> field specifies on which channel the APRS information should be \n  send. This must be an analog channel with one of the typical \n  <link xlink:href=\"https://en.wikipedia.org/wiki/Automatic_Packet_Reporting_System#Technical_information\"\n     >APRS frequencies</link>. The <guilabel>Source</guilabel> field specifies your call and the \n  source <glossterm>SSID</glossterm>, while the <guilabel>Destination</guilabel> field specifies the destination call and\n  SSID. Path is an optional string containing the APRS path for the packet. The \n  <guilabel>Icon</guilabel> combo-box allows to select an icon for the packets. These icons are \n  then shown on <link xlink:href=\"https://aprs.fi\">APRS maps</link>. The \n  <guilabel>Update Period</guilabel> specifies how frequently an APRS packet should be send. \n  Finally, the <guilabel>Message</guilabel> field allows to  set an optional text message for the \n  packet. \n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the APRS system. \n</para>\n</section>\n\n</section>\n\n  "
  },
  {
    "path": "doc/manual/gui/channels.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiChannels\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Creating Channels</title>\n</info>\n\n<para>\n  Creating the list of channels for the DMR radio is the most cumbersome task. Remember, each\n  repeater has two time-slots with possibly multiple talk-groups assigned to each time slot. For\n  the sake of convenience, it is reasonable to define a channel for each talk-group you are\n  interested in on every repeater. Thus, instead of a single channel per\n  FM repeater, you will likely define at least 3-4 channels per DMR repeater. \n</para>\n\n<para>\n  To ease the burden of creating a lot of channels, <application>qdmr</application> implements some\n  features that should help you in creating these channel. One feature is the automatic retrieval \n  of repeater input and output frequencies from\n  <link xlink:href=\"https://repeaterbook.com\">repeaterbook.com</link>. This is a world-wide database\n  of ham-radio repeaters. Both, FM and DMR. \n</para>\n\n<tip>\n  <para>\n    When you enter your locator into the settings dialog (see <xref linkend=\"guiSettingsDialog\"/>), \n    <application>qdmr</application> will provide you with a list of nearby repeater and fill in the\n    input and output frequencies. This feature works for both, FM and DMR repeaters. \n  </para>  \n</tip>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-channels.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the list of channels.</textobject>\n  </mediaobject>\n <title>List of channels.</title>\n</screenshot>\n\n<para> \n  The <guilabel>Channels</guilabel> tab shows the list of all defined channels, irrespective of\n  whether they are FM or DMR channels. You may add an analog or digital channel by \n  clicking on the <guibutton>Add FM Channel</guibutton> or \n  <guibutton>Add DMR Channel</guibutton> button on the bottom, respectively. \n</para>\n\n<para>\n  The <guilabel>Clone Channel</guilabel> button allows for cloning of a selected channel. This \n  enables one to create a set of channels that differ only in the default transmit contact but \n  share the same remaining settings.\n</para>\n\n<para>\n  You can also delete a channel, by selecting that channel in the list and clicking on the\n  <guibutton>Delete Channel</guibutton> button at the bottom. You may move a channel up or down the\n  list by selecting that channel and clicking on the <guibutton>arrow-up</guibutton> or \n  <guibutton>-down</guibutton> button to the right, respectively. Finally you can edit a channel by \n  double-clicking it in the list. \n</para>\n\n<tip>\n  <para>\n    The number of channels usually grow fast and it becomes hard to find channels within the list. \n    To search the list for any channel name or frequency, just hit \n    <keycombo action=\"simul\"><keycap>Ctrl</keycap>+<keycap>F</keycap></keycombo> to open a \n    search box. This search feature is present in all lists. The channel list, however, is likely \n    the largest.\n  </para>  \n  <para>\n    Similar to the search function, all tables have a filter and sort mode. Hit \n    <keycombo action=\"simul\"><keycap>Ctrl</keycap><keycap>Shift</keycap><keycap>L</keycap></keycombo>\n    to toggle modes. Within the filter and sort mode, moving channels around is disabled but you \n    can sort all channels by any column and also filter channels.\n  </para>\n</tip>\n\n\n<section xml:id=\"guiDigitalChannelDialog\">\n<info>\n  <title>Edit DMR channels</title>\n</info>\n\n<para>\n  When you double-click on a DMR channel or click on the <guibutton>Add DMR \n  Channel</guibutton> button, the digital channel editor dialog will be shown. This dialog allows \n  you to edit or create digital channels.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-digital-channel.png\" width=\"7cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the DMR channel editor dialog.</textobject>\n  </mediaobject>\n <title>The DMR channel editor.</title>\n</screenshot>\n\n<para>\n  The dialog is limited to the DMR-channel settings that are relevant for amateur radio. Thus, it\n  is much smaller that the typical dialogs to edit DMR channels in commercial CPSs. However, if \n  you enable the <guilabel>Show device extensions</guilabel> option in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), an additional tab will appear called \n  <guilabel>Extensions</guilabel>. There all implemented device-specific settings are hidden. They \n  can be used to set all options you also find in the manufacturer CPS. See also \n  <xref linkend=\"guiExtensions\"/>.\n</para>\n\n<para>\n  The <guilabel>Name</guilabel>, <guilabel>Rx</guilabel> and <guilabel>Tx Frequency</guilabel> \n  fields contain the chosen name of the channel as well as the transmit and receive frequencies. \n  The latter can be set automatically by using the repeater auto-completion feature: Start to\n  enter the call-sign of a repeater. A list of matching nearby repeaters is shown. Select a \n  repeater from this list and the RX/TX frequencies will be set using the information from\n  <link xlink:href=\"https://repeaterbook.com\">repeaterbook.com</link>. For simplex-channels RX\n  and TX frequencies must be identical. \n</para>\n\n<note>\n  <para>\n    The retrieval of the repeater information may time some moments. Hence, the auto-completion \n    list of repeaters near by may not appear immediately. The found repeaters are cached locally\n    for faster retrieval later on.\n  </para>\n</note>\n\n<para>\n  Once the RX frequency is specified, it is also possible to specify the transmit frequency \n  implicitly via an offset. First specify the offset direction and then the offset frequency. The \n  transmit frequency will be updated accordingly.\n</para>\n\n<para>\n  The <guilabel>Power</guilabel> setting specifies the power used on that channel. For a nearby \n  repeater, you may reduce the power. If you check the <guilabel>Default</guilabel> box, the \n  global power setting will be used. See <xref linkend=\"guiRadioSettings\"/> above.\n</para>\n\n<para>\n  <guilabel>Tx Timeout</guilabel> (<glossterm>TOT</glossterm>)\n  specifies the transmit timeout in seconds. This limits the continuous transmission time to this \n  period. A value of <guilabel>Off</guilabel> disables the timeout. If you check the \n  <guilabel>Default</guilabel> box, the global TOT setting will be used. See \n  <xref linkend=\"guiRadioSettings\"/> above.\n</para>\n\n<para>\n  <guilabel>VOX Level</guilabel> specifies the sensitivity of the <glossterm>VOX</glossterm>\n  for this channel. If <guilabel>OFF</guilabel> is selected, the VOX is disabled for this channel.\n  If you check the <guilabel>Default</guilabel> box, the global VOX setting will be used. See \n  <xref linkend=\"guiRadioSettings\"/> above.\n</para>\n\n<para>\n  Checking <guilabel>Rx Only</guilabel> will disable transmission on this channel. \n</para>\n\n<para>\n  <glossterm>Scan List</glossterm> allows to specify the \n  scan list associated with this channel. If a scan is started on this channel, this scan list will \n  be used. Each channel may have a different scan list.\n</para>\n\n<para>\n  The <guilabel>DMR ID</guilabel> field allows to select the Radio DMR ID for this channel. Some \n  radios allow to program several DMR IDs to be used with one radio. This option makes not sense\n  for HAM-radio usage. However, if you use the same radio for HAM as well as commercial \n  applications, you may need to set your HAM DMR ID for HAM-radio channels and your commercial ID\n  on commercial channels.\n</para>\n\n<para>\n  The <guilabel>Tx Admit</guilabel> field specifies the <glossterm>Admit Criterion</glossterm>,\n  under which you are allowed to transmit on the channel. For DMR repeater channels this \n  should be set to <guilabel>Color Code</guilabel>. This means that you may only transmit if the \n  radio received the correct color code of the repeater before. On simplex channels \n  <guilabel>Channel Free</guilabel> should be chosen and thus the radio will only transmit on \n  the channel if the channel is free.\n</para>\n\n<para>\n  The <glossterm>Color Code</glossterm> specifies the color code of the repeater. For simplex \n  channels, this should be set to 1. For repeater channels, this must match the color code of \n  the repeater. If the auto-completion feature is used, the color code is set automatically.\n</para>\n\n<para>\n  The <glossterm>Time Slot</glossterm> specifies the time-slot  of the repeater for this channel. \n  All repeaters have two time slots but different talk groups might be associated with each time \n  slot. You may need to consult the webpage of the repeater. In the <glossterm>Brandmeister \n    network</glossterm>, usually the time-slot 2 is for local/regional communication while \n  time-slot 1 is for <quote>DX</quote>.\n</para>\n\n<para>\n  The <glossterm>Group List</glossterm> specifies the list of group-calls you want to receive on \n  this channel. See <xref linkend=\"guiGroupLists\"/> above for details.\n</para>\n\n<para>\n  The <guilabel>Tx Contact</guilabel> specifies the default <glossterm>Transmit Contact</glossterm> \n  you want to call on this channel when pressing the PTT button. \n</para>\n\n<para>\n  The <guilabel>Positioning System</guilabel> (DMR ARPS) specifies how you location information is \n  send over this channel (selecting <guilabel>None</guilabel> disables GPS for this channel). Please \n  note, that this setting is ignored for radios without GPS.\n</para>\n\n<para>\n  The <guilabel>Roaming</guilabel> allows to associate a roaming zone (see <xref linkend=\"guiRoaming\"/> \n  below) with this channel. If the radios has the roaming feature, this zone is then used to search\n  for a another channel of another repeater that is reachable once this repeater get out-of-range.\n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the channel. \n</para>\n</section>\n\n\n<section xml:id=\"guiAnalogChannelDialog\">\n<info>\n  <title>Edit FM channels</title>\n</info>\n\n<para>\n  When you double-click on an analog channel or click on the <guibutton>Add FM Channel</guibutton> \n  button, the analog-channel-editor dialog will be shown. This dialog allows for editing or \n  creating FM channels.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-analog-channel.png\" width=\"7cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the FM channel editor dialog.</textobject>\n  </mediaobject>\n <title>The FM channel editor.</title>\n</screenshot>\n\n<para>\n  The left column of the editor are identical to the DMR channel dialog, that is \n  <guilabel>Name</guilabel>, <guilabel>Rx</guilabel> and <guilabel>Tx Frequency</guilabel>, \n  <guilabel>Power</guilabel>, <guilabel>Tx Timeout</guilabel>, <guilabel>VOX Level</guilabel> \n  <guilabel>Rx Only</guilabel> and <guilabel>Scan List</guilabel>.\n</para>\n\n<para>\n  Like for DMR channels, analog channels may also have an <guilabel>Tx Admit</guilabel> \n  criterion. Possible options are <guilabel>Always</guilabel>, <guilabel>Channel Free</guilabel> \n  and <guilabel>Tone</guilabel>. For FM repeaters the <guilabel>Always</guilabel> option should be \n  chosen to allow for a quick turn-around in a QSO. For simplex channels <guilabel>Channel \n  Free</guilabel> should be chosen, as it only allows to transmit when the simplex channel is free.\n  Selecting <guilabel>Channel Free</guilabel> on repeater channels, would prevent transmission while\n  the repeater is active although the last transmission already ended.\n</para>\n\n<para>\n  The <guilabel>Squelch</guilabel> field specifies the squelch threshold. If \n  <guilabel>Open</guilabel> is selected, the squelch is disabled. If <guilabel>Default</guilabel>\n  is enabled, the global squelch level is used. See <xref linkend=\"guiRadioSettings\"/> above.\n</para>\n\n<para>\n  <guilabel>RX</guilabel> and <guilabel>TX Tone</guilabel> specify the \n  <glossterm>CTCSS</glossterm>/<glossterm>DCS</glossterm>\n  sub-tones for this channel/repeater. The <guilabel>RX Tone</guilabel> specifies the sub-tone that\n  is needed to open the squelch. The <guilabel>TX Tone</guilabel> specifies the sub-tone that gets \n  transmitted (e.g., to open the repeater). If the auto-completion feature is used, these tones are\n  set automatically.\n</para>\n\n<para>\n  <guilabel>Bandwidth</guilabel> specifies the band-width of the transmission. Possible \n  values are 12.5kHz (<guilabel>Narrow</guilabel>) or 25kHz (<guilabel>Wide</guilabel>).\n</para>\n\n<para>\n  <guilabel>APRS</guilabel> allows to specify an APRS system to be used on this channel. If the \n  radio supports analog APRS, your position will be sent using the frequency, destination and path\n  specified in this APRS system. If <guilabel>None</guilabel> is selected, APRS is disabled for\n  this channel. If your radio does not support APRS, this setting is ignored.\n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the channel. \n</para>\n</section>\n\n</section>\n\n  "
  },
  {
    "path": "doc/manual/gui/contacts.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiContacts\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Creating Contacts</title>\n</info>\n\n<para>\n  The second tab is the <guilabel>Contact List</guilabel>. Here all DMR contacts are defined, \n  irrespective of their type. It is not only possible to define digital DMR contacts (i.e., \n  private, group and all calls) but also DTMF contacts (and in future two-tone, five-tone contacts\n  too). This eases the control of the <glossterm>EchoLink</glossterm> feature of repeaters.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-contacts.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the contact list tab.</textobject>\n  </mediaobject>\n <title>The contact list.</title>\n</screenshot>\n\n<para>\n  You may add a contact by clicking on the <guibutton>Add Contact</guibutton> button at the bottom. \n  You can also delete a contact by selecting the contact in the list and clicking on the \n  <guibutton>Delete Contact</guibutton> button at the bottom. You may also reorder the contacts by \n  selecting a contact in the list and use the <guibutton>arrow-up</guibutton> and \n  <guibutton>arrow-down</guibutton> buttons on the right to move the contact up and\n  down the list, respectively.\n</para>\n\n\n<section>\n<info><title>Editing DMR contacts</title></info>\n\n<para>\n  When you create a code-plug, the contact list should contain all talk groups and reflectors you\n  are interested in, as well as a so-called <glossterm>All Call</glossterm> contact to the number\n  <emphasis>16777215</emphasis>. Additionally you may add private calls to several operators you \n  know, as well as some <emphasis>service numbers</emphasis>.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-dmr-contact.png\" width=\"3cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot new/edit DMR contact dialog.</textobject>\n  </mediaobject>\n  <title>The new/edit DMR contact dialog.</title>\n</screenshot>\n\n<para>\n  When you click on the <guibutton>Add Contact</guibutton> button or when you double-click a \n  contact entry in the list, the <guilabel>Edit Contact</guilabel> dialog will appear. The first \n  drop-down box allows to choose the type of the call. The possible options are <glossterm>Private \n  Call</glossterm>, <glossterm>Group Call</glossterm> and <glossterm>All Call</glossterm>. The second \n  entry is the name of the contact. Here any text can be entered. The third entry is the number of\n  the contact. This entry gets disabled when <glossterm>All Call</glossterm> is selected as the \n  call-type. Finally, if the last option <guilabel>Rx Tone</guilabel> is enabled, you will hear a \n  ring-tone whenever this contact calls you.\n</para>\n\n<tip>\n  <para>\n    <application>qdmr</application> tries to download the current list of all registered user DMR IDs.\n    The contact dialog will use this information (once downloaded) to resolve call-signs to DMR IDs.\n    Just start entering the call-sign into the name field and matching call-signs are shown. \n  </para>\n  <para>\n    The same holds true for Brandmeister <glossterm>Talkgroup</glossterm> names. That is, start \n    typing the name of a talk group and a drop-down list of talk group names will appear for \n    auto-completion. Select the talk group and its DMR ID gets set.\n  </para>\n</tip>\n\n<para>\n  If <guilabel>Show device extensions</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for contacts. \n</para>\n</section>\n\n\n<section>\n<info><title>Editing DTMF contacts</title></info>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-dtmf-contact.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot new/edit DTMF contact dialog.</textobject>\n  </mediaobject>\n  <title>The new/edit DTMF contact dialog.</title>\n</screenshot>\n\n<para>\n  Beside DMR contacts, the contact list may also contain analog DTMF contacts. You can add a new DTMF \n  contact by clicking on the <guibutton>Add DTMF contact</guibutton> button. This will open the \n  <guilabel>Edit DTMF contact</guilabel> dialog (see above). This is a rather simple dialog. \n</para>\n<para>\n  <guilabel>Name</guilabel> specifies the name of the DTMF contact, while <guilabel>Number</guilabel>\n  specifies the DTMF number. There, any DTMF number can be entered, this includes symbols like *, #, \n  A-D. The <guilabel>Ring</guilabel> option specifies whether the radio should ring if a call by this\n  contact is received.\n</para>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/gui/extensions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiExtensions\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Edit Device Specific Extensions</title>\n</info>\n\n<para>\n  Since version 0.9.2, <application>qdmr</application> supports the editing of \n  so-called device-specific extensions. These extensions were introduced with version \n  0.9.0, to allow for configuring all features of a radio, although not\n  represented within the common codeplug structure used by <application>qdmr</application>, to \n  program all supported radios. \n</para>\n\n<para>\n  These extensions can be added to every element of a codeplug. For example contacts, \n  channels, zones etc. They are stored in a common way to allow for an unified method for \n  manipulating them. All extensions are displayed in a so-called <quote>Extension View</quote>,\n  a tree-like structure present for all codeplug elements. \n</para>\n\n<para>\n  As <application>qdmr</application> is intended to be a clean and easy-to-use CPS for all radios, \n  these device-specific settings are normally hidden. To show them, you need to enable the \n  <guilabel>Show device extensions</guilabel> option in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>). \n</para>\n\n<para>\n  As this extension view is common to all codeplug elements, this section will discuss the usage on\n  the codeplug extensions. When <guilabel>Show device extensions</guilabel> is enabled in the \n  settings dialog, the codeplug extensions are shown in the last tab called \n  <guilabel>Extensions</guilabel>. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-codeplug-extensions.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the codeplug extensions.</textobject>\n  </mediaobject>\n  <title>Extension view for the codeplug extensions.</title>\n</screenshot>\n\n<para>\n  Unless a codeplug is read from a device, the extensions are usually not set (see screen shot \n  above). In these cases, the <guilabel>Value</guilabel> column of the extension view shows \n  <guilabel>[None]</guilabel>. You can add these extensions to the codeplug by selecting the \n  corresponding extension and click on the <guilabel>Create</guilabel> button at the bottom of the\n  extension view. \n</para>\n\n<para>\n  Similarly, an extension can be removed from the codeplug by selecting it and clicking on the \n  <guilabel>Remove</guilabel> button at the bottom of the extension view. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-codeplug-tyt-extensions.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the codeplug extensions for TyT devices.</textobject>\n  </mediaobject>\n  <title>Example for codeplug extensions for TyT devices.</title>\n</screenshot>\n\n<para>\n  Once an extension is added to the codeplug, it can be expanded to view and edit the contained \n  settings. To edit a particular setting, simply double-click on the value. \n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/gui/fig/Makefile",
    "content": "all:\n"
  },
  {
    "path": "doc/manual/gui/grouplists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiGroupLists\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Assembling Group Lists</title>\n</info>\n\n<para>\n  <emphasis>RX group lists</emphasis> or simply \n  <glossterm>Group Lists</glossterm>s are just lists of <glossterm>Group Call</glossterm>s you may wish \n  to receive on a channel. Of cause, you may want to receive calls to multiple talk groups on one \n  channel, hence you have to create these lists of talk groups beforehand. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-rx-groups.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the RX group lists.</textobject>\n  </mediaobject>\n  <title>The list of group lists.</title>\n</screenshot>\n\n<para>\n  The <guilabel>Group Lists</guilabel> tab is just a simple list of all group lists you created. You \n  may add a group list by clicking on the <guibutton>Add RX Group</guibutton> button at the bottom.\n  You can delete a group list by selecting it in the list and clicking on the \n  <guibutton>Delete RX Group</guibutton> button there. You can also edit a RX group list by \n  double-clicking on that group in the list.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-rx-groups.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the group list edit dialog.</textobject>\n  </mediaobject>\n  <title>The group list dialog.</title>\n</screenshot>\n\n<para>\n  When creating a new group list or when editing one, the <guilabel>Edit Group \n  List</guilabel> dialog will open. Within this dialog, you can change the name of the group at the\n  top. In the center of the dialog, you will find the list of group calls of this group list. You \n  can add group call to the list by clicking on the <guibutton>Add Contact</guibutton> button on \n  the bottom. You can also remove contacts from the list by selecting the contact and clicking on the \n  <guibutton>Remove Contact</guibutton> button. When you are done editing the group list, click on \n  the <guibutton>Ok</guibutton> button. The <guibutton>Cancel</guibutton> button will discard all \n  changes and closes the dialog.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-grouplist-add.png\" width=\"3cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the group call selection dialog.</textobject>\n  </mediaobject>\n  <title>Selecting the group calls to add.</title>\n</screenshot>\n\n<para>\n  When adding contacts to the group list, the a dialog appears, that allows for selecting the group\n  calls to add to the group list. Some radios (in particular the OpenGD77 firmware) also allow to \n  add private calls to group lists. To this end, the <guilabel>Show private calls</guilabel> option\n  allows to add private calls too. All other devices do not allow for private calls in group lists.\n  They will simply ignored by <application>qdmr</application> when the codeplug is generated for \n  these devices.\n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the group list. \n</para>\n</section>\n  "
  },
  {
    "path": "doc/manual/gui/gui.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<chapter xml:id=\"gui\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n<info>\n  <title>The Graphical User Interface</title>\n</info>\n  \n<para>\n  This chapter describes the graphical user interface (<acronym>GUI</acronym>) in some detail. \n</para>\n\n<note>\n  <para>\n    <application>qdmr</application> aims at being a device and manufacturer independent CPS. To this \n    end, the GUI reflects a device independent code plug. This means, that not all of the shown \n    features are present in all radios. For example, not all supported devices implement APRS or \n    roaming. These settings are then ignored when generating the device specific binary code plug.\n  </para>  \n\n  <para>\n    Some radios provide settings, that are important but also specific to a single model or family\n    of models. <application>qdmr</application> aims at supporting these settings through so-called \n    extensions. These extensions can be attached to all elements of a codeplug. For example to \n    channels, contacts, zones, etc. \n  </para>\n</note>\n\n<xi:include href=\"radiosettings.xml\"/>\n<xi:include href=\"contacts.xml\"/>\n<xi:include href=\"grouplists.xml\"/>\n<xi:include href=\"channels.xml\"/>\n<xi:include href=\"zones.xml\"/>\n<xi:include href=\"scanlists.xml\"/>\n<xi:include href=\"aprs.xml\"/>\n<xi:include href=\"roaming.xml\"/>\n<xi:include href=\"satellite.xml\"/>\n<xi:include href=\"extensions.xml\"/>\n<xi:include href=\"programradio.xml\"/>\n<xi:include href=\"settingsdialog.xml\"/>\n\n</chapter>\n"
  },
  {
    "path": "doc/manual/gui/programradio.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiProgram\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Programming the radio</title>\n</info>\n  \n<para>\n  Once the <glossterm>code plug</glossterm> is finished, it can be programmed onto the radio. Just select the\n  <guibutton>write cod plug</guibutton> button in the tool-bar at the top of the window or select\n  <guimenuitem>write</guimenuitem> from the <guimenu>device</guimenu> menu.\n</para>\n\n<para>\n  In a first step, <application>qdmr</application> will try to detect a connected radio. This will \n  be done automatically (unless disabled in the settings menu) if there is only one radio connected\n  and it is safe to access the USB device. Some radios use some generic USB to serial chips in their\n  cable instead of connecting the micro controller of the radio directly to the computer. This way, \n  it is not possible for qdmr to safely assume, that the found serial port is actually a radio. If \n  such a generic interface is detected, qdmr will ask which interface the radio is connected to.\n</para>\n\n\n<para>Once a radio is  found it will verify the code plug with that radio. That is, it will check \n  whether any limits are  exceeded. For example the number of channels, contacts, group lists, etc. \n</para>\n\n<para>\n  There are several levels of issues that can be detected when verifying a code plug with a radio. \n  The lowest level is the <guilabel>Information</guilabel>. These are just messages generated to inform \n  you about minor changes made to the code plug to fit it into the specific radio. For example when \n  zones are split. These information are usually ignored and <application>qdmr</application> will \n  proceed writing the code plug. \n</para>\n\n<para>\n  <guilabel>Warnings</guilabel> are one level more severe. They are issued if changes are made that \n  may change the behavior of the code plug. The result, however, will still be a working code plug. They are \n  usually issued when names are too long. When warnings are issued, <application>qdmr</application>\n  will not automatically proceed writing the code plug. The user, however, can ignore the warnings and \n  continue. In the application settings (see <xref linkend=\"guiSettingsDialog\"/>), \n  you may choose to always ignore verification warnings. In this case, \n  <application>qdmr</application> will write the code plug automatically, even if there were \n  some warnings.\n</para>\n\n<para>\n  Finally <guilabel>Errors</guilabel> are the most severe verification issues. They simply prevent \n  writing the code plug to the device. The user cannot ignore errors as they would result in \n  invalid and even damaging code plugs being written to the device.\n</para>\n\n<para>\n  If, however, everything fits into the radio, <application>qdmr</application> will start writing \n  the binary code plug to the device.\n</para>\n\n<para>\n  Writing the code plug is a two-step process. First, the current code plug is read from the\n  radio. This includes all settings. Then the device-specific code plug is updated and then\n  re-uploaded to the device. This two-step process will maintain all device-specific settings made\n  earlier unless explicitly set within qdmr.\n</para>\n\n<para>\n  During the reading or writing, the <application>qdmr</application> GUI will turn gray (inactive) to\n  prevent any changes to the code plug during the transfer. However, a progress-bar is shown in the\n  bottom-right to indicate the progress.\n</para>\n\n\n<section>\n<info>\n  <title>Permissions</title>\n</info>\n\n<para>\n  When running <application>qdmr</application> or <command>dmrconf</command> under Linux, you may \n  need to change the permissions to access USB devices. Along with the software, a \n  <command>udev</command> rules file was installed. This file specifies that members of the \n  <systemitem class=\"groupname\">dialout</systemitem> group have access to the radios. Consequently, \n  you need to be a member of this group. \n</para>\n\n<para>\n  You can check your group membership with <command>groups</command>. This command lists all groups \n  your user is a member of. This list should contain <systemitem class=\"groupname\">dialout</systemitem>. \n</para>\n\n<para>\n  If your user is not yet a member of the <systemitem class=\"groupname\">dialout</systemitem> group,\n  you can add your user to it by calling\n</para>\n<screen>\n  sudo adduser YOUR_USER dialout\n</screen>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/gui/radiosettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiRadioSettings\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>General configuration</title>\n</info>\n\n<para>\n  The figure below shows the <guilabel>General Configuration</guilabel> tab of \n  <application>qdmr</application>. This tab is divided into Five subtabs:\n  <guilabel>Boot Settings</guilabel>, <guilabel>Audio Settings</guilabel>,\n  <guilabel>Tone Settings</guilabel>, <guilabel>Channel Default Values</guilabel>\n  and <guilabel>Extensions</guilabel>.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-general-settings.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the general configuration tab showing the boot settings.</textobject>\n  </mediaobject>\n  <title>General settings tab.</title>\n</screenshot>\n\n<section>\n<info><title>Boot settings</title></info>\n<para>\n  Within the <guilabel>Boot Settings</guilabel>, the <guilabel>Intro Line 1</guilabel> &amp; \n  <guilabel>2</guilabel> specify the text that some radios show on startup. You may enter any text\n  here. Some radios show an image during boot. For those radios, these settings have no effect.\n</para>\n</section>\n\n\n<section>\n<info><title>Audio Settings</title></info>\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-general-settings-audio.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the audio settings.</textobject>\n  </mediaobject>\n  <title>Audio settings tab.</title>\n</screenshot>\n\n<para>\n  Within the <guilabel>Audio Settings</guilabel>, the <guilabel>Default Microphone Amplification</guilabel> specifies\n  (by default) the common mic gain for all modes. This must be a value between 1 and 10, where 1 is the smallest\n  amplification and 10 the loudest. A separate <guilabel>FM Microphone Amplification</guilabel> can be specified below.\n  If <guilabel>Default</guilabel> is selected, the default microphone gain is used instead.\n</para>\n\n<para>\n  The <guilabel>Default Squelch</guilabel> specifies the common squelch level for all modes. This must either be\n  <guilabel>Open</guilabel> or a value between 1 and 10. The larger the value, the stronger the station must be to open\n  the squelch. A squelch level is usually only relevant for FM channels. Some radios allow to set a separate DMR squelch\n  level. This can be done with the <guilabel>DMR Squelch</guilabel> field. If <guilabel>Default</guilabel> is checked,\n  the default (FM) squelch is used.\n</para>\n\n<para>\n  The <guilabel>VOX Sensitivity</guilabel> field specifies the sensitivity of the VOX (voice-operated-switch). If set\n  to <guilabel>Off</guilabel>, the VOX is disabled. Otherwise, a value between 1 and 10 must be selected. The larger the\n  value, the louder you have to talk into the radio, to trigger the VOX. There is also <guilabel>VOX Delay</guilabel>\n  field, specifying a delay before the VOX is triggered. This can be used to avoid unintentional transmission of short\n  loud bursts, for example in some working environments.\n</para>\n\n<para>\n  The <guilabel>Maximum Speaker</guilabel> and <guilabel>Maximum Headphone Volumes</guilabel> allow for limiting the\n  loudness the speaker or headphone. This can be used to adapt the range of volume knob on the radio to the current\n  working environment.\n</para>\n\n<para>\n  The <guilabel>Speech Synthesis</guilabel> option allows for enabling speech synthesis by the \n  radio. Some radios support speech synthesis to help visually impaired \n  operators to handle the radio and navigate its menus. If this option is checked, the speech-synthesis \n  will be enabled if the radio supports this feature.\n</para>\n</section>\n\n\n<section>\n<info><title>Tone Settings</title></info>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-general-settings-tone.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the tone settings.</textobject>\n  </mediaobject>\n  <title>Tone settings tab.</title>\n</screenshot>\n\n<para>\n  The tone settings control some of the sounds generated by the radio. Especially, some radios allow to disable all\n  sounds globally. Or to enable/disable or set the volume of the key tones.\n</para>\n\n<para>\n  The <guilabel>SMS tone</guilabel>, <guilabel>Ringtone</guilabel> and <guilabel>Talk permit</guilabel>  fields allow to\n  enable/disable tones sounding, when an SMS, a private all is received or if it is allowed to transmit on the currently\n  active channel according to the admit criterion set for that channel. The SMS and private call tones can only be\n  enabled or disabled globally, while the talk-permit tone can be enabled on a by-channel-type basis. To enable/disable\n  the tone for channel types, click on the pen icon.\n</para>\n\n<para>\n  The remaining tone-settings concern the boot, call-start, call-end, channel-idle and call-reset melodies. These can\n  either be enabled/disabled globally (e.g., boot and call-reset melodies) or enabled for channel types individually.\n  Again, click on the pen button to select the channel types. Some radios allow for programming custom melodies for\n  these tones. These melodies can be specified in LilyPond format and speed in BPM (beats per minute). The play-button\n  on the right plays the specified melody.\n</para>\n\n<para>\n  <application>qdmr</application> uses a simplified LilyPond format. It consists of a simple, white-space separated\n  list of notes and pauses. The general format is\n</para>\n<para><token>Name</token><token>[Length]</token><token>[Ocave]</token><token>[.]</token></para>\n<para>\n  Name must be a note name (i.e., <token>c</token>, <token>cis</token>, <token>d</token>, <token>dis</token>,\n  <token>e</token>, <token>f</token>, <token>fis</token>, <token>g</token>, <token>gis</token>, <token>a</token>,\n  <token>ais</token> or <token>b</token>) or <token>r</token> for a pause (rest).\n</para>\n\n<para>\n  The length specifies the length of the note or pause. It must be one of <token>1</token>, <token>2</token>,\n  <token>4</token>, <token>8</token>, <token>16</token> or <token>32</token>, specifying a whole, half, quarter, ...\n  note. Adding a dot (<token>.</token>) to the end of the note extends it by half of its length. I.e. <token>a4.</token>\n  will be 3/8th long. The length can also be omitted. In this case, the length of the previous not is re-used.\n  Consequently, the first note need an explicit length.\n</para>\n\n<para>\n  Finally, by adding one or more <token>'</token> the octave can be increased, while adding one or more <token>,</token>\n  the octave of a note can be decreased. It is not possible to specify the octave of the entire melody.\n</para>\n</section>\n\n\n<section>\n<info><title>Channel Default Values</title></info>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-general-settings-defaults.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the channel default settings.</textobject>\n  </mediaobject>\n  <title>Channel default settings tab.</title>\n</screenshot>\n\n<para>\n  The <guilabel>Channel Default Values</guilabel> block allows to specify some default values that can \n  be referenced by channels (see <xref linkend=\"guiChannels\"/> below). This serves two use cases. First, it \n  allows to set some channel properties for all channels that reference these values at once. More \n  importantly, however, some radios do not allow to set these options on a per-channel basis. For those\n  radios, these values are used for all channels.\n</para>\n</section>\n\n\n<section>\n<info><title>Extensions</title></info>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-general-settings-extensions.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the settings extensions.</textobject>\n  </mediaobject>\n  <title>Settings extensions tab.</title>\n</screenshot>\n\n<para>\n  Finally, the tab labeled <guilabel>Extensions</guilabel> contains extensions. This view allows to add, remove and edit\n  device specific radio settings. All device specific settings are handled and displayed in the same way. See\n  <xref linkend=\"guiExtensions\"/> for some description of this process.\n</para>\n</section>\n\n</section>\n  \n  "
  },
  {
    "path": "doc/manual/gui/roaming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n<section xml:id=\"guiRoaming\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Roaming</title>\n</info>\n\n<para>\n  Roaming is a feature that allows DMR radios to select an alternative repeater once you leave \n  the range of the currently selected one. To do that, you have to specify so-called \n  <glossterm>roaming zone</glossterm>s. Within these zones, you collect all repeaters that provide \n  access to a particular talk group. When roaming is enabled, the radio will check periodically,\n  whether the current repeater is still reachable. If not, the strongest repeater from the \n  selected roaming zone will then be selected instead. \n</para>\n\n<para>\n  In order to stay connected to a particular talk group, not the entire channel settings must be \n  changed. But only those properties, that are specific to the repeater we change to. These are \n  the RX and TX frequencies, color codes and sometimes the time-slot. The latter is necessary, as \n  those repeaters may be located in other regions. Usually &#x2014; at least for Brandmeister \n  repeaters &#x2014; the time slot 2 is reserved for regional communication while time slot 1 is \n  intended for inter-regional communication. So you may need to override the time-slot of the active\n  channel, whenever you roam outside of the region associated with the talk group. Some talk groups,\n  however, are over-regional anyway. For example, the world-wide talk group 91. Here, the time slot \n  will always be TS 1 and thus does not need to be overridden. \n</para>\n\n<para>\n  As only some properties of the current DMR channel needs to be overridden, there are special \n  channels called <glossterm>roaming channels</glossterm> that contain only those settings, that\n  are changed during roaming. \n</para>\n\n\n<section>\n  <info><title>Roaming Channels</title></info>\n\n  <para>\n    The <guilabel>Roaming Channels</guilabel> tab collects all defined roaming channels. \n  </para>\n\n  <screenshot>\n    <mediaobject>\n      <imageobject><imagedata fileref=\"fig/qdmr-roaming-channels.png\" width=\"13cm\" align=\"center\"/></imageobject>\n      <textobject>Screen-shot of the list of roaming channels.</textobject>\n    </mediaobject>\n  <title>List of roaming channels.</title>\n  </screenshot>\n\n  <para> \n    Each roaming channel has a name, for easy reference in the roaming zones. This should be the call\n    of the repeater. Additionally, each roaming channel has a transmit (TX) and receive (RX) frequency\n    overriding the TX and RX frequencies of the current channel on roaming.\n  </para>\n\n  <para>\n    There are two optional settings, that may override the time slot (<guilabel>TS</guilabel>) and \n    color code (<guilabel>CC</guilabel>) of the current channel. If <guilabel>[Selected]</guilabel> \n    is show, the time slot and color code of the current channel is used instead during roaming.\n  </para>\n</section>\n\n<section>\n  <info><title>Roaming Zones</title></info>\n\n  <para>\n    The <guilabel>Roaming Zones</guilabel> tab collects all defined roaming zones. \n  </para>\n\n  <screenshot>\n    <mediaobject>\n      <imageobject><imagedata fileref=\"fig/qdmr-roamingzones.png\" width=\"13cm\" align=\"center\"/></imageobject>\n      <textobject>Screen-shot of the list of roaming zones.</textobject>\n    </mediaobject>\n    <title>List of roaming zones.</title>\n  </screenshot>\n\n  <para>\n    There are two ways to create a new roaming zone. The easiest way is to generate a zone and the \n    associated roaming channels automatically. Click on <guibutton>Generate roaming zone</guibutton>, \n    to generate a roaming zone automatically or <guibutton>Add Roaming Zone</guibutton> to assemble one \n    manually.\n  </para>\n\n  <section>\n    <info><title>Creating a Roaming Zone</title></info>\n    <para> \n      When generating a roaming zone automatically, a dialog will you to select a talk group or several\n      talk groups to create a roaming zone for. That is, the talk group you want to stay connected to \n      in case of losing contact to the repeater.\n    </para>\n\n    <screenshot>\n      <mediaobject>\n        <imageobject><imagedata fileref=\"fig/qdmr-generate-roaming-zone.png\" width=\"5cm\" align=\"center\"/></imageobject>\n        <textobject>Screen-shot of the talk-group-selection dialog when generating a roaming zone automatically.</textobject>\n      </mediaobject>\n    <title>Selecting talk groups to generate a roaming zone.</title>\n    </screenshot>\n\n    <para>\n      Once a talk group is selected, a roaming channel created for each DMR channel, which has the \n      selected talk group as its transmit contact. These newly created channels are then collected \n      into a new roaming zone. The roaming zone editing dialog is then opened, allowing you to edit \n      the newly created zone. \n    </para>\n\n    <para>\n      Alternatively, you may create a roaming zone manually. Simply click the \n      <guibutton>Add Roaming Zone</guibutton> button and the roaming zone editing dialog will open. \n      There you can add roaming channels manually.\n    </para>\n  </section> \n\n  <section>\n    <info><title>Editing Roaming Zones</title></info>\n    <para>\n      Double-clicking on the roaming zone or clicking on the <guibutton>Add Roaming Zone</guibutton> \n      button will open the <guilabel>Roaming Zone Editor</guilabel>.\n    </para>\n\n    <screenshot>\n      <mediaobject>\n        <imageobject><imagedata fileref=\"fig/qdmr-edit-roamingzone.png\" width=\"5cm\" align=\"center\"/></imageobject>\n        <textobject>Screen-shot of the edit-roaming-zone dialog.</textobject>\n      </mediaobject>\n      <title>The roaming-zone editing dialog.</title>\n    </screenshot>\n\n    <para>\n      Here you can edit the name of the roaming zone as well as adding the roaming channels that \n      are part of this zone. However, you may also add DMR channels to this zone by clicking on the \n      <guibutton>Add DMR Channel</guibutton> button. This will not add DMR channel directly to the \n      roaming zone, but will create a new roaming channel from this DMR channel.\n    </para>\n\n    <para>\n      If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n      <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n      the device specific settings for the roaming zone. \n    </para>\n  </section>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/gui/satellite.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiSatellite\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n\n\n<info>\n  <title>Orbital Elements and Transponder</title>\n</info>\n\n<para>\n  Some radios support the prediction of satellite passes and also ease satellite operations by \n  correcting doppler shifts of up- and downlink frequencies. To this end, this feature turns a\n  cheap handheld radio in a fully fledged satellite station. \n</para>\n\n<para>\n  The satellite editor dialog allows to select which satellites will be tracked by the device. The \n  dialog also allows to specify the up- and downlink frequencies for the chosen satellites. \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-satellite-editor.png\" width=\"15cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the satellite editor dialog.</textobject>\n  </mediaobject>\n  <title>The satellite editor dialog.</title>\n</screenshot>\n\n<para>\n  To add a new satellite, click the <guilabel>Add</guilabel> button at the bottom of the dialog. \n  You will be presented with a selection dialog of all known satellites.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-satellite-select.png\" width=\"6cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the satellite selection dialog.</textobject>\n  </mediaobject>\n  <title>The satellite selection dialog.</title>\n</screenshot>\n\n<para> \n  The list of all known (amateur radio) satellites is huge. You may search for a name using the \n  <shortcut><keycombo><keycap>Ctrl</keycap>-<keycap>F</keycap></keycombo></shortcut> shortcut. \n</para>\n\n<para>\n  Once all satellites are added you are interested in, you can edit the transponder frequencies. \n  In qdmr, a satellite may have an FM transponder, an APRS transponder and a beacon. At least one \n  of these features should be configured. \n</para>\n\n<para>\n  The uplink (from earth to satellite) or transmit frequency as well as the downlink (from \n  satellite to search) or receive frequency can be entered directly or selected from a known set of\n  frequencies. To change a frequency, simply double-clock the field in the table.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-satellite-edit-downlink.png\" width=\"4cm\" align=\"center\"/></imageobject>\n    <textobject>Detail screen shot of the satellite editor dialog, selecting a downlink frequency.</textobject>\n  </mediaobject>\n  <title>The satellite editor dialog: Selecting a downlink frequency.</title>\n</screenshot>\n\n<para>\n  Some satellites also require a sub-tone (CTCSS). These tones can be selected from the dropdown \n  menu for up- and downlink separately.\n</para>\n\n<para>\n  Once all satellites and their transponders are set up, you can write the configuration to the \n  radio using <guimenuitem>Write satellites</guimenuitem> from the <guimenu>Device</guimenu> menu.\n</para>\n</section>\n  \n"
  },
  {
    "path": "doc/manual/gui/scanlists.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiScanLists\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Assembling Scan Lists</title>\n</info>\n\n<para>\n  Scan lists are simple lists of channels that are scanned sequentially, when scanning is started. A\n  <glossterm>ScanList</glossterm> may be associated with an analog or digital channel (see \n  <xref linkend=\"guiChannels\"/>).\n</para>\n\n<note>\n  <para> \n    For many radios, you need to associate a scan list with a channel (see \n    <xref linkend=\"guiChannels\"/> above) in the analog or digital channel edit dialog. This determines \n    which scan list is used, when a scan is started on a particular channel.\n  </para>\n</note>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-scan-lists.png\" width=\"13cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the list of scan lists.</textobject>\n  </mediaobject>\n <title>The list of scan lists.</title>\n</screenshot>\n\n<para>\n  A new scan list can be created by clicking on the <guibutton>Add Scan List</guibutton> button. A \n  scan list can be deleted by selecting the scan list and clicking on the <guibutton>Delete Scan \n  List</guibutton> button. The order of the scan list can be changed by selecting a list and moving \n  it up/down using the <guibutton>up</guibutton> and <guibutton>down</guibutton> buttons on the \n  right.\n</para>\n\n<para>\n  Double-clicking on a scan list or clicking the <guibutton>Add Scan List</guibutton> button will \n  open the scan-list edit-dialog. This dialog allows to alter/assemble the scan list by adding, \n  removing or reordering the channels in the scan list.\n</para>\n\n\n<section>\n<info>\n  <title>Edit Scan Lists</title>\n</info>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-scanlist.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the edit scan-list dialog.</textobject>\n  </mediaobject>\n  <title>The scan-list editor.</title>\n</screenshot>\n\n<para>\n  By double-clicking an existing scan list or clicking on the <guibutton>Add Scan \n  List</guibutton> button, the scan-list dialog opens. The <guilabel>Name</guilabel> field allows \n  to specify the name of the scan list.\n</para>\n\n<para>\n  The optional <guilabel>Primary</guilabel> and <guilabel>Secondary Channel</guilabel> fields allow\n  to specify channels that are visited more frequently. The <guilabel>Primary Channel</guilabel> will\n  be visited 50% of the time. That is, after a channel of the scan list was visited, the primary \n  channel is visited again, and after that, the next channel from the scan list is visited. In the end,\n  the primary channel is scanned half of the time. The secondary channel is similar but gets visited only\n  25% of the time. The drop down list allows to select none, any channel or the <guilabel>Selected</guilabel> \n  channel. The latter refers to the channel, the scan started on. \n</para>\n\n<para>\n  The optional <guilabel>Transmit Channel</guilabel> or <glossterm>Revert Channel</glossterm> specifies \n  the channel to transmit on during a scan. Here none, any channel, the selected channel and also the \n  <guilabel>Last</guilabel> channel can be chosen. The <guilabel>Last</guilabel> channel refers to \n  the last active channel on the scan list. This allows to answer a heard call during a scan.\n</para>\n\n<para>\n  Channels can be added to the scan list by clicking on the <guibutton>Add Channel</guibutton> \n  button at the bottom. Similarly, channels can be removed by selecting them and clicking on the \n  <guibutton>Remove Channel</guibutton> button. Like for all other lists, the channels can be moved \n  around within the list by selecting channels and using the <guibutton>up</guibutton> and \n  <guibutton>down</guibutton> buttons to the right.\n</para>\n\n<para>\n  If <guilabel>Show Commercial Features</guilabel> is enabled in the settings dialog (see \n  <xref linkend=\"guiSettingsDialog\"/>), a tab bar is shown at the top. There you can also access\n  the device specific settings for the scan list. \n</para>\n</section>\n\n</section>\n"
  },
  {
    "path": "doc/manual/gui/settingsdialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiSettingsDialog\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" >\n\n\n<info>\n  <title>Application Settings Dialog</title>\n</info>\n\n<para>\n  The application settings dialog controls the behavior of <application>qdmr</application>.\n  The dialog is divided into 2 sections, accessible via the tabs on the top.\n</para>\n\n\n\n<section>\n<info>\n  <title>Data Sources</title>\n</info>\n\n<para>\n  The <guilabel>Data Sources</guilabel> tab collects the settings for the location service and \n  repeater database.  \n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-settings-datasources.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the application settings dialog for the data sources.</textobject>\n  </mediaobject>\n  <title>The settings dialog: Data sources</title>\n</screenshot>\n\n<para>\n  The first section concerns the location of the user. You may enter your <glossterm>Maidenhead \n  Locator</glossterm> here or you may enable <guilabel>System location</guilabel>. The latter tries\n  to obtain the current location from the operating system. This information is then used in the \n  channel editors (see <xref linkend=\"guiChannels\"/>) to provide auto-completion for repeaters \n  nearby.\n</para>\n\n<para>\n  Some data sources restrict search queries to a radius around some position. This radius is set in\n  <guilabel>Search radius</guilabel>. This is radius only limits the results obtained from specific sources,\n  not the auto-completion by <application>qdmr</application>.\n</para>\n\n\n<para>\n  The second section allows to set the sources for the repeater database. This enables\n  <application>qdmr</application> to automatically complete some information for a repeater if its \n  callsign is entered as the name, when creating a new channel. As several source may have different settings,\n  each source has its own settings tab.\n</para>\n\n<formalpara>\n  <title>repeaterbook.com</title>\n  <para>\n    For some weird reason, <ulink url=\"https://www.repeaterbook.com/\">Repeater Book</ulink> provides two identical\n    APIs for North America and the rest of the World. You must select your source accordingly, also you need to\n    request a user-API access token and paste it here.\n  </para>\n</formalpara>\n\n<formalpara>\n  <title>repeatermap.de</title>\n  <para>\n    <ulink url=\"https://repeatermap.de/\">repeatermap.de</ulink> also requires a user-API access token. See\n    <ulink url=\"https://repeatermap.de/api.php\">API access page</ulink> from repeatermap.de.\n  </para>\n</formalpara>\n</section>\n\n\n<section>\n<info>\n  <title>Radio programming settings</title>\n</info>\n\n<para>\n The second tab controls, how the radios are programmed. That is, how the codeplug is assembled and\n  also, how the callsign database gets curated.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-settings-programming.png\" width=\"5cm\" align=\"center\"/></imageobject>\n    <textobject>Screen shot of the application settings dialog for programming the radios.</textobject>\n  </mediaobject>\n  <title>The settings dialog: Programming</title>\n</screenshot>\n\n<para>\n  The <guilabel>Radio Interfaces</guilabel> section contains settings, controlling, how the \n  radios are accessed. For now, there is only one setting, called \n  <guilabel>disable auto-detect</guilabel>. This will disable all means to detect and identify \n  connected radios. You will then have to select an interface and choose a radio model, every time\n  you access it. \n</para>\n\n<para>\n  The second section specifies how codeplugs are assembled. The first option \n  <guilabel>Update codeplug</guilabel> specifies whether a codeplug is generated from\n  scratch or whether the codeplug currently programmed on the radio gets updated. \n  <application>qdmr</application> does not implement all settings possible for all radios, \n  consequently <guilabel>Update code plug</guilabel> should be chooses to maintain all settings \n  of the radio that are not touched by <application>qdmr</application>.\n</para>\n\n<para>\n  For some radios, the GPS and roaming functionality must be enabled explicitly. The \n  <guilabel>Auto-enable GPS</guilabel> and <guilabel>Auto-enable roaming</guilabel> options\n  can be used to automatically enable GPS or roaming. If selected, whenever any channel has a\n  GPS/APRS system or a roaming zone associated with it, the GPS and/or roaming gets enabled \n  globally.\n</para>\n\n<para>\n  As described in <xref linkend=\"guiProgram\"/>, the upload of a code plug will be paused \n  if some verification warnings are issued. The <guilabel>Ignore verification warnings</guilabel>\n  option allows to continue silently even in the presence of verification warnings. The may be \n  needed for some radios with some rather short communication timeout. The radio may reset the \n  connection to the computer while the warnings are shown. To prevent this, this option might be \n  used.\n</para>\n\n<para>\n  The <guilabel>Ignore frequency limits</guilabel> option does exactly what it says. Usually, \n  programming a channel outside of the radios frequency range would issue an error. However, many\n  radios are able to receive and even transmit outside of the frequency range specified by the \n  manufacturer. But be aware, that transmitting outside the declared frequency range may destroy \n  the radio!\n</para>\n\n<para>\n  The <guilabel>Call-Sign DB</guilabel> section collects options that control the automatic \n  curation of the call-sign DB. Many radios allow to write a large database of call-signs \n  and DMR IDs to the radio. These DBs are then used to resolve DMR IDs to call-signs, names\n  etc. and display them. \n</para>\n\n<para>\n  Usually, curating these databases is a cumbersome task. <application>qdmr</application> tries to\n  automate this task. Usually, <application>qdmr</application> will select as many call-signs from\n  the global database it can fit into the radio. Although modern radios will provide a huge amount\n  memory, not all registered IDs can be programmed. In these cases, \n  <application>qdmr</application> will select only the <emphasis>closest</emphasis> IDs to your\n  DMR ID (default Radio DMR ID, see <xref linkend=\"guiRadioSettings\"/>). The DMR IDs are compared by \n  the longest matching prefix. This makes sense as DMR IDs are not random. They share the same \n  prefix for countries and regions. This way, <application>qdmr</application> will first select all\n  IDs from the same region followed by all IDs from the same country etc. Of cause, there is no \n  rule without any exceptions. Some countries have several prefixes assigned. \n</para>\n\n<para>\n  The <guilabel>Limit number of DB entries</guilabel> option and <guilabel>Number of DB \n    entries</guilabel> field allow to limit the number of DB entries written to the device. If \n  the <guilabel>Limit number of DB entries</guilabel> option is disabled, as many entries are \n  written to the device as it can hold.\n</para>\n\n<para>\n  The <guilabel>Select using my DMR ID</guilabel> option and the <guilabel>Select using \n    prefixes</guilabel> field can be used to control the selection of entries. If the\n  <guilabel>Select using my DMR ID</guilabel> option is enabled, the aforementioned algorithm is \n  used to select the entries based on the default DMR ID. If this option is disabled, a list of \n  prefixes must be specified in\n  the <guilabel>Select using prefixes</guilabel> field. This must be a list of comma-separated \n  prefixes like <code>262, 263</code>. Whitespace are ignored. Then the DMR IDs closest to these \n  prefixes are used to assemble the final call-sign DB.\n</para>\n</section>\n\n</section>\n  \n"
  },
  {
    "path": "doc/manual/gui/zones.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"guiZones\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n\n<info>\n  <title>Assembling Zones</title>\n</info>\n\n<para>\n  You may program a myriad of different channels for you radio. To organize them in handy chunks,\n  zones are used. That is, a zone is just a named list of channels that are relevant for a\n  particular area or a particular situation.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-zones.png\" width=\"13cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n <title>The list of zones.</title>\n</screenshot>\n\n<para>\n  The <guilabel>Zones</guilabel> tab just lists all defined zones. You may add a \n  <glossterm>Zone</glossterm> using the <guibutton>Add Zone</guibutton> button or you may delete \n  one by selecting the zone in the list and clicking on <guibutton>Delete Zone</guibutton>. You may\n  also alter the ordering of the zones by selecting one from the list and using the \n  <guibutton>up</guibutton> and <guibutton>down</guibutton> buttons on the right.\n</para>\n\n<para>\n  How zones are implemented differs from radio to radio. For example, some radios allow to set a\n  different zone for each VFO (A or B), consequently these zones are simple lists of channels.\n  Other radios allow to select a single zone for both VFOs. For these radios, a zone\n  consists of two lists of channels. One for each VFO. <application>qdmr</application> zones follow\n  the second approach. That is, a zone consists of two lists. One of each VFO. When programming \n  radios that support only one channel list per zone, the zone is split into two (unless the second \n  list is empty). One for each VFO and the A/B label is added to the name.\n</para>\n\n<para>\n  Double-clicking a zone or clicking on the <guibutton>Add Zone</guibutton> button will open the \n  zone editor dialog.\n</para>\n\n<screenshot>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/qdmr-edit-zone.png\" width=\"7cm\" align=\"center\"/></imageobject>\n    <textobject>Screen-shot of the edit-zone dialog.</textobject>\n  </mediaobject>\n <title>The zone editor.</title>\n</screenshot>\n\n<para>\n  This dialog allows for adding or removing channels, and changing the order of the channels within\n  the zone using the up- and down-buttons on the right.\n</para>\n\n</section>\n\n  "
  },
  {
    "path": "doc/manual/html/Makefile",
    "content": "all: manual.xml\n\txsltproc dm3mat.darc.de_manual.xsl manual.xml\n\nclean:\n\trm -rf fig/\n\trm -f manual.xml \n\trm -f *.html\n\trm -f docbook.css"
  },
  {
    "path": "doc/manual/html/dm3mat.darc.de_manual.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:import href=\"file:///usr/share/xml/docbook/stylesheet/docbook-xsl/html/chunk.xsl\"/>\n\n  <xsl:param name=\"html.stylesheet\" select=\"'/layout.css'\"/>\n  <xsl:param name=\"generate.toc\">\n    appendix  nop\n    article   toc,title\n    book      toc,title\n    chapter   toc\n    part      nop\n    preface   nop\n    qandadiv  nop\n    qandaset  nop\n    reference toc,title\n    section   nop\n    set       toc\n  </xsl:param>\n\n  <xsl:template name=\"user.head.content\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\n    <link rel=\"stylesheet\" href=\"manual.css\"/>\n    <style type=\"text/css\"><![CDATA[\n      #menu-soft { background-color: #4CAF50; }\n      #menu-qdmr { background-color: #4CAF50; }\n      ]]></style>\n  </xsl:template>\n\n  <xsl:template name=\"body.attributes\">\n    <xsl:comment>#include virtual=\"/menu.html\" </xsl:comment>\n  </xsl:template>\n\n  <xsl:template name=\"credits.div\"/>\n\n  <xsl:template name=\"footer.navigation\">\n    <xsl:param name=\"prev\" select=\"/foo\"/>\n    <xsl:param name=\"next\" select=\"/foo\"/>\n    <xsl:param name=\"nav.context\"/>\n\n    <xsl:variable name=\"home\" select=\"/*[1]\"/>\n    <xsl:variable name=\"up\" select=\"parent::*\"/>\n\n    <xsl:if test=\"count($prev)>0\">\n      <a id=\"prev\">\n        <xsl:attribute name=\"href\">\n          <xsl:call-template name=\"href.target\">\n            <xsl:with-param name=\"object\" select=\"$prev\"/>\n          </xsl:call-template>\n        </xsl:attribute>\n        Prev\n      </a>\n    </xsl:if>\n    <xsl:if test=\"count($up) > 0 and generate-id($up) != generate-id($home)\">\n      <a id=\"up\">\n        <xsl:attribute name=\"href\">\n          <xsl:call-template name=\"href.target\">\n            <xsl:with-param name=\"object\" select=\"$up\"/>\n          </xsl:call-template>\n        </xsl:attribute>\n        Up\n      </a>\n    </xsl:if>\n    <xsl:if test=\"count($up)>0\">\n      <a id=\"index\">\n        <xsl:attribute name=\"href\">\n          <xsl:call-template name=\"href.target\">\n            <xsl:with-param name=\"object\" select=\"$up\"/>\n          </xsl:call-template>\n        </xsl:attribute>\n        Index\n      </a>\n    </xsl:if>\n    <xsl:if test=\"count($next)>0\">\n      <a id=\"next\">\n        <xsl:attribute name=\"href\">\n          <xsl:call-template name=\"href.target\">\n            <xsl:with-param name=\"object\" select=\"$next\"/>\n          </xsl:call-template>\n        </xsl:attribute>\n        Next\n      </a>\n    </xsl:if>\n  </xsl:template>\n\n  <xsl:template name=\"chunk-element-content\">\n  <xsl:param name=\"prev\"/>\n  <xsl:param name=\"next\"/>\n  <xsl:param name=\"nav.context\"/>\n  <xsl:param name=\"content\">\n    <xsl:apply-imports/>\n  </xsl:param>\n\n  <xsl:call-template name=\"user.preroot\"/>\n  \n  <html>\n    <xsl:call-template name=\"root.attributes\"/>\n    <xsl:call-template name=\"html.head\">\n      <xsl:with-param name=\"prev\" select=\"$prev\"/>\n      <xsl:with-param name=\"next\" select=\"$next\"/>\n    </xsl:call-template>\n\n    <body>\n      <xsl:call-template name=\"body.attributes\"/>\n\n      <main>\n      <!--<nav>\n        <xsl:call-template name=\"user.header.navigation\">\n          <xsl:with-param name=\"prev\" select=\"$prev\"/>\n          <xsl:with-param name=\"next\" select=\"$next\"/>\n          <xsl:with-param name=\"nav.context\" select=\"$nav.context\"/>\n        </xsl:call-template>\n\n        <xsl:call-template name=\"header.navigation\">\n          <xsl:with-param name=\"prev\" select=\"$prev\"/>\n          <xsl:with-param name=\"next\" select=\"$next\"/>\n          <xsl:with-param name=\"nav.context\" select=\"$nav.context\"/>\n        </xsl:call-template>\n      </nav>-->\n\n      <xsl:call-template name=\"user.header.content\"/>\n      <xsl:copy-of select=\"$content\"/>\n      <xsl:call-template name=\"user.footer.content\"/>\n\n      <nav>\n        <xsl:call-template name=\"footer.navigation\">\n          <xsl:with-param name=\"prev\" select=\"$prev\"/>\n          <xsl:with-param name=\"next\" select=\"$next\"/>\n          <xsl:with-param name=\"nav.context\" select=\"$nav.context\"/>\n        </xsl:call-template>\n\n        <xsl:call-template name=\"user.footer.navigation\">\n          <xsl:with-param name=\"prev\" select=\"$prev\"/>\n          <xsl:with-param name=\"next\" select=\"$next\"/>\n          <xsl:with-param name=\"nav.context\" select=\"$nav.context\"/>\n        </xsl:call-template>\n      </nav>\n      </main>\n    </body>\n  </html>\n  <xsl:value-of select=\"$chunk.append\"/>\n</xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/manual/html/manual.css",
    "content": "main > nav {\n  margin-top:20px;\n}\n\ndl.variablelist {\n  margin-left: 2em;\n}\n\ndl.toc {\n  margin-left: 2em;\n  margin-right: 2em;\n  background-color: #f2f2f2;\n  overflow: auto; \n  padding: 1em;\n  border: 0px solid black;\n  border-radius: 5px;\n  font-weight: bolder;\n}\n\npre.programlisting {\n  background-color: #f2f2f2;\n  overflow-x: auto;\n  padding: 1em;\n  margin-left: 16px;\n  margin-right: 16px;\n}\n\ntable {\n  margin-left: 16px;\n  margin-right: 16px;\n}\nthead td {\n  font-weight: bold;\n}\ntbody td {\n  vertical-align: top;\n}\n\n.token {\n  font-family: 'Courier New', Courier, monospace;\n}"
  },
  {
    "path": "doc/manual/intro/codeplug.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introCodeplug\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Codeplug Assembly</title>\n</info>\n\n<para>\n  After the basic concepts and technical details of the DMR mode has been discussed, it is time to \n  consider the actual configuration of the DMR radios. This usually not done via the keypad of the \n  radio but with the help of a separate software. The so-called <glossterm>CPS</glossterm> or \n  codeplug programming software. \n</para>\n\n<para>\n  Before we can start, we need like any other participant in the DMR network a unique number, the \n  DMR ID.\n</para>\n\n<note>\n  <para>\n    You can get your personal and unique DMR ID from \n    <link xlink:href=\"https://www.radioid.net/\">radioid.net</link>. There you need to verify that\n    you are a licensed ham operator.\n  </para>\n</note>\n\n<para>\n  You will receive your personal DMR ID usually within 24h per Mail. Once you've got your ID, \n  you can start.\n</para>\n\n<para>\n  As this script is intended for the beginners, it is very likely that you do not own a top-shelf \n  Motorola device but rather one of the cheap devices of the common manufacturers.\n</para>\n\n<warning>\n  <para>\n    If you do not own a DMR device yet but consider to by one, you should explicitly check whether \n    it supports DMR Tier I and II<footnote><para>As usual, DMR is not a single standard but a \n      family of standards. Tier I describes the simplex operation while Tier II considers the \n      repeater operation and time-slots. You will therefore need a device that also implements \n      Tier II to be able to work with repeaters.</para></footnote>. Ignore any marketing BS and\n      check the technical description of the product for Tier I &amp; II. If it is not mentioned\n      there, simply skip that product. This is particularly true for the Baofeng MD-5R but not \n      the RD-5R. \n  </para>\n</warning>\n\n<para>\n  The manufacturer of the device of your choice will provide the CPS for download you need to program\n  your radio. Usually you will also find there firmware updates for your device. The manufacturers \n  usually provide a separate CPS version for every device and even firmware revision. So please check\n  whether you've got the correct CPS version. The configuration of the device differs from device to\n  device and even more from manufacturer to manufacturer. The basic setup, however, remains the same.\n</para>\n\n<para>\n  When you start the CPS for the first time, you will likely note two things. First, that the user \n  experience stems from the last millennium (about Windows 3.11). And second, that there are a tone\n  of obscure and badly translated options for your device. These options are usually named cryptic\n  and are not documented. \n</para>\n\n<para>\n  The configuration of your device usually happens in five to six steps:\n</para>\n<orderedlist>\n  <listitem><para>General settings, </para></listitem>\n  <listitem><para>creating contacts, </para></listitem>\n  <listitem><para>assembling group lists, </para></listitem>\n  <listitem><para>creating channels, </para></listitem>\n  <listitem><para>assembling zones and </para></listitem>\n  <listitem><para>optionally assembling scan lists.</para></listitem>\n</orderedlist>\n\n<para>\n  Within the following sections, I want to guide you through these steps.\n</para>\n\n\n<section xml:id=\"introCPGeneral\">\n<info><title>General radio-wide settings.</title></info>\n\n<para>\n  The single most important options within the general settings is your DMR ID and your call sign. These \n  options are usually located under the label <guilabel>Radio Settings</guilabel> or \n  <guilabel>General Settings</guilabel><footnote><para>The actual name may vary from manufacturer\n    to manufacturer.</para></footnote>.\n  Your DMR ID is entered in the field name <guilabel>Radio ID</guilabel>. Many radios support to \n  enter several DMR IDs. This feature is usually not used in ham radio. In fact you will only always\n  need a single DMR ID even with several radios.\n</para>\n\n<para>\n  Your call can be entered in the <guilabel>Radio Name</guilabel> field.\n</para>\n</section>\n\n\n<section xml:id=\"introCPContact\">\n<info><title>Creating Contacts</title></info>\n\n<para>\n  Once you have made these basic settings, you may create some contacts in your contact list. This \n  list should contain all talk groups you are interested in, some private contacts to OM you know\n  as well as some <quote>service numbers</quote> for the <quote>echo</quote>-service, SMS service \n  etc. A sample is shown in <xref linkend=\"tabIntroContacts\"/>.\n</para>\n\n<table xml:id=\"tabIntroContacts\">\n  <title>Example contacts for germany</title>\n  <thead>\n    <tr><td>Name</td> <td>Typ</td> <td>Nummer</td>  <td>Name</td> <td>Typ</td> <td>Nummer</td></tr>\n  </thead>\n  <tbody>\n    <tr><td>Local</td>       <td>group call</td> <td>9</td>     <td>Ham/SlHo</td>  <td>group call</td> <td>2622</td></tr>\n    <tr><td>Regional</td>    <td>group call</td> <td>8</td>     <td>NiSa/Bre</td>  <td>group call</td> <td>2623</td></tr>\n    <tr><td>TG99</td>        <td>group call</td> <td>99</td>    <td>NRW</td>       <td>group call</td> <td>2624</td></tr>\n    <tr><td>All call</td>    <td>all call</td> <td>16777215</td> <td>RhPf/Saar</td> <td>group call</td> <td>2625</td></tr>\n    <tr><td>World wide</td>  <td>group call</td> <td>91</td>    <td>Hessen</td>    <td>group call</td> <td>2626</td></tr>\n    <tr><td>Europe</td>      <td>group call</td> <td>92</td>    <td>BaWü</td>      <td>group call</td> <td>2627</td></tr>\n    <tr><td>D-A-CH</td>      <td>group call</td> <td>920</td>   <td>Bay</td>       <td>group call</td> <td>2628</td></tr>\n    <tr><td>Germany</td>     <td>group call</td> <td>262</td>   <td>Sa/Th</td>     <td>group call</td> <td>629</td></tr>\n    <tr><td>Austria</td>     <td>group call</td> <td>232</td>   <td>Echo Test</td> <td>private call</td> <td>262997</td></tr>\n    <tr><td>Switzerland</td> <td>group call</td> <td>228</td>   <td>SMS Serv.</td> <td>private call</td> <td>262993</td></tr>\n    <tr><td>EMCOM EU</td>    <td>group call</td> <td>9112</td>  <td>DAPNET</td>    <td>private call</td> <td>262994</td></tr>\n    <tr><td>EMCOM WW</td>    <td>group call</td> <td>9911</td>  <td>APRS GW</td>   <td>private call</td> <td>262999</td></tr>\n    <tr><td>MeVo/SaAn</td>   <td>group call</td> <td>2620</td>  <td>DM3MAT</td>    <td>private call</td> <td>2621370</td></tr>\n    <tr><td>Ber/Bra</td>     <td>group call</td> <td>2621</td>  <td>...</td>       <td>...</td>          <td>...</td></tr>\n  </tbody>\n</table>\n\n<para>\n  Of cause there are much more talk groups. There are also talk groups for specific topics which \n  are not necessarily targeted at a specific region. A rather complete list can be found in the \n  <link xlink:href=\"https://wiki.brandmeister.network/index.php/TalkGroups\">Brandmeister Wiki</link>.\n</para>\n</section>\n\n\n<section xml:id=\"introCPGroupList\">\n<info><title>Assemble group lists</title></info>\n\n<para>\n  The next step is to assemble so-called <glossterm>Group List</glossterm>s. These are simple lists of \n  talk groups that you want to receive on a particular channel. As mentioned in the introduction, the \n  network does not know which talk groups you are interested in. This must be programmed into the radio.\n  Group lists do exactly that: The specify which talk groups you want to receive. All others are ignored.\n</para>\n\n<para>\n  You should at least create two group lists. One for the simplex operation, one for regional communication\n  and optionally one for the trans-regional communication. You should also create one for each region you \n  frequently visit.\n</para>\n\n<para>\n  The simplex group list is theoretically not necessary as simplex calls should always use the so-called \n  <glossterm>All Call</glossterm>. Frequently, however, also the talkgroups TG99, TG9 and TG8 are used \n  in simplex operation. Hence a group list with these talk groups is needed for simplex operation.\n</para>\n\n<para>\n  Your trans-regional talk group should include the talk groups for the entire world TG91, your \n  continent (e.g., Europe TG92), your country (e.g., Germany TG262) and also the emergency talk \n  group (e.g., 9112 in Europe).\n</para>\n\n<para>\n  Finally the talk group for the local/regional communication should contain the local TG9, \n  regional TG8 and the talk group for your region (e.g., TG 2621 for my region Berlin/Brandenbug).\n  As I am also frequently in saxony, I also created a group list for that region. My \n  group list settings are shown in <xref linkend=\"tabIntroGroupLists\"/>.\n</para>\n  \n<table xml:id=\"tabIntroGroupLists\">\n  <title>Example group lists</title>\n  <thead>\n    <tr><td>Name</td> <td>Group calls</td></tr>\n  </thead>\n  <tbody>\n    <tr><td>Simplex</td> <td>Local, Regional, TG99</td></tr>\n    <tr><td>WW/EU/DL</td> <td>World wide, Europe, D-A-CH, Germany, EMCOM EU</td></tr>\n    <tr><td>Ber/Bra</td> <td>Local, Regional, Ber/Bra</td></tr>\n    <tr><td>Sa/Th</td> <td>Local, Regional, Sa/Th</td></tr>\n  </tbody>\n</table>\n</section>\n\n\n<section xml:id=\"introCPChannel\">\n<info><title>Creating channels</title></info>\n\n<para>\n  Before we start assembling any channels, I should mention that DMR radios are also able to \n  transmit and receive analog FM. Thus, you can also use them for classic FM simplex and repeater \n  operation. In this section, I describe the configuration of digital DMR channels usually called \n  <quote>digital channels</quote>. The configuration of analog FM channels is not described. To \n  create a DMR channel, you have to select <guilabel>digital</guilabel> for the <guilabel>channel \n  type</guilabel>, for FM channels <guilabel>analog</guilabel>.\n</para>\n\n<para>\n  When you already have some experience with the analog FM repeater operation, the configuration \n  of DMR channels may appear quiet weird. For analog FM repeaters, you usually configure exactly \n  one channel. For DMR repeaters you will configure at least two (one for each time slot), but \n  usually many more. To cut a long story short, let me explain it with a concrete example.\n</para>\n\n\n<section>\n<info><title>Creating Simplex Channels</title></info>\n<table xml:id=\"tabIntroCPSimplexChannels\">\n  <title>Example simplex channel configuration</title>\n  <thead>\n    <tr><td>Name</td> <td>RX Freq.</td> <td>TX Freq.</td> <td>TS</td> <td>CC</td> <td>TX Contact</td> <td>Grp.List</td></tr>\n  </thead>\n  <tbody>\n    <tr><td>DMR S0</td> <td>433.4500 MHz</td> <td>433.4500 MHz</td> <td>1</td> <td>1</td> <td>all call</td> <td>simplex</td></tr>\n    <tr><td>DMR S1</td> <td>433.6125 MHz</td> <td>433.6125 MHz</td> <td>1</td> <td>1</td> <td>all call</td> <td>simplex</td></tr>\n    <tr><td>...</td> <td>...</td> <td>...</td> <td>...</td> <td>...</td> <td>...</td> <td>...</td></tr>\n  </tbody>\n</table>\n\n<para>\n  In <xref linkend=\"tabIntroCPSimplexChannels\"/> an example for a simplex channel configuration is \n  shown. Of cause, you should extend it to all 8 simplex channels. The first column simply specifies\n  the name of the channel.\n</para>\n\n<para>\n  The second and third columns specify the transmit and receive frequencies for these channels.\n  For simplex channels, RX and TX frequencies are the same.\n</para>  \n\n<para>\n  In simplex operation, there is no repeater. That is, no instance that dictates a beat. To this \n  end the choice of the time slot (TS) is irrelevant and usually TS1 is chosen.\n</para>\n\n<para>\n  The color code, however, matters. Repeater as well as your radio will ignore calls with a mismatching\n  color code. For simplex channels, the color code 1 has been established.\n</para>\n\n<para>\n  The sixth column specifies the default transmit contact. For simplex channels, the so-called \n  <glossterm>All Call</glossterm> should be chosen, to ensure that really everyone can receive the \n  call irrespective of the receivers group list settings. The default transmit contact specifies \n  the contact (private, group or all call) that is called whenever the PTT is pressed. As mentioned\n  earlier, there is an exception to that rule. Whenever you directly answer a call within in the \n  so-called <glossterm>Hang Time</glossterm>, you will answer with the same call you received. \n</para>\n\n<para>\n  The last column specifies the to so-called <glossterm>Group List</glossterm>. This list specifies\n  which talk groups are received on that channel. As mentioned earlier, no entry should be needed \n  here, as the all-call should be used for the transmit contact on simplex channels. Unfortunately,\n  it is not uncommon to find several talk groups being used as transmit contacts on simplex \n  channels like TG9, TG8, TG99. For these cases, a group list <quote>simplex</quote> was created \n  earlier.\n</para>\n\n<para>\n  Within your CPS, you will find many more options for channels. The majority can be left \n  untouched. At the end of this section, I will describe some of these settings briefly. Many of \n  these settings are quite uncommon in amateur radio or even straight illegal.\n</para>\n\n<para>\n  The <glossterm>Admit Criterion</glossterm> specifies under which conditions your radio is \n  allowed to transmit. For simplex channels, the option <guilabel>channel free</guilabel> should be\n  chosen. This configures the radio to only transmit if the channel is currently free.\n</para>\n</section>\n\n<section>\n<info><title>Creating repeater channels</title></info>\n\n<para>\n  Creating repeater channels is slightly more complex than creating simplex channels, as we need to\n  create several channels per repeater. Before you can create any channels, you need to know which \n  DMR repeaters are near to you. A good overview provides the\n  <link xlink:href=\"https://repeaterbook.com\">repater book</link>. There you can also filter for\n  DMR repeaters and you get all information you need to configure the DMR repater channels. That \n  is input and output frequencies and the color code of the repeater.\n</para>\n\n<table orient=\"land\" xml:id=\"tabIntroCPRepeaterChannels\">\n  <title>Example channels for a single repeater DB0LDS</title>\n  <thead>\n    <tr><td>Name</td> <td>RX Freq. (output)</td> <td>TX Freq. (input)</td> <td>TS</td> <td>CC</td> <td>TX Contact</td> <td>Grp.List</td></tr>\n  </thead>\n  <tbody>\n    <tr><td>DB0LDS TS1</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>1</td> <td>1</td> <td>-</td> <td>WW/EU/DL</td></tr>\n    <tr><td>DB0LDS DL TS1</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>1</td> <td>1</td> <td>Germany</td> <td>WW/EU/DL</td></tr>\n    <tr><td>DB0LDS Sa/Th TS1</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>1</td> <td>1</td> <td>Sa/Th</td> <td>Sa/Th</td></tr>\n    <tr><td>DB0LDS TG9 TS2</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>2</td> <td>1</td> <td>TG9</td> <td>Ber/Bra</td></tr>\n    <tr><td>DB0LDS TG8 TS2</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>2</td> <td>1</td> <td>TG8</td> <td>Ber/Bra</td></tr>\n    <tr><td>DB0LDS BB TS2</td> <td>439.5625 MHz</td> <td>431.9625 MHz</td> <td>2</td> <td>1</td> <td>Ber/Bra</td> <td>Ber/Bra</td></tr>\n  </tbody>\n</table>\n\n<para>\n  I think, it is the best to explain the creation of repeater channels using a concrete example for\n  a repeater near to me shown in <xref linkend=\"tabIntroCPRepeaterChannels\"/>. This repeater has \n  the call DB0LDS and has the input frequency 431.9625 MHz and output frequency 439.5625 MHz. \n  According to repeater book, this repeater expects the color code 1. These are the elementary \n  information you need to set for all channels using this repeater. Many CPSs allow to copy or \n  clone channels. This way you only need to enter this basic information once.\n</para>\n\n<para>\n  At the end of <xref linkend=\"introTimeSlot\"/>, I mentioned that trans-regional communication is \n  happening on time slot 1 while regional communication is happening on time slot 2. This is \n  visible in this example. The repeater is located in the region Berlin/Brandenbug (Ber/Bra),\n  consequently all channels with within-region talk-groups have the time slot 2, all others have\n  the time slot 1.\n</para>\n\n<para>\n  The first channel <quote>DB0LDS TS1</quote> is a generic channel for the time slot 1. There is no\n  default transmit contact defined for this channel. This channel can be used to perform arbitrary \n  direct and group calls by selecting a contact or talk group from the contact list. This means, \n  that a call cannot be started by simply pressing PTT on that channel. First, a contact must be \n  selected that should get called.\n</para>\n\n<para>\n  The second channel <quote>DB0LDA DL TS1</quote> is almost identical to the first except for the \n  default transmit contact. Here <quote>Germany</quote> (TG262) is selected. This means, if this \n  channel is selected and the PTT is pressed, the talk group 262 is called. By configuring a \n  separate channel for this talk group allows to start a call to it without having to search for \n  it in the contact list. This also allows to temporarily subscribe this talk group on a repeater\n  easily, by simply pressing PTT briefly. \n</para>\n\n<note>\n  <para>\n    Irrespective of the default transmit contact, you can always answer to a call within the hang \n    time.\n  </para>\n</note>\n\n<para>\n  The third channel <quote>DB0LDS Sa/Th TS1</quote> is also similar to the first two. Here the \n  default transmit contact is the talk group for Saxony/Thuringia (TG2629) to be able to \n  subscribe that talk group at my local repeater and call it easily. Please not that for this \n  channel the time slot 1 is used. The repeater is located in Brandenburg and therefore any \n  communication with Saxony is inter-regional and should happen on the time slot 1. The group list\n  contains only the talk group for Saxony/Thuringia and thus other inter-regional talk groups \n  are not received on that channel.\n</para>\n\n<para>\n  Channels four, five and six are for repeater-local (TG9), regional (TG8) and the talk group \n  Berlin/Brandenburg (TG2621) calls. As this is all regional communication, it happens on the \n  time slot 2. Also they all have the group list <quote>Ber/Bra</quote> set (see \n  <xref linkend=\"tabIntroGroupLists\"/>). Therefore, all regional talk groups (TG8, TG9, TG2621)\n  are received on that channel. As the default transmit contact, the corresponding talk group\n  is set. If the channel <quote>DB0LDS TG9 TS2</quote> is selected and the PTT is pressed, \n  a call to TG9 is repeated only by the repeater DB0LDS. If the channel <quote>DB0LDS BB \n  TS2</quote> is selected and the PTT is pressed, a call to TG2621 is repeater by almost all \n  repeaters in the region Berlin/Brandenbug. Therefore, chose a talk group that is sufficient\n  for you intended communication. \n</para>\n\n<note>\n  <para>\n    On any channel, you can start an arbitrary call (group, privat, all) by either selecting \n    the contact from the contact list of even simply entering the DMR number into the keypad\n    of the radio. This is independent from the default contact on the current channel. In the\n    end, the default transmit contact is a convenience feature. With the default contact, \n    channels for frequently used contacts can be created.\n  </para>\n</note>\n\n<para>\n  The so-called <glossterm>Admit Criterion</glossterm> should be set to <guilabel>Color \n  Code</guilabel> for DMR repeater channel. This means, that the radio will only transmit if the \n  channel is free and the color code of the repeater matches the color code of the channel.\n</para>\n</section>\n\n<section>\n<info><title>Other channel options</title></info>\n\n<para>\n  The user interface of the manufacturer CPS where you configure the channels, is usually very \n  extensive. There is a huge amount of options that control the behavior of the channel. The \n  majority of these options are not used in ham radio applications. Some of these, however, I want\n  to describe here briefly.\n</para>\n\n<para>\n  The <glossterm>Admit Criterion</glossterm> was mentioned before. It controls under which \n  conditions the radios can transmit. There are usually three options. <guilabel>Always</guilabel>\n  does exactly what it says: it allows to transmit always. This option should be chosen for \n  analog FM repeater channels. <guilabel>Channel free</guilabel> means that the radio will only \n  transmit if the current channel is free. This option should be choses for simplex channels.\n  When <guilabel>Color code</guilabel> is selected, the radio will only transmit if the channel is\n  free and the color code of the repeater matches the color code of the channel. This option should\n  be chosen for DMR repeater channels.\n</para>\n\n<para>\n  The <glossterm>TOT</glossterm> setting or <quote>transmit timeout</quote> specifies the maximum \n  duration of continuous transmission. After that period of continuous transmission, the radio \n  will stop the transmission automatically. The feature is used in commercial applications to avoid\n  the blocking of a channel or talk group by a participant. This option has little sense in amateur \n  radio and can be set to infinity.\n</para>\n\n<para>\n  The <glossterm>Emergency System</glossterm> is a method to signal an alarm or an emergency \n  situation. Also this feature is not used in amateur radio. \n</para>\n\n<para>\n  The option <glossterm>Privacy Group</glossterm> or <glossterm>Encryption Key</glossterm> refers\n  to a built-in method of encrypting the traffic. This is actually forbidden in amateur radio.\n</para>\n\n<para> \n  The flags <quote>Emergency Alarm Confirmed</quote>, <quote>Private Call Confirmed</quote> and \n  <quote>Data Call Confirmed</quote> specify how the radio starts these calls. The radio will first\n  establish a call to the destination and will signal once the call is <quote>confirmed</quote>. \n  Once the confirmation is received, the actual call starts. These options are not used in amateur\n  radio and should be disabled as they may interfere with the normal operation.\n</para>\n\n<para>\n  The option <glossterm>Talkaround</glossterm> allows to operate simplex on a repeater channel. \n  That is the radio transmits and receives on the repeater input frequency. This allows to bypass\n  the repeater and to communicate directly with other participants on the same repeater channel.\n  Also this option makes little sense in amateur radio.\n</para>\n\n<para>\n  When the <glossterm>RX Only</glossterm> flag is enabled, the radio cannot transmit on that \n  channel. This may be useful for out-of-band monitoring channels where you are not allowed \n  to transmit.\n</para>\n\n<para>\n  The <glossterm>VOX</glossterm> feature is actually used in ham radio. It stands for <quote>voice \n  operated switch</quote> and allows to start a call using the voice without the need to press PTT.\n  Some radios allow to enable this option on a per-channel bases others only radio-wide.\n</para>\n\n<para>\n  The <quote>Power</quote> option allows to specify the transmit power level. This can usually \n  be set in predefined steps like <guilabel>Low</guilabel>, <guilabel>Middle</guilabel>, \n  <guilabel>High</guilabel>. Some radios may also allow a fine grained setting of the power level.\n</para>\n\n<para>\n  The <glossterm>Scan List</glossterm> specifies a list of channel that are scanned if a scan is\n  started on that channel. This feature might be used as an alternative to a missing roaming \n  feature (see <xref linkend=\"introRoaming\"/>). \n</para>\n</section>\n</section>\n\n\n<section xml:id=\"itroCPZone\">\n<info><title>Assembling zones</title></info>\n\n<para>\n  Once you have assembled all channels of interest, you may notice that the list is quite long. \n  Hence all DMR radios organize the channels in so-called <glossterm>Zone</glossterm>s. Zones are \n  simple lists of channels that group them into relevant sets, usually based on the location. You \n  may therefore collect all channels for <quote>Home</quote>, <quote>Work</quote> and \n  <quote>Holidays</quote> into one zone each. How you organize your zones is up to you.\n</para>\n\n<para>\n  You may also organize these channels by talk groups. This way you may implement some kind of a \n  manual roaming. Once you left the range of a repeater you may search for another one in the same\n  zone. This way you stay connected to a particular talk group. In contrast to the automatic roaming,\n  you have to select the repeater by hand.\n</para>\n\n<note>\n  <para>\n    Channels that are not assigned to any zone are usually not selectable by the radio. It is, \n    however, perfectly fine to assign a channel to several zones.\n  </para>\n</note>\n</section>\n\n\n<section xml:id=\"introCPScanList\">\n<info><title>Assembling scan lists</title></info>\n<para>\n  <glossterm>Scan List</glossterm>s are simple lists of channels. When the scan is started on a \n  particular channel, the channels scan list is used. The radio will then step though that list\n  and may stop on a channel that shows activity. It is then possible to answer the received call.\n  This function allows for observing several channels. Additionally, it is usually possible to specify\n  one or more <quote>priority channels</quote> for a scan list. This channel is then visited more \n  frequently and thus monitored more intensively.\n</para>\n</section>\n</section>"
  },
  {
    "path": "doc/manual/intro/fig/Makefile",
    "content": "IMG = 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 \\\n      trunk_net_ex1.png trunk_net_ex2.png trunk_net_ex3.png trunk_net_ex4a.png trunk_net_ex4b.png \\\n\t\t\tsimplex_allcall.png simplex_groupcall.png simplex_privatecall.png \\\n\t\t\trepeater_local.png repeater_privatecall.png \\\n\t\t\ttalkgroup_ex1a.png talkgroup_ex1b.png talkgroup_ex1c.png \\\n\t\t\ttimeslot.png\n\n#.PRECIOUS: %.pdf # keep PDFs\nall: $(IMG)\n\n%.png: %.pdf\n\tconvert -density 300 -depth 8 -quality 85 $< $@\n\n%.pdf: %.tex\n\tpdflatex $<\n\nclean:\n\trm -f $(IMG)\n\trm -f *.aux *.log\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_duplex_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DL0LDS};\n  \\user{U2}{6,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$431.9625 MHz$} (R1) ;\n  \\path[->] (R1) edge node[above,rotate=-17] {$439.5625 MHz$} (U2);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_duplex_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DL0LDS};\n  \\activeuser{U2}{6,0}{DL2XYZ};\n  \\path[->] (U2) edge node[above,rotate=-17] {$431.9625 MHz$} (R1) ;\n  \\path[->] (R1) edge node[above,rotate=17] {$439.5625 MHz$} (U1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_echolink_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\repeater{R2}{6,1}{DB0LDS};\n  \\user{U2}{9,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$DTMF: 662699$} node[below,rotate=17]{$431.825 MHz$} (R1) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_echolink_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\activerepeater{R2}{6,1}{DB0LDS};\n  \\user{U2}{9,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above,rotate=17] {$431.825 MHz$} (R1) ;\n  \\path[->,dashed] (R1) edge node[above] {via Echolink} (R2) ;\t\n  \\path[->] (R2) edge node[above,rotate=-17] {$439.150 MHz$} (U2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_echolink_c.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activerepeater{R1}{3,1}{DB0SP};\n  \\activerepeater{R2}{6,1}{DB0LDS};\n  \\activeuser{U2}{9,0}{DL2XYZ};\n  \\path[->] (R1) edge node[above,rotate=17] {$439.425 MHz$} (U1) ;\n  \\path[->,dashed] (R2) edge node[above] {via Echolink} (R1) ;\t\n  \\path[->] (U2) edge node[above,rotate=-17] {$431.875 MHz$} (R2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_simplex_a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{U1}{0,0}{DM3MAT};\n  \\user{U2}{6,0}{DL2XYZ};\n  \\path[->] (U1) edge node[above] {$144.500 MHz$} (U2) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/fm_simplex_b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{U1}{0,0}{DM3MAT};\n  \\activeuser{U2}{6,0}{DL2XYZ};\n  \\path[->] (U2) edge node[above] {$144.500 MHz$} (U1) ;\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/repeater.tex",
    "content": "\\newcommand{\\repeater}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [black,thick,domain=-45:225] plot ({0.2*cos(\\x)}, {0.5+0.2*sin(\\x)});%\n   \\draw [black,thick,domain=-45:225] plot ({0.4*cos(\\x)}, {0.5+0.4*sin(\\x)});%\n   \\node (xxx) at (0,-.2) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\\newcommand{\\activerepeater}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,thick] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [red,thick,domain=-45:225] plot ({0.2*cos(\\x)}, {0.5+0.2*sin(\\x)});%\n   \\draw [red,thick,domain=-45:225] plot ({0.4*cos(\\x)}, {0.5+0.4*sin(\\x)});%\n   \\node (xxx) at (0,-.2) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\n\\newcommand{\\user}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [black,fill=black] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [black,fill=black] (0,.5) circle (.2); %\n   \\node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n\n\\newcommand{\\activeuser}[3]{%\n \\node ({#1}) at ({#2}) {%\n  \\begin{tikzpicture}%\n   \\draw [red,fill=red] (-.25,0) -- (0,0.5) -- (0.25,0) -- (-0.25,0);%\n   \\draw [red,fill=red] (0,.5) circle (.2); %\n   \\node (xxx) [text width=0.6cm, align=center] at (-.35cm,-.4) {{#3}};%\n  \\end{tikzpicture}%\n } %\n}\n"
  },
  {
    "path": "doc/manual/intro/fig/repeater_local.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT};\n  \\user{u2}{ 2,0}{DL1XYZ};\t\n  \\user{u3}{ 4,0}{DL2XYZ};\t\n  \\draw[dotted] (5,4) -- (5,-1);\n  \\activeuser{u4}{ 6,0}{DL3XYZ};\n  \\user{u5}{ 8,0}{DL4XYZ};\n  \\user{u6}{10,0}{DL5XYZ};\n  \\activerepeater{R1}{1,3}{DB0ABC};\n  \\repeater{R2}{3,3}{DB0DEF};\n  \\activerepeater{R3}{7,3}{DB0GHI};\n  \\activerepeater{R4}{9,3}{DB0JKL};\n  \\draw[->] (u1) -- node[above,rotate=70]{GC: TG9} (R1);\n  \\draw[->] (R1) -- node[above,rotate=-70]{GC: TG9} (u2);\n  \\draw[->] (u4) -- node[above,rotate=70]{GC: TG8} (R3);\n  \\draw[->] (R3) -- node[above,rotate=-70]{GC: TG8} (u5);\n  \\draw[->] (R4) -- node[above,rotate=-70]{GC: TG8} (u6);\n  \\path[->] (R3) edge[dashed,bend left] node[above]{via network} (R4);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/repeater_privatecall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT 2621370};\n  \\activerepeater{R1}{1,3}{DB0ABC};\n  \\draw[dotted] (2,4) -- (2,-1);\n  \\user{u2}{ 4,0}{I/DL2XYZ\\\\2621234};\t\n  \\activerepeater{R2}{3,3}{I0ABC};\n  \\draw[->] (u1) -- node[above,rotate=70]{PC: 2621234} (R1);\n  \\draw[->] (R2) -- node[above,rotate=-70]{PC: 2621234} (u2);\n  \\path[->] (R1) edge[dashed,bend left] node[above]{via network} (R2);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/simplex_allcall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 6,2}{DL1XYZ, TG99};\n   \\user{u3}{ 6,0}{DL2XYZ, TG99};\n   \\user{u4}{ 6,-2}{DL3XYZ};\n   \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{All Call} (u2);\n   \\path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{All Call} (u3);\n   \\path[->] (u1) edge[bend right] node[above, rotate=-10]{$433.450 MHz$} node[below, rotate=-10]{All Call} (u4);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/simplex_groupcall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 6,2}{DL1XYZ, TG99};\n   \\user{u3}{ 6,0}{DL2XYZ, TG99};\n   \\user{u4}{ 6,-2}{DL3XYZ};\n   \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{GC: TG99} (u2);\n   \\path[->] (u1) edge node[above]{$433.450 MHz$} node[below]{GC: TG99} (u3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/simplex_privatecall.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{u1}{ 0,0}{DM3MAT};\n  \\activeuser{u2}{ 6,2}{DL1XYZ, TG99};\n  \\user{u3}{ 6,0}{DL2XYZ, TG99};\n  \\user{u4}{ 6,-2}{DL3XYZ};\n  \\path[->] (u1) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DL1XYZ} (u2);\n  \\path[->] (u2) edge[bend left] node[above, rotate=10]{$433.450 MHz$} node[below, rotate=10]{PC: DM3MAT} (u1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/talkgroup_ex1a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\repeater{R3}{9,3}{I0ABC};\n   \\path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/talkgroup_ex1b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\user{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\activeuser{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\activerepeater{R3}{9,3}{I0ABC, (TG2621)};\n   \\path[->] (u4) edge node[above,rotate=-70]{GC: TG2621} (R3);\n   \\path[->] (R1) edge node[above,rotate=70]{GC: TG2621} (u1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R3) edge[bend right] node[below]{GC: TG2621} (R2);\n   \\path[->] (R3) edge[bend right] node[above]{GC: TG2621} (R1);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/talkgroup_ex1c.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{u1}{ 0,0}{DM3MAT};\n   \\user{u2}{ 2,0}{DL1XYZ};\t\n   \\user{u3}{ 6,0}{DL2XYZ};\t\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{u4}{10,0}{I/DL3XYZ};\n   \\activerepeater{R1}{1,3}{DB0ABC, TG2621};\n   \\activerepeater{R2}{5,3}{DB0DEF, TG2621};\n   \\activerepeater{R3}{9,3}{I0ABC, (TG2621)};\n   \\path[->] (u1) edge node[above,rotate=70]{GC: TG2621} (R1);\n   \\path[->] (R1) edge node[above,rotate=-70]{GC: TG2621} (u2);\n   \\path[->] (R2) edge node[above,rotate=-70]{GC: TG2621} (u3);\n   \\path[->] (R3) edge node[above,rotate=-70]{GC: TG2621} (u4);\n   \\path[->] (R1) edge[bend left] node[below]{GC: TG2621} (R2);\n   \\path[->] (R1) edge[bend left] node[above]{GC: TG2621} (R3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/timeslot.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\usetikzlibrary{patterns,snakes}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}\n  \\draw[|-,dotted, semithick] (-1,-0.2) -- (0,-0.2);\n  \\draw[|-,semithick] (0,-0.2) -- (1,-0.2);\n  \\draw[|-,semithick] (1,-0.2) -- (2,-0.2);\n  \\draw[|-,semithick] (2,-0.2) -- (3,-0.2);\n  \\draw[|-,semithick] (3,-0.2) -- (4,-0.2);\n  \\draw[|-,semithick] (4,-0.2) -- (5,-0.2);\n  \\draw[|-,semithick] (5,-0.2) -- (6,-0.2);\n  \\draw[|->,dotted,semithick] (6,-0.2) -- (7,-0.2);\n  \\node at (7, -.5) {$t$};\n  \\draw [thick,decoration={brace,mirror},decorate] (0,-0.4) -- (1,-0.4) node [pos=0.5, anchor=north,yshift=-0.55] {$30\\ ms$}; \n  \\fill[red!30] (0.1,0) -- (0.1,1) -- (0.9,1) -- (0.9,0) -- cycle;\n  \\node at (0.5,0.5) {TS 1};\n  \\fill[blue!30] (1.1,0) -- (1.1,1) -- (1.9,1) -- (1.9,0) -- cycle;\n  \\node at (1.5,0.5) {TS 2};\n  \\fill[red!30] (2.1,0) -- (2.1,1) -- (2.9,1) -- (2.9,0) -- cycle;\n  \\node at (2.5,0.5) {TS 1};\n  \\fill[blue!30] (3.1,0) -- (3.1,1) -- (3.9,1) -- (3.9,0) -- cycle;\n  \\node at (3.5,0.5) {TS 2};\n  \\fill[red!30] (4.1,0) -- (4.1,1) -- (4.9,1) -- (4.9,0) -- cycle;\n  \\node at (4.5,0.5) {TS 1};\n  \\fill[blue!30] (5.1,0) -- (5.1,1) -- (5.9,1) -- (5.9,0) -- cycle;\n  \\node at (5.5,0.5) {TS 1};  \n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/trunk_net_ex1.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\user{r1}{ 0,0}{Clean 1};\n  \\user{r2}{ 2,0}{Clean 2};\t\n  \\draw[dotted] (3,4) -- (3,-1);\n  \\user{s1}{ 4,0}{Security 1};\n  \\user{z} { 6,0}{HQ};\n  \\draw[dotted] (7,4) -- (7,-1);\n  \\user{s2}{ 8,0}{Security 2};\n  \\user{r3}{10,0}{Clean 3};\n  \\repeater{R1}{1,3}{Terminal 1, TG: C,S};\n  \\repeater{R2}{5,3}{Terminal 2, TG: C,S};\n  \\repeater{R3}{9,3}{Apron, TG: S};\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/trunk_net_ex2.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n \\begin{tikzpicture}[every node/.style={scale=.8}]\n  \\activeuser{r1}{ 0,0}{Clean 1};\n  \\user{r2}{ 2,0}{Clean 2};\t\n  \\draw[dotted] (3,4) -- (3,-1);\n  \\activeuser{s1}{ 4,0}{Security 1};\n  \\activeuser{z} { 6,0}{HQ};\n  \\draw[dotted] (7,4) -- (7,-1);\n  \\user{s2}{ 8,0}{Security 2};\n  \\activeuser{r3}{10,0}{Clean 3};\n  \\activerepeater{R1}{1,3.5}{Terminal 1, TG: C,S};\n  \\activerepeater{R2}{5,3.5}{Terminal 2, TG: C,S};\n  \\activerepeater{R3}{9,3.5}{Apron, TG: S};\n  \\draw[->] (r1) -- node[above,rotate=74] {PC: Clean 3} (R1);\n  \\path[->,dashed] (R1) edge [bend left] node[above] {via network} (R3);\n  \\draw[->] (R3) -- node[above,rotate=-74] {PC: Clean 3} (r3);\n  \\draw[->] (z) -- node[above,rotate=-74] {PC: Security 1} (R2);\n  \\draw[->] (R2) -- node[above,rotate=74] {PC: Security 1} (s1);\n \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/trunk_net_ex3.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Clean 1};\n   \\activeuser{r2}{ 2,0}{Clean 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Security 1};\n   \\activeuser{z} { 6,0}{HQ};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Security 2};\n   \\user{r3}{10,0}{Clean 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: C,S};\n   \\activerepeater{R2}{5,3}{Terminal 2, TG: C,S};\n   \\repeater{R3}{9,3}{Apron, TG: S};\n   \\draw[->] (z) -- node[above,rotate=-74] {TG: C} (R2);\n   \\path[->,dashed] (R2) edge [bend right] node[above] {via network} (R1);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/trunk_net_ex4a.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Clean 1};\n   \\activeuser{r2}{ 2,0}{Clean 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Security 1};\n   \\user{z} { 6,0}{HQ};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Security 2};\n   \\user{r3}{10,0}{Clean 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: C,S};\n   \\repeater{R2}{5,3}{Terminal 2, TG: C,S};\n   \\activerepeater{R3}{9,3}{Apron, TG: S,(C)};\n   \\draw[->] (r3) -- node[above,rotate=-74] {TG: C} (R3);\n   \\path[->,dashed] (R3) edge [bend right] node[above] {via network} (R1);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/fig/trunk_net_ex4b.tex",
    "content": "\\documentclass{standalone}\n\\usepackage{tikz}\n\\usetikzlibrary{shapes.geometric}\n\\input{repeater}\n\\begin{document}\n  \\begin{tikzpicture}[every node/.style={scale=.8}]\n   \\activeuser{r1}{ 0,0}{Clean 1};\n   \\activeuser{r2}{ 2,0}{Clean 2};\t\n   \\draw[dotted] (3,4) -- (3,-1);\n   \\user{s1}{ 4,0}{Security 1};\n   \\activeuser{z} { 6,0}{HQ};\n   \\draw[dotted] (7,4) -- (7,-1);\n   \\user{s2}{ 8,0}{Security 2};\n   \\activeuser{r3}{10,0}{Clean 3};\n   \\activerepeater{R1}{1,3}{Terminal 1, TG: C,S};\n   \\activerepeater{R2}{5,3}{Terminal 2, TG: C,S};\n   \\activerepeater{R3}{9,3}{Apron, TG: S,(C)};\n   \\draw[->] (z) -- node[above,rotate=-74] {TG: C} (R2);\n   \\path[->,dashed] (R2) edge [bend right] node[above] {via Netzwerk} (R1);\n   \\path[->,dashed] (R2) edge [bend left] node[above] {via Netzwerk} (R3);\n   \\draw[->] (R1) -- node[above,rotate=74] {TG: C} (r1);\n   \\draw[->] (R1) -- node[above,rotate=-74] {TG: C} (r2);\n   \\draw[->] (R3) -- node[above,rotate=-74] {TG: C} (r3);\n  \\end{tikzpicture}\n\\end{document}\n"
  },
  {
    "path": "doc/manual/intro/foreknowledge.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introForeknowledge\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Basics: Repeater operations</title>\n</info>\n\n<para>\n  This section briefly describes the common amateur radio FM-repeater operation. The majority of all\n  licensed operators will be familiar with this topic. \n</para>\n\n<para>\n  If you are not yet licensed and interested into amateur radio, consider reading this section. \n  Otherwise, skip right to <xref linkend=\"introOrigin\"/>\n</para>\n\n<para>\n  The majority of connections between HAM operators are made in the so-called simplex mode. That \n  is, the two operators transmit and receive alternately on the same frequency<footnote><para>\n    This is actually called <emphasis>semi-duplex</emphasis>, however the term \n    <quote>simplex</quote> stuck. The term simplex actually refers to the situation, where \n    there is only one transmitter and possibly many receivers (e.g., broadcast).</para></footnote> \n  and the connection is direct. This works very well on HF where world-wide direct connections can \n  be made.\n</para>\n\n<example>\n  <title>Typical FM simplex operation</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_simplex_a.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_simplex_b.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  For this example, DM3MAT transmits directly to DL2XYZ on the frequency 144.500 MHz. The latter \n  then also answers directly on the same frequency.\n</para>\n\n<para>\n  On higher frequencies, however, radio waves behave more like light and it gets increasingly\n  difficult<footnote><para>Also on VHF and UHF, larger distances can be bridged using an elevated \n    location and larger antennas.</para></footnote> to bridge significant distances beyond\n  the horizon. This fact limits the operating range of simple hand-held radios. To still cover \n  a large area, repeaters can be used.\n</para>\n\n<para>\n  Repeaters are autonomous amateur radio stations that are usually located on a mountain,\n  hill or high tower. This allows them to easily cover a large area. They receive signals from\n  HAM operators and retransmit them at the same time. To do that, they cannot send and receive on \n  the same frequency (otherwise they would interfere with themselves). Therefore, repeaters operate\n  in the so-called duplex mode. That is, the repeater receives on one frequency (the so-called \n  input frequency) and transmits the received signal on another frequency (the \n  output frequency) simultaneously.\n</para>\n\n<example xml:id=\"figIntroFMRepeater\">\n  <title>Simple FM repeater operation</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_duplex_a.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_duplex_b.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  For this example, DM3MAT sends on the input frequency 431.9625 MHz to the repeater DB0LDS. The \n  repeater transmits the received signal on its output frequency 439.5625 MHz. On that frequency,\n  DL2XYZ receives the original call.\n</para>\n\n<para>\n  The <xref linkend=\"figIntroFMRepeater\"/> shows a common repeater operation on UHF. Here, the \n  operator DM3MAT transmits its call to DL2XYZ not directly but on the input frequency of the \n  repeater DL0LDS (431.9625 MHz). The repeater receives the call and transmits it simultaneously\n  on its output frequency (439.5625 MHz). This signal is then received by DL2XYZ. Consequently, \n  the call has reached its destination, although DM3MAT and DL2XYZ may not be able to communicate \n  directly. The reply of DL2XYZ to DM3MAT follows the same path. Here DL2XYZ transmits on the\n  repeater input frequency, and DM3MAT receives that call on the repeater output frequency. This \n  way the two operators can communicate with each other even if they are not able to reach \n  each other directly.\n</para>\n\n\n<section>\n<info>\n  <title>Echolink</title>\n</info>\n<para>\n  However, there are situations, where two operators are far away and they cannot reach the same\n  repeater. For these cases, it is possible to connect two repeaters via the \n  <link xlink:href=\"http://www.echolink.org/\">EchoLink</link> network.\n</para>\n\n<example xml:id=\"figIntroEchoLink\">\n  <title>Repeater operation with Echolink</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_echolink_a.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_echolink_b.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/fm_echolink_c.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n<para>\n  DM3MAT connects repeaters DB0SP (near Berlin) and DB0LEI (near Leipzig) via EchoLink. Now, they \n  are able to communicate with each other.\n</para>\n\n<para>\n  This network allows to link FM repeaters via <trademark class=\"trade\">internet</trademark> or to \n  connect directly to a remote repeater via internet. Many FM repeaters are connected to the \n  EchoLink network, allowing for world-wide communication with simple hand-held radios. \n</para>\n\n<para>\n  Frequently, it is also possible to control a repeater over-the-air and to connect that repeater \n  to some other repeater via EchoLink. Usually, this can be done by sending the EchoLink number of \n  the destination repeater using DTMF to the local repeater. This is shown in \n  <xref linkend=\"figIntroEchoLink\"/> above. There, DM3MAT sends the EchoLink number 662699 of the \n  repeater DB0LEI near Leipzig to his local repeater DB0SP near Berlin. Then the local repeater\n  (DB0SP) will link with the destination repeater (DB0LEI) via the EchoLink network. For some \n  limited time, both repeaters will act like one logical repeater. That is, everything that is \n  received by one repeater will also be transmitted by the other. This way, the two operators \n  DM3MAT and DL2XYZ can communicate although they cannot reach a common repeater.\n</para>\n\n<note>\n  <para>\n    Once two repeaters are linked via EchoLink, they behave like a single repeater.\n  </para>\n</note>\n\n<para>\n  All over the world, there are FM repeater that are part of the EchoLink network. Therefore, it\n  is possible to communicate world-wide at any time using simple hand-held radios that are as cheap as \n  40€ or even less.\n</para>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/intro/introduction.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<chapter xml:id=\"introduction\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Introduction</title>\n\n  <abstract>\n    <para>\n      The chapter tries to provide an introduction to <acronym>DMR</acronym>\n      (digital mobile radio) targeted at the unexperienced operator and anyone\n      interested in this topic. I try to hide details until it gets absolutely \n      necessary to explain them. The majority of DMR introductions I've found, \n      are more or less extensive glossaries (if you are interested in that, see \n      <xref linkend=\"glossary\"/>). They are hard to comprehend, unless one has at \n      least some experience with DMR. \n    </para>\n    <para>\n      The perceived complexity of DMR comes from its origin as a radio standard\n      for commercial applications at large events and companies (i.e., \n      <link xlink:href=\"https://en.wikipedia.org/wiki/Trunked_radio_system\">trunked\n      radio</link>). Therefore, I will first describe an example how DMR is used commercially\n      before I describe how it is used in amateur radio. I hope that this approach will make some\n      of the weird terms and concepts of DMR clearer.\n    </para>\n  </abstract>\n</info>\n\n<xi:include href=\"foreknowledge.xml\"/>\n<xi:include href=\"origin.xml\"/>\n<xi:include href=\"simplex.xml\"/>\n<xi:include href=\"local.xml\"/>\n<xi:include href=\"private.xml\"/>\n<xi:include href=\"textmessages.xml\"/>\n<xi:include href=\"talkgroup.xml\"/>\n<xi:include href=\"roaming.xml\"/>\n<xi:include href=\"technicalbackground.xml\"/>\n<xi:include href=\"codeplug.xml\"/>\n<xi:include href=\"networks.xml\"/>\n\n</chapter>"
  },
  {
    "path": "doc/manual/intro/local.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introLocal\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Local Repeater Operation</title>\n</info>\n\n<para>\n  One central objective of DMR is to be repeater transparent. That is, it does not matter which \n  repeater you use. You will always be able to reach the same groups and be always reachable \n  through the same means (private or group call). This concept is violated by the talk groups 8\n  &amp; 9. The are the regional and local talk groups.\n</para>\n \n<example xml:id=\"figIntroTG9TG8\">\n  <title>Two regions with two repeaters each.</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/repeater_local.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para> \n  The talk group 9 (TG9) is the so-called local talk group. Group calls to that talk group are not \n  forwarded though the network and only retransmitted locally. Usually <quote>locally</quote> means\n  only the repeater. Sometimes, however, these calls are also forwarded to other repeaters \n  nearby. This case is shown in <xref linkend=\"figIntroTG9TG8\"/> on the left side. Here DM3MAT sends \n  a group call to TG9 via the repeater DB0ABC. This call is not forwarded to any other repeater and \n  thus is only received in the local area around the repeater. DL1XYZ is in that local area and may \n  receive that call if he configured his radio to receive calls from the TG9.\n</para>\n\n<para>\n  The talk group 8 (TG8) is the so-called regional talk group. A call to that talk group is usually \n  forwarded to all repeaters within a specific region. Which repeaters are part of a \n  <quote>region</quote>, is a decision of the repeater administrators. So it is hard to predict to \n  which repeaters a regional call gets forwarded. In <xref linkend=\"figIntroTG9TG8\"/> on the right\n  side, DL3XYZ sends a group call to TG8 to the repeater DB0GHI. This call gets forwarded to \n  all repeaters in the <quote>region</quote>. In this case, also to the repeater DB0JKL within the\n  same region. Therefore, all participants within that region are able to receive the group call \n  if they configured their radios accordingly. In this example, not only DL4XYZ received the call \n  but also DL5XYZ who is not close to the repeater DB0GHI and would have missed a group call to the \n  local talk group.\n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/intro/networks.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introNetworks\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>DMR Networks</title>\n</info>\n\n<para>\n  Within the previous sections, I tried to outline the concepts and some technical details of a \n  DMR network and how a codeplug might be assembled. These concepts, however, apply only to the\n  so-called <glossterm>Brandmeister</glossterm> network. This is the network in the background that\n  routes your private and group calls, connects repeaters etc. In Germany, this is the dominant \n  network. But also world wide, it connects the majority of repeaters (about 5000). However, there\n  are also other networks. There is the <glossterm>DMR-MARC</glossterm> network and the \n  <glossterm>DMR+</glossterm> network. Which network you are likely to encounter, depends on your \n  location. In countries like France, Spain, BeNeLux, Poland, Czech Republic and Slovakia, almost\n  all repeaters are connected to the Brandmeister network, while in Denmark the DMR+ network \n  dominates. In the USA and Austria, DMR-MARC repeaters aren't rare. All these networks do not \n  differ on the technical level. That is, you DMR-ID is valid in all of these networks and you \n  can use any DMR Tier II radio.\n</para>\n\n<para>\n  The concepts however, in particular how group calls are performed, depends heavily on the \n  network. This means, that you need to configure the repeater channels for a DMR+ repeater\n  in a different way compared to a Brandmeister repeater.\n</para>\n\n<section xml:id=\"itroReflector\">\n<info><title>Reflectors (DMR+)</title></info>\n\n<para>\n  <glossterm>Reflector</glossterm>s play an important role in the DMR+ network. They represent\n  a talk group within the DMR+ network.\n</para>\n\n<para>\n  The major difference between a reflector and a talk group is, that they cannot be simply called\n  using a group call. They are subscribed to a local repeater by a private call to the \n  reflector. Then all repeaters subscribed to that reflector behave like a single repeater. You will\n  then participate on that reflector by performing a group call to TG9, the local talk group. Your\n  call will then be sent to the reflector as well as to all other reflectors currently subscribed \n  on the repeater and consequently to all repeaters also subscribed to that reflector.\n</para>\n\n<para>\n  This has the advantage of a much simpler codeplug assembly, as only two channels are configured \n  for each repeater. One for each time slot. The default transmit contact will always be the \n  local talk group TG9. To subscribe a reflector, a private call is started to the reflector from\n  the contact list. This implies that the contact list should contain all reflectors you are \n  interested in. This concept is also much closer to the semi-analog concepts of EchoLink. However,\n  advanced features like roaming are not possible this way. Also the repeater transparency gets \n  lost. Instead of simply starting a group call to the destination talk group, the local repeater \n  needs to be <quote>configured</quote>. Once that configuration is done, the communication will\n  happen on the local talk group TG9, even if the communication is not local anymore. \n</para>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/intro/origin.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introOrigin\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>DMR Introduction &amp; Origin</title>\n</info>\n\n<para>\n  <acronym>DMR</acronym> (digital mobile radio) is a digital radio standard to transmit speech \n  and data. That is, the speech is not directly modulated on the carrier but is first digitalized and \n  compressed using a lossy compression codec (VOCODER). The compressed speech is then transmitted as \n  data packets. The latter allows to attach meta-information to the data packet like source and\n  destination of the packet. \n</para> \n\n<para>\n  <acronym>DMR</acronym> was designed to be the digital replacement for analog trunking \n  networks in commercial applications. A classic example for such a commercial application\n  of DMR would be a civil air port. With this, I do not mean the air-traffic radio but all \n  the communication of the ground staff in and around the actual air port buildings.\n</para>\n\n<para>\n  At such an imaginary air port, there is a huge staff with a wide variety of tasks. For example \n  (without any claim of completeness):\n</para>\n\n<itemizedlist>\n  <listitem><para>The cleaning crew,</para></listitem>\n  <listitem><para>technicians,</para></listitem>\n  <listitem><para>security staff,</para></listitem>\n  <listitem><para>apron staff for refueling, luggage and catering,</para></listitem>\n  <listitem><para>the fire brigade and </para></listitem>\n  <listitem><para>the headquarters.</para></listitem>\n</itemizedlist>\n\n<para>\n  All these people carry a radio and should be able to \n</para>\n<itemizedlist>\n  <listitem><para>Directly call the headquarters. All staff should be able to call the headquarters\n    directly.</para></listitem>\n  <listitem><para>Direct communication between members of the same group, without interfering with\n    other groups. For example, the cleaning staff should be able to communicate with each other, \n    without interfering with the fire brigade.</para></listitem>\n  <listitem><para>Each person should be able to call a complete group. For example, the headquarters \n    may call the entire fire brigade or one member of the security staff may call the entire security\n    for help.</para></listitem>\n</itemizedlist>\n\n<para>\n  An air port, however, is a rather large area. Consequently, not all staff members are able to \n  reach all others. Therefore, some repeaters must be installed to cover the entire air port \n  including all interiors.\n</para>\n\n<para>\n  If you compare this scenario with the classic FM repeater networks (see \n  <xref linkend=\"introForeknowledge\"/>), it gets clear that is hard to implement these concepts \n  using analog FM repeaters. Especially, if several repeaters are connected though a network. In \n  this case, a single call on one repeater may block the entire network<footnote><para>\n    There are means to implement this concept on analog repeater networks using tone-signaling \n    techniques (e.g., DTMF, five-tone etc.).</para></footnote>.\n</para>\n\n<para>\n  Certainly, it would be much better if only those repeaters get activated that are actually \n  required for the communication between two parties. Then, the remaining repeaters are still \n  available for the rest of the staff. This routing, however, should happen automatically. An \n  operator may not know, which repeater to use to reach a particular person.\n</para>\n\n<para> \n  DMR was designed to implements such complex communication networks without requiring from every\n  participant to have detailed knowledge about the structure of the network. That is, the knowledge \n  about where every repeater is installed and which participants are reachable on which repeater.\n</para>\n\n<note>\n  <para>\n    DMR is more similar to a phone network that to classic FM repeater networks.\n  </para>\n</note>\n\n<para>\n  Speaking of phone networks: Each participant and thus his/her radio is uniquely identified by a \n  number. The DMR-ID. This is a number between 1 and 16777215. And like for any other phone network,\n  each participant may call any other directly using this number. This call is called \n  <glossterm>Private Call</glossterm>. \n</para>\n\n<para>\n  And there are groups. Each of these so-called <glossterm>Talk Group</glossterm>s is also assigned \n  a unique number. A talk group can be used to group all staff with a specific task (e.g., the \n  security, fire brigade, etc.). It is then possible to reach all members of this group at once, \n  by performing a <glossterm>Group Call</glossterm> to that talk group. The network, however, \n  does not know which participant is member of which group. Consequently, the radio of the participant \n  needs to know which group calls to accept and which to ignore.\n</para>\n\n<note>\n  <para>\n    This point is important to remember: The DMR network does not know which participant is member of \n    which group. The radio needs to be configured to react on specific group calls.\n  </para>\n</note>\n\n<example xml:id=\"figIntroExNet1\">\n  <title>Simplified air-port network</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/trunk_net_ex1.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  There are 3 cleaning staff, two security and one headquarters. To cover the entire area, three \n  repeaters are required. One in terminal 1, one in Terminal 2 and one on the apron.\n</para>\n\n<para>\n  <xref linkend=\"figIntroExNet1\"/> is a simplified air port network (in reality, it is much larger \n  and way more complex). Consider the situation, where a cleaning stuff 1 &amp; 3 want to communicate.\n  At the same time, the headquarters want to talk to security 1. In an simple analog network, the \n  call between cleaning 1 &amp; 3 would block the entire network and therefore the call between \n  the headquarters and security 1. \n</para>\n\n<example xml:id=\"figIntroExNet2\">\n  <title>Simultaneous calls</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/trunk_net_ex2.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n<para>\n  Two Simultaneous private calls in the example network between cleaning 1 &amp; 3 as well as between \n  headquarters and security 1.\n</para>\n\n<para>\n  Private calls in DMR networks only use those repeaters, that are actually required to establish \n  the communication. This is shown in <xref linkend=\"figIntroExNet2\"/>: Cleaning 1 starts a private\n  call to cleaning 3. As the DMR network knows that cleaning 3 was last active on the apron repeater, \n  this call gets routed only though repeaters terminal 1 and apron. The repeater in terminal 2, however, \n  is not affected. Consequently, this repeater remains available for the call between the headquarters\n  and security 1.  \n</para>  \n\n<note>\n  <para>\n    The network only knows at which repeater each participant was last active. The network will therefore\n    try to establish a connection though that repeater to the participant.\n  </para>\n</note>\n\n<para>\n  During the call between cleaning 1 &amp; 3, the repeaters in terminal 1 and apron are blocked. \n  This means, that the headquarters may not be able to reach cleaning 2 and security 2 immediately. \n  This sounds worse than it actually is. In contrast to classic phone networks, a direct call is \n  considered interrupted, once the calling participant releases the PTT button. To this end, the \n  headquarters may use the pauses between calls to reach the other participants. \n</para>\n\n<para>\n  In the next <xref linkend=\"figIntroExNet3\"/>, the headquarters want to reach all cleaning staff. \n  Therefore, they start a group call to the talk group <quote>cleaning</quote> (<emphasis>C</emphasis> \n  for <quote>cleaning</quote> and <emphasis>S</emphasis> for <quote>security</quote>). With this \n  call, it can reach cleaning 1 &amp; 2 immediately. However, cleaning 3 does not receive that call.\n</para>\n\n<para>\n  This is due to the fact, that the DMR network does not know which participants are members of \n  which groups. As the cleaning crew is usually not on the apron, the apron repeater has not \n  subscribed the talk group <quote>cleaning</quote>. Therefore, it does not forward group calls \n  to that talk group.\n</para>\n\n<example xml:id=\"figIntroExNet3\">\n  <title>Temporary subscription of talk groups.</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/trunk_net_ex3.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/trunk_net_ex4a.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/trunk_net_ex4b.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  To remain reachable for group calls, cleaning 3 needs to temporarily subscribe the apron repeater\n  to the <quote>cleaning</quote> talk group. This can be done by starting a group call to that talk\n  group on the apron repeater. Then the repeater will temporarily subscribe to that talk group for\n  a limited amount of time (usually between 10-30min). During that time, the repeater will forward \n  group calls to that talk group and cleaning 3 remains reachable via that repeater.\n</para>\n\n<para>\n  This temporary subscription will be renewed every time a participant starts a group call to that \n  talk group on this repeater. \n</para>\n\n<para>\n  With these examples, the most basic terms of DMR (DMR-ID, talk group, private and group calls as \n  well as talk group subscriptions where introduced and explained on an example network. The \n  following sections will concern the use DMR in ham radio.\n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/intro/private.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introPrivateCalls\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Private calls</title>\n</info>\n\n<para>\n  Private calls allow to call other participants directly without interfering with other \n  calls (except for using the repeater). In the introduction into DMR above, the private call \n  over several repeaters has been described. I consider this aspect of DMR particularly \n  interesting. With the exception of TG 8 &amp; 9, private and group calls are transparent \n  with respect to the repeaters used. It simply does not matter which repeaters are actually\n  used to establish a connection. Therefore, I do not need ot know where the other \n  participants are located. \n</para>\n\n<example xml:id=\"figIntroRepPriv\">\n  <title>Direct calls between countries.</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/repeater_privatecall.png\" width=\"5cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  Consider the typical vacation situation: An OM at his holiday location may want to participate\n  in his local afternoon net. He can do that by simply starting a group call to the nets talk group\n  over the repeater at his holiday location. Now the local holiday repeater has subscribed to the \n  local talk group at home and the OM can participate as usual in the net. The other participants \n  in the net may not even recognize that the OM is at his holiday location.\n</para>\n\n<para>\n  In a similar fashion, private calls can be started and received at the holiday location. The \n  DMR network knows the repeater a participant was last active on. By briefly pressing the PTT\n  at the holiday repeater, the OM is registered and can now receive private calls at his \n  holiday location. The friends at home may not even know where the OMs current location is nor \n  which repeater to use to reach him. The network takes care of that.\n</para>\n\n<para>\n  <xref linkend=\"figIntroRepPriv\"/> shows such a private call between countries. DM3MAT starts a \n  private call to DL2XYZ using his local repeater DB0ABC. DL2XYZ, however, is at his holiday \n  location in Italy. As he had registered himself at that repeater, the private call gets \n  forwarded to the holiday repeater I0ABC in Italy, where DL2XYZ can receive it. DM3MAT does not \n  need to know the location of the callee nor which repeaters are near to him. This automatic \n  routing of calls (group and private calls) is a major advantage over the analog FM repeater \n  network and EchoLink. For the latter, the ID of the destination repeater needs to be known.\n</para>\n</section>"
  },
  {
    "path": "doc/manual/intro/roaming.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introRoaming\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Roaming</title>\n</info>\n\n<para>\n  Usually all repeater within a region will subscribe the same talk groups. This allows to operate in \n  these talk groups in a repeater transparent way. Therefore, it does not matter which repeater is being \n  used within the region, the same talk group remains reachable. In the region Berlin &amp; \n  Brandenburg, this it the TG2621.\n</para>\n\n<para>\n  It therefore makes sense to enter all repeaters into one list that have the same talk groups \n  subscribed. If the radio now would automatically select a reachable repeater, one could drive around\n  in the region and would stay connected to these talk groups irrespective of the own position in the \n  region. This feature exists in many radios and is called <glossterm>Roaming</glossterm>. Many of the slightly more \n  expensive devices support this feature (e.g., AnyTone). The cheapest ones usually do not<footnote>\n    <para>This is somewhat weird, as this feature is a pure firmware feature and does not need any\n      additional hardware.</para></footnote>. \n</para>\n\n<para>\n  To use this feature, all channels with a certain talk group should be added to a list. The so-called\n  <glossterm>Roaming Zone</glossterm>. This could actually be done automatically, but the programming \n  software for these devices is usually not very user friendly.\n</para>\n\n<para>\n  If the signal strength of the currently selected repeater falls below a certain threshold (usually \n  -105dBm), the radio will start to search the roaming zone for a repeater which is stronger than\n  this threshold. This only happens if the radio is in standby. That is, if neither something is received\n  nor transmitted.\n</para>\n\n<para>\n  If the radio finds a stronger repeater in the roaming zone, it automatically changes to that repeater.\n  The new repeater does not necessarily needs to be the strongest in the zone. It only needs to be \n  stronger than the threshold. If no stronger repeater is found, the radio remains on the currently \n  selected one.\n</para>\n\n<para>\n  This roaming can also be set to <quote>manual</quote>. That is, the roaming search will only \n  start if the signal strength is lower than the threshold and the PTT is pressed or the search is \n  started from the menu.\n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/intro/simplex.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introSimplex\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>DMR Simplex Operation</title>\n</info>\n\n<para>\n  The most simple form of a DMR QSO<footnote><para>QSO is a code for call or connection between two \n    amateur radio stations.</para></footnote> is the simplex QSO. That is a direct connection between \n    two two DMR radios. Like for the DMR repeater operations, this could be a private, group or \n    so-called <glossterm>All Call</glossterm>. \n</para>\n\n<example xml:id=\"figIntroSplxPc\">\n  <title>Simplex private call.</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/simplex_privatecall.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  In <xref linkend=\"figIntroSplxPc\"/> a simple simplex private call from DM3MAT to DL1XYZ is shown \n  as well its reply. Both operators transmit and receive on the same frequency (here the DMR calling\n  channel at 433.450 MHz). Although other operators are in the area (DL2XYZ &amp; DL3XYZ) which \n  receive the signal, their radios remain silent. This is because this is a private call to a \n  specific operator and only the radio of that operator will receive the call. All other radios will ignore the\n  call. The channel, however, remains occupied during that call. \n</para>\n\n<para>\n  At that point it is worth mentioning, that if DL1XYZ answers directly to the initial call by \n  pressing the PTT, he will answer with a private call to DM3MAT. He does not need to search for\n  number of DM3MAT in his address book. The direct answering to calls is only possible for several\n  seconds after the end of the initial call. After that period (called <glossterm>Hang \n  Time</glossterm>) a press on the PTT will start a call to the default contact (see \n  <xref linkend=\"introCPChannel\"/>) associated with the simplex channel.\n</para>\n\n<example xml:id=\"figIntroSplxGc\">\n  <title>Simplex group call</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/simplex_groupcall.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  It is not only possible to call single operators in simplex mode. Also groups can be called using\n  group calls. A common talk group for the simplex mode has the number 99, (TG99, for <quote>talk \n  group 99</quote>). These group calls are then received by all radios that are configured \n  accordingly. Like for the repeater operation, also in simplex mode, the radio needs to know which \n  groups the operator belongs to and therefore, which talk groups to receive on which channels. This \n  is done using so-called <glossterm>Group List</glossterm>s, which are discussed later.\n</para>\n\n<para>\n  In <xref linkend=\"figIntroSplxGc\"/> such a simplex group call is shown. There DM3MAT calls the \n  talk group TG99. As DL1XYZ as well as DL2XYZ configured their radios to receive that call on \n  simplex channels, they do so. DL3XZY did not, so he does not receive the call. DL1XYZ &amp; \n  DL2XYZ can now respond to that call by pressing on the PTT within the hang time irrespective of \n  their default contact for the channel. \n</para>\n\n<example xml:id=\"figIntroSplxAc\">\n  <title>Simplex all call</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/simplex_allcall.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  To be sure that a simplex call gets received by all operators in the area, a so-called \n  <glossterm>All Call</glossterm> should be used. This is a special call type to the reserved \n  number 16777215, that gets received by all radios irrespective of their configuration. For the \n  <xref linkend=\"figIntroSplxAc\"/>, the all call by DM3MAT gets received by all operators including \n  DL3XYZ. By directly answering within the hang time, all participants are able to respond to that \n  call with an all call as well. \n</para>\n\n<note>\n  <para>\n    In short: A DMR channel consists of a transmit and receive frequency (identical on simplex channels), \n    a default contact that gets called whenever the PTT button is pressed and a list of group calls \n    the radio will receive on that channel. \n  </para>\n</note>\n\n<table xml:id=\"tabIntroSimplexFreq\">\n  <title>DMR simplex frequencies</title>\n  <caption>The list of eight common DMR simplex channels. The channel S0 is the calling channel.</caption>\n  <thead>\n    <tr><td>Name</td><td>Frequency</td><td>Name</td><td>Frequency</td></tr>\n  </thead>\n  <tbody>\n    <tr> <td>S0 (call)</td><td>433.4500 MHz</td> <td>S4</td><td>433.6500 MHz</td></tr>\n    <tr> <td>S1</td><td>433.6125 MHz</td> <td>S5</td><td>433.6625 MHz</td></tr>\n    <tr> <td>S2</td><td>433.6250 MHz</td> <td>S6</td><td>433.6750 MHz</td></tr>\n    <tr> <td>S3</td><td>433.6375 MHz</td> <td>S7</td><td>433.6875 MHz</td></tr>\n  </tbody>\n</table>\n\n<para>\n  <xref linkend=\"tabIntroSimplexFreq\"/> lists the common simplex channel frequencies. The channel \n  S0 is the calling channel. Especially in densely populated areas, you should switch to another \n  channel for the actual QSO and use S0 only for the initial call.\n</para>\n</section>\n"
  },
  {
    "path": "doc/manual/intro/talkgroup.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introTalkGroup\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Talk Group Operation</title>\n</info>\n\n<para>\n  As mentioned in <xref linkend=\"introPrivateCalls\"/>, DMR aims at being repeater transparent. That\n  is, it does not matter which repeater is used for the operation. This is also true for talk \n  groups. In this section, I'll continue with the example of an OM at his holiday location, who wants\n  to participate in his afternoon net. For example, the afternoon net is happening on the talk \n  group 2621 (Berlin/Brandenbug, BB). This talk group is usually statically subscribed on all \n  repeaters in the states Berlin and Brandenbug. That means that the local afternoon net can be \n  performed in this talk group without any additional action in this area. (see \n  <xref linkend=\"figIntroTG\"/> top image).\n</para>\n\n<example xml:id=\"figIntroTG\">\n  <title>Talk group operation between countries</title>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/talkgroup_ex1a.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/talkgroup_ex1b.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/talkgroup_ex1c.png\" width=\"10cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</example>\n\n<para>\n  For the OM at his holiday location, this it not true. An Italian repeater will certainly not \n  subscribe the BB talk group statically. Therefore, the OM at the holiday location will not hear \n  his afternoon net. He knows, however, when this net starts. So he can perform a group call to the that TG \n  shortly before the net starts (see <xref linkend=\"figIntroTG\"/> second image). This will \n  subscribe the BB talk group temporarily at the holiday repeater (I0ABC). Now, the OM can hear and\n  participate in the afternoon net. The subscription will be renewed whenever he starts a group \n  call to that TG.\n</para>\n\n<para>\n  Once the OM subscribed the TG to the holiday repeater, he can participate normally at his \n  afternoon net. All other participants will not even notice that he is not in the area and is \n  participating from an italien repeater.\n</para>\n\n<table xml:id=\"tabIntroTG\">\n  <thead>\n    <tr><td>Name</td> <td>Talk group number</td></tr>\n  </thead>\n  <tbody>\n    <tr><td>Global</td> <td>91</td></tr>\n    <tr><td>Europe</td> <td>92</td></tr>\n    <tr><td>Germany</td> <td>262</td></tr>\n    <tr><td>Mecklenburg-Vorpommern &amp; Sachsen-Anhalt</td> <td>2620</td></tr>\n    <tr><td>Berlin &amp; Brandenburg</td> <td>2621</td></tr>\n    <tr><td>Hamburg &amp; Schleswig-Holstein</td> <td>2622</td></tr>\n    <tr><td>Niedersachsen &amp; Bremen</td> <td>2623</td></tr>\n    <tr><td>Nordrhein-Westfalen</td> <td>2624</td></tr>\n    <tr><td>Rheinland-Pfalz &amp; Saarland</td> <td>2625</td></tr>\n    <tr><td>Hessen</td> <td>2626</td></tr>\n    <tr><td>Baden-Württemberg</td> <td>2627</td></tr>\n    <tr><td>Bayern</td> <td>2628</td></tr>\n    <tr><td>Sachsen &amp; Thüringen</td> <td>2629</td></tr>\n  </tbody>\n  <title>Some talk groups</title>\n</table>\n\n<section>\n<info><title>Cluster</title></info>\n<para>\n  In contrast to the regional talk group TG8, all other talk groups are reachable from everywhere \n  in the DMR network. This means, that the OM at his holiday location can easily participate in \n  his afternoon net from everywhere as described above. \n</para>\n\n<para>\n  If the net, however, is happening in the regional TG8, the OM at his holiday location cannot \n  participate. This talk group is only reachable from within the region. If the OM starts a call to\n  TG8, he would only reach the region in italy and not the region at home.\n</para>\n\n<para>\n  For this reason, some regional <emphasis>clusters</emphasis> of repeaters are linked to a\n  so-called <quote>Cluster</quote>. These clusters provide <quote>normal</quote> talk groups for the \n  repeater within a region. These clusters are then also reachable from the outside. A list of \n  regional clusters and their associated talk group numbers can be obtained under \n  <link xlink:href=\"http://bm262.de/cluster/\">bm262.de/cluster/</link>.\n</para>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/intro/technicalbackground.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introTech\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Technical background</title>\n</info>\n\n<para>\n  In the previous sections, I tried to explain the basic concepts of the DMR operation. That is, the\n  repeater independent private and group calls. This section concerns the more technical \n  details of the DMR mode. In particular the <glossterm>Time Slot</glossterm> and <glossterm>Color \n  Code</glossterm>.\n</para>\n\n\n<section xml:id=\"introTimeSlot\"> \n<info><title>Time Slots</title></info>\n<para>\n  As mentioned before, DMR is a digital mode. The speech signal is first digitized and compressed \n  with a lossy compression codec. The latter is also called <glossterm>VOCODER</glossterm>. Modern \n  codecs are very efficient and allow to transfer two independent speech signals within a single \n  12.5kHz wide channel. This is exploited in DMR using a technique called \n  <glossterm>TDMA</glossterm>.\n</para>\n\n<informalfigure>\n  <mediaobject>\n    <imageobject><imagedata fileref=\"fig/timeslot.png\" width=\"7cm\" align=\"center\"/></imageobject>\n  </mediaobject>\n</informalfigure>\n\n<para>\n  <glossterm>TDMA</glossterm> means <quote>time-division media access</quote> and describes that two \n  independent calls can happen simultaneously on one physical channel. To achieve this, each call\n  is assigned a <glossterm>Time Slot</glossterm> (time slot 1 &amp; 2) and both are transmitting \n  and receiving only within their assigned time slot. These time slots are very short. For DMR they\n  are only 30ms long. This short time, however, is sufficient to transfer audio for at least 60ms.\n  Therefore, DMR allows for two independent and simultaneous calls on a single physical channel.\n</para>\n\n<para>\n  When time slot 1 or 2 <quote>happens</quote>, is determined by the repeater. The repeater defines\n  the beat. This also implies that time slots are irrelevant for the simplex operation. Thus you \n  can ignore the time slot settings when programming simplex channels. (see \n  <xref linkend=\"introCodeplug\"/>).\n</para>\n\n<note>\n  <para>\n    What happens on each time slot, is a convention defied by the repeater. A general suggestion is\n    that regional communication happens on the time slot 2 while trans-regional communication \n    should happen on time slot 1.\n  </para>\n</note>\n</section>\n\n\n<section xml:id=\"introColorCode\">\n<info><title>Color Codes</title></info>\n\n<para>\n  <glossterm>Color Code</glossterm>s are a technical tool to avoid conflicts between repeaters \n  operating on the same frequency. This usually happens in commercial applications of DMR. One \n  company usually gets only a small number of frequencies assigned. To cover the entire campus, \n  much more repeaters are needed than frequencies are available. Consequently, overlapping \n  repeater ranges on the same frequency will occur and two repeaters may receive the call of a \n  participant. Then, the color code allows the repeater to detect whether a call was intended \n  for it. Only if the color code of a call matches the color code of the repeater, the repeater\n  will react on that call. \n</para>\n\n<para>\n  This issue usually does not arise in amateur radio applications. We just have enough channels.\n  Hence the color code is usually set to 1. In very densely populated areas, however, overlapping \n  repeater ranges may still occur and different color codes might be used there.\n</para>\n\n<note>\n  <para>\n    To use a repeater, you not only need to know the input and output frequencies but also the \n    color code of the repeater.\n  </para>\n</note>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/intro/textmessages.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"introDataServices\" \n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Data services</title>\n</info>\n\n<para>\n  As DMR is a digital mode that transports digitalized speech, it is possible to transfer other\n  data too. Consequently, there are some other digital services provided with DMR. First, there \n  are text messages similar to the one provided by mobile phones. It is also possible to forward \n  the own GPS position to the <glossterm>APRS</glossterm> network.\n</para>\n\n\n<section xml:id=\"introSMS\">\n<info><title>Textmessages (SMS)</title></info>\n\n<para>\n  With this service, you can transfer short text messages to other participants (like a private call) \n  or to a talk group (like a group call). The latter is rather uncommon and should be avoided. In \n  principle a text messages works like a private call. If the destination is reachable, the text \n  message will be routed to it.\n</para>\n\n<para> \n  There are also service numbers (free of charge). If messages are sent to them, certain \n  information can be retrieved to forwarded to other networks (e.g. to the \n  <glossterm>DAPNET</glossterm>). In Germany (and other countries) there are:\n</para>\n\n<orderedlist>\n  <listitem>\n    <para>262993 -- GPS and weather</para>\n    <itemizedlist>\n      <listitem><para>Send <token>help</token> and you will receive a list of commands.</para></listitem>\n      <listitem><para>Send <token>wx</token> and you will receive the weather at the location of \n        the repeater you used.</para></listitem>\n      <listitem><para>Send <token>wx CITY</token> and you will receive the weather at the specified\n        city.</para></listitem>\n      <listitem><para>Send <token>gps</token> and the last GPS position is returned that you have \n        sent to the DMR network.</para></listitem>\n      <listitem><para>With <token>gps CALL</token> you can retrieve the last position sent by the \n        specified call.</para></listitem>\n      <listitem><para>Send <token>rssi</token> and you will receive a signal report from the \n        repeater.</para></listitem>\n    </itemizedlist>\n  </listitem>\n  <listitem>\n    <para>262994 -- Repeater information &amp; pager messages</para>\n    <itemizedlist>\n      <listitem><para>Send <token>rpt</token> to receive a list of static an dynamic subscribed \n      talk groups at the repeater. </para></listitem>\n      <listitem><para>Send <token>CALL MESSAGE</token> to send the given message to the given \n        destination call in the DAPNET. </para></listitem>\n    </itemizedlist>\n  </listitem>\n</orderedlist>\n</section>\n\n\n<section xml:id=\"introAPRS\">\n<info><title>Position reporting to the APRS network</title></info>\n<para>\n  As mentioned in the previous section, it is possible to report the own position via the \n  DMR network to the APRS network. This position can then be tracked at for example \n  <link xlink:href=\"https://aprs.fi\">aprs.fi</link>. To do that, a radio with a built-in \n  GPS receiver is required. But not even these devices are expensive anymore. Simple DMR \n  hand-helds with built-in GPS receivers are available for about 120€.\n</para>\n<para>\n  Beside the text message services, it is also possible to transmit the position to the DMR \n  network using a special service number 262999. How the radio is configured to do that, \n  depends heavily on the device being used. <application>qdmr</application> helps with the \n  configuration by providing common means for these settings across all supported radios.\n</para>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/manual.code-workspace",
    "content": "{\n\t\"folders\": [\n\t\t{\n\t\t\t\"path\": \".\"\n\t\t}\n\t],\n\t\"settings\": {}\n}"
  },
  {
    "path": "doc/manual/manual.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<book xmlns=\"http://docbook.org/ns/docbook\" \n      xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n      xmlns:xi=\"http://www.w3.org/2001/XInclude\"\n      version=\"5.1\" xml:lang=\"en\">\n\n  <info>\n    <title>qdmr - User Manual</title>\n    <subtitle>A universal codeplug programming tool for Linux and MacOS X.</subtitle>\n    \n    <xi:include href=\"meta/authors.xml\"/>    \n\n    <date>April 1st, 2026</date>\n    \n    <copyright>\n      <year>2022-2026</year>\n      <holder>Hannes Matuschek</holder>\n    </copyright>\n    \n    <releaseinfo>\n      <para>\n        This document covers <application>qdmr</application> version 0.14.1\n      </para>\n    </releaseinfo>\n    \n    <legalnotice>\n      <para>\n        This document is released under the conditions of the \n        <link xlink:href=\"https://creativecommons.org/licenses/by-sa/3.0/\">Creative Commons\n          Attribution-ShareAlike 3.0</link>.\n      </para>\n    </legalnotice>\n\n    <xi:include  href=\"meta/abstract.xml\"/> \n  </info>\n  \n  <xi:include  href=\"meta/preface.xml\"/>\n  \n  <xi:include  href=\"intro/introduction.xml\"/> \n\n  <xi:include  href=\"gui/gui.xml\"/> \n  \n  <xi:include  href=\"codeplug/format.xml\"/>\n  <xi:include  href=\"codeplug/extensions.xml\"/>\n  <xi:include  href=\"codeplug/commercial/extensions.xml\"/>\n\n  <xi:include  href=\"conf/format.xml\"/>\n\n  <xi:include href=\"cli/commandline.xml\"/>\n\n  <xi:include href=\"reveng/reverseengineering.xml\"/>\n  \n  <xi:include href=\"meta/glossary.xml\"/>\n\n  <index/>\n</book>\n"
  },
  {
    "path": "doc/manual/manual_fo.debian.xsl",
    "content": "<?xml version=\"1.0\"?>\n\n<xsl:stylesheet version=\"1.0\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n\n  <xsl:import href=\"file:///usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl\"/>\n\n  <xsl:attribute-set name=\"monospace.verbatim.properties\">\n    <xsl:attribute name=\"font-size\">7pt</xsl:attribute>\n    <xsl:attribute name=\"keep-together.within-column\">always</xsl:attribute>\n  </xsl:attribute-set>\n\n  <xsl:param name=\"paper.type\" select=\"'A4'\"/>\n  <xsl:param name=\"double.sided\" select=\"1\"/>\n  <xsl:param name=\"highlight.source\" select=\"1\"/>\n  <xsl:param name=\"generate.toc\">\n    appendix  nop\n    article   toc,title\n    book      toc,title\n    chapter   toc\n    part      nop\n    preface   nop\n    qandadiv  nop\n    qandaset  nop\n    reference toc,title\n    section   nop\n    set       toc\n  </xsl:param>\n\n  <xsl:template match=\"token\">\n    <xsl:call-template name=\"inline.monoseq\"/>\n  </xsl:template>\n</xsl:stylesheet>\n"
  },
  {
    "path": "doc/manual/meta/abstract.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<abstract xmlns=\"http://docbook.org/ns/docbook\"\n          xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n  <title>About This Document</title>\n  <!--<informalfigure pgwide=\"0\">\n    <mediaobject>\n      <imageobject><imagedata fileref=\"fig/qdmr-channels.png\" width=\"15cm\" align=\"center\"/></imageobject>\n      <textobject>Screen-shot of qdmr in action.</textobject>\n    </mediaobject>\n  </informalfigure>//-->\n  <para>\n    This document contains a brief manual for the <acronym>qdmr</acronym> graphical user interface \n    (<acronym>GUI</acronym>) application to program <acronym>DMR</acronym> radios. The aim of this \n    application is to provide a simple to use, platform and vendor independent and well documented \n    codeplug programming software (<acronym>CPS</acronym>) for popular <acronym>DMR</acronym> radios.\n  </para>\n  <para>\n    This document is available in several formats: \n  </para>\n  <itemizedlist>\n    <listitem><para><link xlink:href=\"https://static.dm3mat.de/qdmr/manual/\">HTML</link>,</para></listitem>\n    <listitem><para><link xlink:href=\"https://static.dm3mat.de/qdmr/manual.pdf\">PDF</link> and </para></listitem>\n    <listitem><para><link xlink:href=\"https://static.dm3mat.de/qdmr/manual.epub\">EPUB 3</link>.</para></listitem>\n  </itemizedlist>\n</abstract>"
  },
  {
    "path": "doc/manual/meta/authors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<authorgroup xmlns=\"http://docbook.org/ns/docbook\" \n             xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n             xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<author>\n  <personname>\n    <firstname>Hannes</firstname>\n    <surname>Matuschek</surname>\n  </personname>\n  <email>dm3mat@darc.de</email>\n  <affiliation>\n    <shortaffil>OV Y07</shortaffil>\n    <orgname>OV Y07, DARC</orgname>\n  </affiliation>\n</author>  \n\n</authorgroup>"
  },
  {
    "path": "doc/manual/meta/glossary.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<glossary xml:id=\"glossary\"\n          xmlns=\"http://docbook.org/ns/docbook\" \n          xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n  <title>Glossary</title>\n  \n  <glossentry>\n    <glossterm>Admit Criterion</glossterm>\n    <glossdef>\n      <para>\n        The admit criterion specifies a criterion under which transmissions on a channel are \n        allowed. These criteria can be defined for both, analog and DMR channels. Irrespective of\n        the channel type, the admit criteria <quote>always</quote> and <quote>channel free</quote>\n        can be selected. The former simply always allows to transmit. The former requires the \n        channel to be free. \n      </para>\n      <para>\n        For analog channels, usually there is also the criterion <quote>tone</quote>. This \n        criterion would only allow transmissions if the matching CTCSS tone or DCS code has been\n        received. For analog repeater operation, the criterion <quote>channel free</quote> should\n        not be used as this would prevent any transmissions while the repeater is active.\n      </para>\n      <para>\n        For digital channels, usually there is also the criterion <quote>color code</quote>. This\n        criterion would only allow transmissions if the matching color code has been received. To \n        this end, this criterion is similar to the <quote>tone</quote> criterion for analog \n        channels.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>All Call</glossterm>\n    <glossdef>\n      <para>\n        An all-call is a special type of an DMR call to a special number (<emphasis>16777215</emphasis>). \n        All radios are supposed to receive this call irrespective of their particular configuration. This\n        call type is usually used in emergency situations or on simplex channels.\n      </para>\n      <para>\n        Unfortunately, the local talk group (number 9) is frequently used on simplex channel although only \n        those radios will receive calls to this talk group if it is in the group list assigned to the \n        simplex channel. To be on the safe side, use the all-call for simplex calls.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Automatic Packet Reporting System</glossterm> <acronym>APRS</acronym>\n    <glossdef>\n      <para>\n        The Automatic Packet Reporting System is a protocol that uses single broadcast frequencies \n        to transmit small amounts of information though a network of repeaters. Usually but not \n        limited to position reports. This allows other services (e.g.,\n        <link xlink:href=\"https://aprs.fi\">aprs.fi</link> to display that information on a map. The\n        usual APRS frequencies are 144.390 MHz (North America), 144.800 MHz (Europe) and \n        145.175 MHz (Australia).\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Codeplug</glossterm>\n    <glossdef>\n      <para>\n        The term <quote>codeplug</quote> is rather loosely defined. Usually it refers to the binary\n        representation of the configuration of a radio that is written to the radio to configure it.\n        It contains all contacts, channels, zones, settings, etc that form the configuration of the\n        radio.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Color Code</glossterm>\n    <glossdef>\n      <para>\n        A color code is some additional information attached to each DMR call. It is used to prevent \n        interference between repeaters with overlapping ranges that operate at the same frequencies. \n        In amateur radio, this may only happen in very densely populated areas. It is much more \n        frequent in commercial applications, where a single company may got only a few frequencies \n        assigned but needs much more repeaters to cover a campus reliably.\n      </para>\n      <para>\n        The color code is a number between 0 and 16. A radio or repeater may only react to calls with\n        matching color codes. Hence beside the actual input and output frequencies, the color code of\n        a repeater must be known to be able to access it.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry xml:id=\"glCTCSS\">\n    <glossterm>Continuous Tone Coded Subaudio Squelch</glossterm>\n    <acronym>CTCSS</acronym>\n    <glossdef>\n      <para>\n        Means to control the squelch of a radio or to open a repeater, that does not rely on the \n        strength of a carrier. Instead a tone is transmitted along with the audio, which is usually\n        filtered out (e.g., less than 300Hz) by the receiver. There are a set of common CTCSS tone\n        frequencies, but may radios allow for specifying arbitrary sub-tone frequencies. \n      </para>\n    </glossdef>\n    <glossseealso otherterm=\"glDCS\">DCS</glossseealso>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Decentralized Amateur Paging Network</glossterm>\n    <acronym>DAPNET</acronym>\n    <glossdef>\n      <para>\n        <acronym>DAPNET</acronym>, short for Decentralized Amateur Paging Network does exactly what\n        it says. It is a network of transmitters that transmit pager messages in the UHF band, \n        usually at 439.9875 MHz. As the old pager operated at a frequency near by, they can be \n        modified to operate at that frequency and can therefore be used in amateur radio. \n      </para>\n      <para>\n        The DAPNET is particularly popular with emergency operators as it allows for an convenient\n        and reliable multicast notification.\n      </para>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry xml:id=\"glDCDM\">\n    <glossterm>DCDM</glossterm>\n    <glossdef>\n      <para>\n        Stands for dual capacity direct mode. This allows for two independent connections on a \n        single simplex channel by TDMA. Lacking a single central repeater, one of the \n        participants must act as the so-called <emphasis>leader</emphasis>. This radio then defines\n        the clock and thus time slot 1 and 2. All remaining participants must act as so-called \n        <emphasis>follower</emphasis>. They must synchronize to the clock of the \n        <emphasis>leader</emphasis>. This obviously requires that all participants can reach the \n        <emphasis>leader</emphasis> directly.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry xml:id=\"glDCS\">\n    <glossterm>Digital Coded Squelch</glossterm>\n    <acronym>DCS</acronym>\n    <glossdef>\n      <para>\n        Similar to the CTCSS tones, the DCM allows to open the squelch or repeater by transmitting \n        sub-tones along with the audio. These tones are usually filtered out by the receiver (e.g.,\n        below 300Hz). In contrast to CTCSS tones, DCS uses these tones to repeatedly transmit a \n        digital code.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>EchoLink</glossterm>\n    <glossdef>\n      <para>\n        EchoLink is a network of analog FM repeaters that allows to link repeaters within this \n        network temporarily. That is, two FM repeaters that are linked via EchoLink behave like \n        a single repeater irrespective of their location. This network also allows to access\n        the repeaters directly though the internet. This is particularly helpful if one cannot \n        reach any repeater.  \n      </para>  \n    </glossdef>\n  </glossentry>\n\n  <glossentry xml:id=\"glGroupCall\">\n    <glossterm>Group Call</glossterm>\n    <glossdef>\n      <para>\n        A group call is simply a call to a talk group. That is, not to a single participant but \n        rather to a group of participants. Every participant that has this talk group in their \n        group list can receive this call. See also <xref linkend=\"glGroupList\">Group List</xref>\n        and <xref linkend=\"glTalkGroup\">Talk Group</xref>.\n      </para>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry xml:id=\"glGroupList\">\n    <glossterm>Group List</glossterm>\n    <glossdef>\n      <para>\n        A group list is a simple list of talk groups. A group list is then assigned to a channel\n        to specify which group calls to receive on that channel. If a talk group is not listed in \n        the group list of a channel, the radio will ignore calls to this talk group on that \n        channel.\n      </para>\n      <para>\n        The DMR network cannot know which talk groups you are interested in. You have to tell your\n        radio using group lists.\n      </para>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry xml:id=\"glHangTime\">\n    <glossterm>Hang Time</glossterm>\n    <glossdef>\n      <para>\n        The <emphasis>hang time</emphasis> specifies the time period, a DMR call remains \n        <quote>active</quote> after it ended. For this time period, the call will replace the\n        default transmit contact on that channel. This allows to directly answer a call received on\n        a channel, even if that call is not the default transmit contact on the channel.  \n      </para>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry>\n    <glossterm>Maidenhead Locator</glossterm>\n    <glossdef>\n      <para>\n        The Maidenhead locator, also known as <quote>QTH locator</quote> or \n        <quote>IARU locator</quote> is a means for transferring a geo-location over radio. Using a\n        so-called grid-square system.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry xml:id=\"glLoneWorker\">\n    <glossterm>Lone Worker</glossterm>\n    <glossdef>\n      <para>\n        <quote>Lone worker</quote> refers to a feature of many DMR radios used in some commercial \n        settings, where a participant in the network needs to report regularly to some other \n        station. This feature is certainly not very useful for the HAM radio context.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Private Call</glossterm>\n    <glossdef>\n      <para>\n        A DMR call to an individual participant. Usually, radios do not receive private calls not\n        addressed to the radio. This therefore can be used to talk to a specific HAM over a \n        repeater without disturbing other participants. The repeater however, will be blocked \n        during that time.\n      </para>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry>\n    <glossterm>Revert Channel</glossterm>\n    <glossdef>\n      <para>\n        A revert channel is a designated channel the radio resorts to, to transfer some information\n        or the radio switches to, when the PTT is pressed. This is usually used in the context of \n        APRS (i.e., the channel to switch to send the position) or scan lists (i.e., the channel\n        the radio will switch to, when the PTT is pressed during the scan).\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Roaming Channel</glossterm>\n    <glossdef>\n      <para>\n        Specifies which settings of the current channel needs to be overridden to stay in contact \n        with a particular talk group. These settings are usually those, specific for a repeater. \n        That is, receive and transmit frequency and color code. The time-slot might also be \n        overridden.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Roaming Zone</glossterm>\n    <glossdef>\n      <para>\n        A collection of <glossterm>roaming channels</glossterm> usually sharing the same transmit \n        contact. When roaming is enabled (and supported by the radio) this list of channels is \n        scanned for a reachable channel once the current repeater gets out-of-range. This way, one \n        can stay in contact with a particular talk group when being mobile.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Secondary Station Identifier</glossterm>\n    <acronym>SSID</acronym>\n    <glossdef>\n      <para>AX.25 addresses (and thus APRS) consists of a call sign and a so-called SSID (0-15). \n        This number can be used as a replacement for the lack of ports in AX.25 or to provide \n        some additional information about the station.</para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry xml:id=\"glTalkaround\">\n    <glossterm>Talkaround</glossterm>\n  </glossentry>\n\n  <glossentry xml:id=\"glTalkGroup\">\n    <glossterm>Talk Group</glossterm>\n    <acronym>TG</acronym>\n    <glossdef>\n      <para>\n        The term <quote>Talk Group</quote> refers to a DMR ID, which represents a virtual contact.\n        DMR repeater may subscribe to one or more talk groups. Every call to these talk groups \n        are then transmitted by those repeaters, that are subscribed to these talk groups. \n        Usually, it is possible to subscribe a repeater to any talk group temporarily by simply\n        starting a transmission to this talk group on a repeater channel. For some time period \n        after the initial transmission, the repeater will transmit all calls to this now \n        subscribed talk group. The subscription gets renewed with every further call the talk \n        group.\n      </para>\n      <glossseealso otherterm=\"glGroupCall\">Group Call</glossseealso>\n    </glossdef>\n  </glossentry>\n  \n  <glossentry xml:id=\"TDMA\">\n    <glossterm>Time-Division Multiple Access</glossterm>\n    <acronym>TDMA</acronym>\n    <glosssee otherterm=\"glTimeSlot\"/>\n  </glossentry>\n\n  <glossentry xml:id=\"glTimeSlot\">\n    <glossterm>Time Slot</glossterm>\n    <glossdef>\n      <para>\n        Due to the lossy compression of audio data, it is possible to send more than twice the \n        amount of audio data through a single 12.5kHz wide channel than needed. This allows to \n        provide two independent audio streams within a single physical channel. \n      </para>\n      <para>\n        There are basically two ways to separate these two audio streams. Either by frequency, \n        effectively splitting the single 12.5kHz channel into two 6.25kHz wide ones. Alternatively,\n        one can split the channel in <quote>time</quote>. That is, the band width remains 12.5kHz\n        but each of the two simultaneous participants sends one after the other. One in time slot 1\n        and the other in time slot 2. This is called TDMA and DMR uses this technique to provide \n        two independent audio and data streams within a single physical channel.\n      </para>\n      <para>\n        For this TDMA to work, one participant needs to define the <quote>clock</quote> to specify\n        when time slot 1 or 2 happen. This role is usually taken by the repeater but on direct \n        simplex connections, one of the participants might provide that clock. This is then called\n        DCDM (Dual-Capacity Direct Mode). This mode, however, makes little sense in amateur radio\n        context.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Transmit Contact</glossterm>\n    <glossdef>\n      <para>\n        A transmit or default contact can be specified for each DMR channel. Some radios actually \n        require that each channel has an transmit contact. This contact can be any contact you want\n        to call whenever the PTT is pressed on that channel. \n      </para>\n      <para>\n        It is, however, possible to answer any received call directly by pressing the PTT button \n        within the so-called hang time, although the call may not match the transmit contact. \n        These hang times can be set within the radio settings, usually for private and group calls\n        separately.\n      </para>\n      <para>\n        It is also possible to call any contact by selecting it from the contact list or by \n        entering the contacts DMR ID into the keypad.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Transmit Timeout</glossterm> <acronym>TOT</acronym>\n    <glossdef>\n      <para>\n        Specifies the time of continuous transmission, after which the transmission is interrupted \n        automatically. This prevents the accidental blocking of a repeater or talk group by \n        transmitting continuously. \n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Scan List</glossterm>\n    <glossdef>\n      <para>\n        A scan list is just a list of analog and digital channels that one wants to scan. A scan\n        list can be associated with each channel. This scan list is then used whenever a scan is\n        started on that channel.\n      </para>\n    </glossdef>\n  </glossentry>\n\n  <glossentry>\n    <glossterm>Zone</glossterm>\n    <glossdef>\n      <para>\n        As there are usually several channels defined per repeater (at least two), the number of \n        defined channels grows rapidly. To organize this large number of channels, zones are used.\n        These zones usually collect all relevant channels for a region. That is all (analog and \n        digital) channels associated with the local repeaters. The relevant zone is then selected \n        via the keypad on the radio and only those channels in that zone are then accessible. \n      </para>\n      <note>\n        <para>\n          A defined channel that is not member of any zone is usually not accessible in the radio.\n        </para>\n      </note>\n      <para>\n        Some radios allow to specify a different zones for each VFO. Others set a zone globally. \n        In the latter case, the zone consists of two channel lists. One for each VFO.\n      </para>\n    </glossdef>\n  </glossentry>\n</glossary>"
  },
  {
    "path": "doc/manual/meta/preface.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<preface xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Foreword</title>\n</info>\n\n<simplesect>\n<para>\n  Before you start programming your <acronym>DMR</acronym> radio, \n  <link xlink:href=\"https://www.dmrfordummies.com/\">get familiar</link> with this digital \n  mode. I have written a brief introduction into <acronym>DMR</acronym> too. You will find it \n  in <xref linkend=\"introduction\"/> (also available \n  <link xlink:href=\"https://dm3mat.darc.de/qdmr/manual/script_de.pdf\">in german</link>). If you \n  are already familiar with <acronym>DMR</acronym>, skip this chapter and head directly to \n  <xref linkend=\"gui\"/>, where I describe how <application>qdmr</application> is used. If you are\n  a fan of command line tools, have a look at <xref linkend=\"commandLine\"/>.\n</para>\n\n<para>\n  Digital mobile radio (DMR) was not invented for the use in amateur radio. It was rather designed\n  to be a radio standard for commercial applications in large companies (e.g., airports etc.).\n  Therefore many features of this standard are of no use for ham radio or are even illegal\n  (e.g., encryption). This complexity of the standard makes the programming of the radios\n  cumbersome.\n</para>\n\n<para>\n  Moreover, the resulting configuration (codeplug) is highly device-dependent. These\n  codeplugs cannot be shared between different devices let alone between different vendors. For\n  commercial applications, this is not a big problem, as a company will likely buy identical\n  radios at once from one company. Thus codeplugs can be shared between all radios.\n</para>\n\n<para>\n  For ham radio applications, this incompatibility is a real issue. Since assembling a decent\n  codeplug for one region is hard enough, doing the same work all over again for different models\n  of different vendors is not manageable.\n</para>\n\n<para>\n  Finally, the typical code-plug programming software (CPS), particularly those for cheap Chinese\n  DMR radios, is by no means user-friendly and seldom documented completely. Many options are\n  named cryptic and it is not possible to identify which options are necessary for basic DMR\n  operation. Moreover, the CPSs provided by the vendors usually only run under Windows.\n</para>\n\n<para>\n  The aim of the <application>qdmr</application> project is to overcome these shortcomings of typical CPSs. It has a\n  reduced feature set only supporting those options necessary of amateur radio usage. It tries\n  to be user-friendly by finding repeaters nearby and importing their input and output\n  frequencies. Moreover, it stores the final codeplug not in a device-specific binary format but\n  in a human-readable text format that is device independent and can therefore be shared across\n  multiple device and even across vendors.\n</para>\n\n<para>\n  Finally, I try to keep the application well documented. This manual is part of this effort. It\n  is a guide on how a codeplug is set up using <application>qdmr</application>.\n</para>\n</simplesect>\n\n</preface>\n"
  },
  {
    "path": "doc/manual/reveng/codeplug.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<section xml:id=\"reverseEngineeringCodeplug\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info><title>Reverse engineering of the code plug format</title></info>\n\n<para>\n  Once the communication protocol is known, it is time to focus on the code plug format. Depending \n  on the code plug complexity, this can be a very cumbersome task. Moreover, depending on the \n  device, it can be pretty hard to access the binary representation of the configuration (code plug)\n  that is actually written onto the device. In general, one cannot expect, that the binary file, \n  created by the manufacturer, does relate to the binary code plug written onto the device at all. \n</para>\n<para>\n  To figure the relationship out, between the code plug file and the actual code plug written onto \n  the device, the code plug must first be extracted from the Wireshark capture. As we already know \n  the protocol, this can be done easily using Python and pyshark. When doing so, be careful \n  with the addresses, data is written to or read from. The binary code plug might not be written \n  sequentially and with gaps. A best practice is to extract the data read or written along with \n  its addresses and sort it with respect to the address. Then dump everything as a hex-dump. \n</para>\n<para>\n  Once the code plug is extracted from the Wireshark capture, compare it to the corresponding \n  code plug file created by the CPS. If you are lucky (usually for very cheap DMR radios), the CPS\n  code plug file is simply the binary code plug written to the device (e.g., for GD77, RD5R). Then \n  it is very easy to reverse engineer the code plug format, as you can study changes to the CPS code\n  plug file. \n</para>\n<para>\n  If they are not identical, you will need to somehow extract the binary code plug written to the \n  device from the manufacturer CPS. If the protocol is based on Serial-over-USB, you may be able\n  to write an emulator for the device, knowing the communication protocol. This then allows to \n  trick the CPS to talk to the emulator instead of the device and dump the code plug. If not, you\n  are likely stuck with writing a lot of code plugs to the real device and extracting the code plug\n  from the Wireshark captures. This can be considered the worst-case scenario.\n</para>\n\n<section>\n<info><title>Differential analysis</title></info>\n<para>\n  Irrespective on how you gain access to the binary code plug written onto the device, the analysis\n  of this code plug is always the same. This is called differential analysis. The idea is, to change\n  a single option in the CPS and compare the resulting binary code plugs bit by bit. Ideally, \n  only a single place in the binary code plug will change too and this change then reflects the \n  encoding of the particular setting touched. This is then repeated for all possible settings in\n  the CPS until the entire code plug structure is known. \n</para>\n<note>\n  <para>\n    There will be some bits and bytes in the code plug that will never changed. These are usually\n    reserved bits/bytes. There may also be some undocumented and hidden options in the code plug,\n    that cannot be set via the (normal) CPS. \n  </para>\n</note>\n<para>\n  Before you can start reverse engineering the code plug, you need to create a <quote>base</quote>\n  code plug. That is, one small simple code plug, that touches every feature of the radio. It should \n  not be overly complex with hundreds of channels, but should contain everything the radio provides.\n  For example, it should contain the APRS settings, if the radio has GPS or a roaming zone if the \n  radio supports roaming. You should also create two of every kind. That is, at least two channels, \n  contacts, group lists, zones and scan lists. Otherwise, it will be hard to figure the index \n  scheme out.\n</para>\n<para>\n  Once you created the base-code plug, get its binary representation (e.g., from file, emulation or \n  Wireshark capture). Change something simple. For example the name of the first contact. Then get \n  the binary representation of this modified code plug and compare the two binary code plugs.  \n</para>\n<para>\n  As an example, consider the following difference between two hex-dumps of the binary code plugs \n  for a BTech DMR-6X2UV Pro. Here, the name of the first contact was changed. \n</para>\n<example>\n  <title>Difference between two binary code plugs for a DMR-6X2UV Pro. Only the name of the first \n    contact was changed.</title>\n  <programlisting><![CDATA[< 02680000 : 01 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......\n< 02680010 : 00 00 00 55 6e 6b 6e 6f 77 6e 00 00 00 00 00 00 | ...Unknown......\n> 02680000 : 01 56 65 72 79 20 6c 6f 6e 67 20 6e 61 6d 65 2e | .Very long name.\n> 02680010 : 2e 00 00 55 6e 6b 6e 6f 77 6e 00 00 00 00 00 00 | ...Unknown......]]></programlisting>\n</example>\n<para>\n  This difference already shows some very important information about the code plug encoding. First,\n  the list of contacts appears to start at address 2680000h. This is very important for the coarse \n  structure of the binary code plug. We also learn about the structure of the encoded contacts, that\n  the name starts at offset 01h. So, the first byte is still unknown. The length of the name is \n  limited to 16 bytes and is terminated/padded with 0-bytes. However, we still don't know how large\n  the encoded contact element in the code plug actually is. \n</para>\n<para>\n  To figure out the size of the contact element, one can use the fact, that all elements very \n  likely have identical sizes. Hence the offset from one name to the name of the next contact will\n  be the size of the contact element. We already know, that the contact list starts at the address \n  2680000h. Hence, we can look in the code plug directly at that address.\n</para>\n<example>\n  <title>Codeplug hex-dump at the contact-list address.</title>\n  <programlisting><![CDATA[02680000 : 01 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......\n02680010 : 00 00 00 55 6e 6b 6e 6f 77 6e 00 00 00 00 00 00 | ...Unknown......\n02680020 : 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 | ................\n02680030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680060 : 00 00 00 00 01 43 6f 6e 74 61 63 74 20 32 00 00 | .....Contact 2..\n02680070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680080 : 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 | ................\n02680090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................]]></programlisting>\n</example>\n</section>\n<para>\n  The offset between the two names <quote>Contact 1</quote> and <quote>Contact 2</quote> is exactly \n  100 bytes (64h). With this, we know the exact size of the contact element. However, wo only know\n  what 16 of these 100 bytes mean. So there is a lot to figure out. The differential analysis, \n  however, makes it quiet easy. First we may change the call type of the first contact. From \n  <quote>group call</quote> to <quote>private call</quote> and study the differences in the \n  code plug. \n</para>\n<example>\n  <title></title>\n  <programlisting><![CDATA[< 02680000 : 01 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......\n> 02680000 : 00 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......]]></programlisting>\n</example>\n<para>\n  We see, that only one byte has changed. The one at offset 0. It changed from 01h to 00h. We may \n  conclude that this byte encodes the contact type, where 00h means private call and 01h means \n  group call. Analogously, we find out that 03h means all-call. \n</para>\n<para>\n  The next step is to find the encoding and offset of the contact DMR ID. To find it, we reload the\n  base code plug and change the DMR ID of the first contact from <quote>1</quote> to something more \n  complex. E.g., to <quote>1234567</quote> and compare the binary  .\n</para>\n<example>\n  <title></title>\n  <programlisting><![CDATA[< 02680020 : 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 | ................\n> 02680020 : 00 00 00 01 23 45 67 00 00 00 00 00 00 00 00 00 | ....#Eg.........]]></programlisting>\n</example>\n<para>\n  Here we find some changes at the offset 23h. The content changed from 00000001h to 01234567h. \n  This is somewhat weird, as the binary representation of 1234567 in hex is actually 12d687h. \n  However, for DMR code plugs, it is quiet common to store integers in so-called BCD. Here each \n  digit is stored in 4bits. This has the consequence, that the decimal digits are readable in \n  hex. But this storage is inefficient, as the DMR ID is actually a 24bit number, that can be \n  stored in 3 bytes. Its BCD encoding requires 4 bytes. \n</para>\n<para>\n  The last setting for contacts remaining in the CPS is the <quote>call-alert</quote>. This setting \n  is only valid for private calls. Changing the contact 1 from group to private call, changing \n  the call alert from <quote>None</quote> to <quote>Ring</quote> and comparing the code plugs, one \n  gets:\n</para>\n<example>\n  <title></title>\n  <programlisting><![CDATA[< 02680000 : 01 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......\n> 02680000 : 00 43 6f 6e 74 61 63 74 20 31 00 00 00 00 00 00 | .Contact 1......\n< 02680020 : 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 | ................\n> 02680020 : 00 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 | ................]]></programlisting>\n</example>\n<para>\n  Of cause, we find two changes. The change of the first byte, indicating the change from group to \n  private call, as well as one byte changing at offset 27h from 00h to 01h. This is the only \n  unexplained change and thus must reflect the alert-type setting. Consequently, the alert type \n  <quote>None</quote> is represented by 00h and <quote>Ring</quote> by 01h. Analogously, we find \n  that alert-type <quote>Online</quote> is encoded as 02h. \n</para>\n<para>\n  Obviously, we still don't know the majority of the 100 bytes of the contact element. Lets have a\n  look at the structure of the contact element.\n</para>\n<programlisting><![CDATA[\n     0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 |TYP| Name, 16 x ASCII, 0-pad                                ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10     |Pad| Unknown 16 bytes, ASCII?                           ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20  ...    |Pad|DMR ID, BCD, BE|Rng| Unused, filled with 00h    ...\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60                 |\n   +---+---+---+---+]]>\n</programlisting>\n<para>\n  There are some additional Pad bytes (00h) that terminate ASCII strings. This is nothing, we found\n  by the differential analysis, but it is some common scheme, found in many binary code plugs. \n  Obviously, the firmware developer did not knew the <function>strnlen</function> C function.\n</para>\n\n<para>\n  The remaining bytes of the contact element appears to be empty, unused space. This is also quiet\n  frequent in code plugs. They contain a lot of unused bytes that might be reserved for future uses.\n  This allows the firmware developer to extend the code plug, while maintaining backward \n  compatibility with older versions of the CPS. \n</para>\n\n<para>\n  Having reverse engineered the contacts, does not mean that you are done. There are a lot more \n  elements to decipher. Did I mentioned that it is a lot of work? But the general scheme of things \n  remain the same. Change a single setting in the CPS and compare the resulting binary code plugs. \n  Document your findings. Step by step, you will figure out the entire code-plug structure. \n</para>\n</section>"
  },
  {
    "path": "doc/manual/reveng/protocol.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<section xml:id=\"reverseEngineeringProtocol\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info><title>Reverse engineering communication protocols</title></info>\n\n<para>\n  There is no one ultimate way to successfully reverse engineer a communication protocol. But some\n  methods are very common. First, one needs to get some captures of the communication between the \n  manufacturer codeplug programming software (<acronym>CPS</acronym>) and the radio of interest. \n  The majority of these radios will communicate via USB. Hence, some means are needed to capture\n  USB traffic.\n</para>\n<para>\n  There is an application, called <application>Wireshark</application> \n  (<ulink url=\"https://www.wireshark.org/\"/>). This application is usually used to capture and \n  analyze network traffic. It can, however, also capture traffic from USB. How this is done, \n  depends a bit on the operating system you are using. If you want to capture and analyze the \n  communication under <trademark>Windows</trademark>, read the instructions at \n  <ulink url=\"https://wiki.wireshark.org/CaptureSetup/USB\"/>. This makes sense, as you need a \n  windows installation, to run the manufacturer CPS. \n</para>\n<para>\n  I personally prefer to work under Linux, to this end, I've installed Windows in a virtual \n  machine, but capture the USB traffic under the host Linux system. To do that, the \n  <command>usbmon</command> kernel module must be loaded. If loaded, Wireshark will show USB\n  as a capture source.\n</para>\n<para>\n  Once, capturing USB traffic works, connect the radio to the host. You may need to allow the \n  guest system to have access to the USB device. Then fire up the manufacturer CPS, start \n  capturing in Wireshark and start a codeplug read. Stop capturing once the read is complete.\n  Save the captured data and restart the capturing. Then write the codeplug back to the device,\n  stop the capture and save it in another file.\n</para>\n<para>\n  These files now contain all packets send and received by the PC over USB. In a next step, \n  the packets must be filtered and inspected. To do that, I personally prefer to write short\n  Python scripts. The <productname>pyshark</productname> package provides means to read \n  packages from the capture file and access their content. This allows to filter those\n  packages to and from the device, that contain the actual payload to and from the device.\n</para>\n<para>\n  Please note, that may radios may misuse some already existing protocols over USB. Some \n  Radioddity and Baofeng devices use the HID specification to transfer data to and from \n  the device. Others may use the DFU specification (TyT, Retevis), mass storage (CSi) or \n  simply serial-over-USB (AnyTone). Irrespective of the underlying specification used to\n  send data to the device, the payload of these packages must be extracted.\n</para>\n<para>\n  To do that, use Wireshark to inspect these packages by hand. Search for a packet, that \n  was definitely send to or by the device and note the USB (URB) device address. This is \n  the best way to filter all packets to and from the device.\n</para>\n<example>\n  <title>Some simple example to filter packages by device address and destination/source.</title>\n  <programlisting language=\"python\"><![CDATA[\nimport pyshark\n\npackages = pyshark.FileCapture(FILENAME, include_raw=True, use_json=True)\ndevice_address = '9' # just an example\n\nfor package in packages:\n  if device_address != package.usb.device_address:\n    continue\n  if \"host\" == package.usb.src:\n    # From host to device.\n  else if \"host\" == package.usb.dst:\n    # From device to host.]]>\n  </programlisting>\n</example>\n<para>\n  This example shows how to filter only those packets to and from a specific device and also \n  dispatch depending on the destination and source of the packet. That is, if the packet was \n  send by the host or the device. \n</para>\n<para>\n  In a next step, the captured payload must be inspected. If the packet contains any additional\n  payload data, the <varname>usb.data_length</varname> field will be set. Then, there is a field\n  called <varname>usb.capdata_raw</varname>. Unfortunately, that field name contains a dot, so you\n  need to access it differently. That is\n</para>\n<programlisting language=\"python\"><![CDATA[\n  # [...] \n  if int(package.usb.data_length):\n    data = package[\"usb.capdata_raw\"].value\n  # [...]]]>\n</programlisting>\n<para>\n  The extracted data is hex string. Now comes the difficult part. Staring at these packets and \n  making sense of them. Usually, the communication is performed in a strict command-response \n  structure. That is, the host sends a request packet and the device responses with a single \n  packet. This eases the analysis a lot, as the communication is always initiated by the host and\n  the association between the request and the response packets are trivial.\n</para>\n\n<section>\n<title>Identifying the communication structure</title>\n<para>\n  A typical communication with the device is performed in three steps: First, the \n  radio is identified and brought into a programming mode. Several packets might be needed to \n  perform this task. In that state, the radio usually displays some message on the screen or\n  blinks an LED.\n</para>\n<para>\n  The second step consists of the actual codeplug transfer. There, a large \n  amount of packets are send. This step is usually easy to find within the captured packets, as a\n  large number of equally sized packets are exchanged. Sometimes, a relatively large amount of \n  data is read or written at once. This can be spotted easily, as basic control commands of \n  the first step are usually pretty short.\n</para>\n<para>\n  Frequently, these packets contain an address field or sequence number used to specify where the \n  codeplug data chunk is written to or read from. These fields are of upmost importance and need to\n  be identified. They are, however, easy to spot, as this field is likely constantly increasing, \n  with a fixed increment from packet to packet. \n</para>\n<para>\n  The final step usually is to reboot the radio or leaving the programming mode. This is commonly \n  performed by a single packet. Sometimes, the device reboots immediately and no response to the \n  command is received by the host. \n</para>\n<para>\n  Once the protocol structure is identified, that is, the purpose of the single packets is known, \n  the actual work of identifying the packet structure can start. \n</para>\n</section>\n\n<section>\n<title>Identifying the packet format</title>\n<para>\n  The actual packet format will be much harder to reverse engineer. I cannot give any general \n  suggestions on how to figure out the meaning of each byte in a packet. However, I can give you \n  some examples. This might help in reverse engineering new protocols as the structure of the \n  packets will be different, but the concepts remain the same. For example, read and write commands\n  must somehow specify an address to read from and write to as well as the amount of data to read \n  or write. This might be helpful\n</para>\n<para>\n  Let us inspect some packets send and received from an AnyTone device.\n</para>\n<example xml:id=\"revEngD578\">\n  <title>Capture of a codeplug read from an AnyTone AT-D578UV</title>\n  <programlisting><![CDATA[> 50 52 4f 47 52 41 4d                            | PROGRAM\n< 51 58 06                                        | QX.\n\n> 02                                              | .\n< 49 44 35 37 38 55 56 00 12 56 31 31 30 00 00 06 | ID578UV..V110...\n\n> 52 02 64 00 00 10                               | R.d...\n< 57 02 64 00 00 10 fe ff ff ff ff ff ff ff ff ff | W.d.............\n| ff ff ff ff ff ff 65 06                         | ......e.\n\n> 52 02 64 00 10 10                               | R.d...\n< 57 02 64 00 10 10 ff ff ff ff ff ff ff ff ff ff | W.d.............\n| ff ff ff ff ff ff 76 06                         | ......v.\n\n> 52 02 64 00 20 10                               | R.d. .\n< 57 02 64 00 20 10 ff ff ff ff ff ff ff ff ff ff | W.d. ...........\n| ff ff ff ff ff ff 86 06                         | ........\n\n...\n\n> 52 01 64 08 80 10                               | R.d...\n< 57 01 64 08 80 10 00 ff ff ff ff ff ff ff ff ff | W.d.............\n| ff ff ff ff ff ff ee 06                         | ........\n\n> 52 02 48 02 00 10                               | R.H...\n< 57 02 48 02 00 10 01 08 00 00 ff ff ff ff ff ff | W.H.............\n| ff ff ff ff ff ff 59 06                         | ......Y.\n\n... ]]></programlisting>\n</example>\n<para>\n  The <xref linkend=\"revEngD578\"/> shows a capture of the data exchanged between the host \n  and an AnyTone AT-D578UV during a codeplug read. Everything starting with <token>&gt;</token> is\n  sent from the host to the device, while everything starting with <token>&lt;</token> is sent by \n  the device to the host.\n</para>\n<para>\n  The first packet send, is the ASCII string <token>PROGRAM</token>. This causes the device to enter\n  the programming mode. A message will be shown on the screen of the device. This is then \n  acknowledged by the device with a short message <token>QX</token> followed by a single \n  <token>06h</token> byte. The last byte appears to be weird, but it will always be present on any \n  response from the device. This might be seen as an end-of-packet byte.\n</para>\n<para>\n  The next command send by the host is pretty simple. It consists of a single <token>02h</token> \n  byte. The device responds with a 15 bytes long ID string, identifying the device name and hardware\n  version followed by the 06h end-of-packet byte. \n</para>\n<para>\n  After this, the actual codeplug read appears to start. The host sends a series of equally size \n  commands, each starting with an <token>R</token> followed by 5 bytes of payload. As mentioned \n  above, this payload must contain some sort of memory address and length field, to specify how \n  much to read and from where. To figure that out, one may study subsequent read requests. \n</para>\n<para>\n  The payloads of the first tree read read requests are \n  <programlisting><![CDATA[02 64 00 00 10\n02 64 00 10 10\n02 64 00 20 10\n]]></programlisting>\n  The only difference of these requests is the fourth byte. Hence, one may assume, that this byte is \n  part of the address field. However, we do not know which bytes are part of the address as well. \n</para>\n<para>\n  The responses to these requests also contain these bytes as well as 18 more bytes of payload. It is \n  reasonable that the transfer size is a power of two. So the request likely reads 16 bytes from the \n  device. 16 in hex is <token>10h</token>, hence one may assume that the last byte specifies the \n  amount of data to read. Hence we identified the size field as the fifth byte of the read request \n  payload data.\n</para>\n<para>\n  As the address increases by 10h from request to request, one may infer, that the read request\n  specifies the address to read from, not the sequence or block number of a read from. As the \n  entire Codeplug will not fit into 256 bytes, the address field must be larger. Even 64kb will \n  be too small, to hold the entire codeplug, the address is likely a 32bit integer. \n</para>\n<para>\n  This does not need to be true. Some radios will implement special commands to select the memory \n  bank beforehand. Then each 64kB bank can be accessed with a 16bit address. \n</para>\n<para>\n  To verify our assumption, we will study some read requests, that appear much later and check if \n  the first bytes change as well. And indeed, much later, we observe read request payloads like\n  <programlisting><![CDATA[01 64 08 80 10\n02 48 02 00 10\n]]></programlisting>  \n  Obviously, these bytes change too. Consequently, we may assume that the address is stored as a \n  32bit unsigned integer in big-endian byte-order. Finally, we may summarize the read request \n  format as\n  <programlisting><![CDATA[+---+---+---+---+---+---+\n|'R'| Address       | N |\n+---+---+---+---+---+---+]]></programlisting>\nWhere <token>N</token> is the number of bytes to read.\n</para>\n<para>\n  Now, it is time to study the structure of the read response. As we already reverse engineered the\n  read request, the read response is then easy to understand. Each of these responses start with a \n  <token>W</token> char, followed by the same address and length as send by the request. That is\n  <programlisting><![CDATA[> 52 02 64 00 00 10\n< 57 02 64 00 00 10 fe ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 65 06]]></programlisting>\n  Obviously the 16 byte data read, follows the length byte. The last byte is the common 06h \n  end-of-packet byte, we have already seen. So a single unknown 65h byte is still unknown.\n</para>\n<para>\n  This byte might be some sort of checksum over the payload. Checksums are notoriously difficult to\n  figure out. Frequently, common techniques like CRC16 are used. Here, a single checksum byte is \n  used, hence one of the common checksums is unlikely. Now starts some guesswork. We may compare \n  some very similar read responses to get an idea, how this checksum may work. For example\n  <programlisting><![CDATA[57 02 64 00 10 10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 76 06\n57 02 64 00 20 10 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff 86 06]]></programlisting>\n  The only difference between these two responses is one byte in the address, the remaining payload\n  is identical. The address also only differs by 10h like the checksum. So one may assume, that the \n  checksum is simply the sum over the payload bytes. Summing all bytes of the latter up to the \n  checksum gets 10ddh. Of cause, this is not the checksum directly. The checksum is a single byte. \n  But the least significant byte of the sum (ddh), however, does not match.\n</para>\n<para>\n  Maybe, the sum is not taken over the entire payload, but over the part that actually matters. That \n  is, the address, length and data fields. This time, the sum is 1086h and this time, the least \n  significant byte (86h) matches the checksum. Now, we can summarize the read response format as\n  <programlisting><![CDATA[+---+---+---+---+---+---+---+...+---+---+---+\n|'W'| Address       | N | Data (N)  |CRC|06h|\n+---+---+---+---+---+---+---+...+---+---+---+]]></programlisting>\n  where the CRC is the least significant byte of the sum over the address, length and data payload.\n</para>\n</section>\n\n</section>"
  },
  {
    "path": "doc/manual/reveng/reverseengineering.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<chapter xml:id=\"reverseEngineering\"\n         xmlns=\"http://docbook.org/ns/docbook\" \n         xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n         xmlns:xi=\"http://www.w3.org/2001/XInclude\">\n\n<info>\n  <title>Reverse engineering</title>\n</info>\n\n<para>\n  The majority of the development time needed for <application>qdmr</application>, consists of \n  reverse engineering the code plug and communication protocols of the radios. Fortunately, many \n  radios share the same communication protocol, in particular those from the same manufacturer.\n  Sometimes these protocols are even used by other manufacturers. Even if the protocol is \n  already implemented in <application>qdmr</application>, you may need to reverse engineer the \n  code plug format. This step has to be performed always, even if an already implemented code plug\n  is reused, you will need to verify that format. Any mistake here, may brick your device.\n</para>\n<note>\n  <para>\n    Before you attempt to reverse engineer anything, consider to invest some significant time in \n    research. Reverse engineering is an cumbersome and frequently frustrating task, there is no\n    need to waste your valuable time on something, that someone else has already done. Also, \n    consider documenting and publishing your results, so others can find it, even if it is \n    incomplete. Someone else might pickup your work and complete it.\n  </para>\n</note>\n<para>\n  In the following sections, I attempt to describe, how I approach reverse engineering the \n  communication protocols and code plugs. To this end, I hope it might help you in your reverse \n  engineering work. And if you like, you may contribute your work to qdmr.\n</para>\n\n<xi:include href=\"protocol.xml\"/>\n<xi:include href=\"codeplug.xml\"/>\n\n</chapter>"
  },
  {
    "path": "doc/qdmr.in.xml",
    "content": "<?xml version=\"1.0\"?>\n\n<refentry xml:id=\"qdmr\"\n  xmlns=\"http://docbook.org/ns/docbook\"\n  xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n  <refmeta>\n    <refentrytitle>qdmr</refentrytitle>\n    <manvolnum>1</manvolnum>\n    <refmiscinfo class=\"source\">qdmr</refmiscinfo>\n    <refmiscinfo class=\"version\">${PROJECT_VERSION}</refmiscinfo>\n    <refmiscinfo class=\"manual\">User Commands</refmiscinfo>\n  </refmeta>\n\n  <info>\n    <author>\n      <personname><firstname>Hannes</firstname> <surname>Matuschek</surname></personname>\n      <email>dm3mat@darc.de</email>\n      <contrib>Main author</contrib>\n    </author>\n  </info>\n\n  <refnamediv>\n    <refname>qdmr</refname>\n    <refpurpose>Graphical tool for programming DMR radios.</refpurpose>\n  </refnamediv>\n\n  <refsynopsisdiv>\n    <cmdsynopsis>\n      <command>qdmr</command>\n      <arg choice=\"opt\"><option>OPTIONS</option></arg>\n      <arg choice=\"opt\"><option>CODEPLUG_FILE</option></arg>\n    </cmdsynopsis>\n  </refsynopsisdiv>\n\n  <refsect1>\n    <title>Description</title>\n    <para>\n      <command>qdmr</command> is a graphical tool to program DMR radios.\n      That is, reading, editing, generating and uploading codeplugs to these \n      radios. \n    </para>\n    <para>\n      The configuration of these radios is usually stored in a highly vendor\n      and device specific binary codeplug. <command>qdmr</command> stores this\n      configuration in a human-readable and device independent format, thus \n      allowing for sharing codeplugs across devices.\n    </para>\n  </refsect1>\n\n  <refsect1>\n    <title>Options</title>\n    <variablelist>\n      <varlistentry>\n        <term><option>CODEPLUG_FILE</option></term>\n        <listitem>\n          <para>\n            If the optional codeplug file is passed to <command>qdmr</command>, \n            it gets loaded on startup. Otherwise, the application starts with \n            an empty codeplug.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-style=STYLE</option></term>\n        <listitem>\n          <para>\n            This option allows to set a Qt style for the application. This can\n            be used to alter the style of the widgets. So call \n            <userinput>qdmr -style=windows</userinput> if you fancy.\n          </para>\n          <para>\n            However, this option is of real use if you want to use a dark \n            theme. By installing a dark Qt theme like kvantum-dark and starting\n            qdmr by passing this theme to the style option. That is\n          </para>\n          <screen>\n            qdmr -style=kvantum-dark\n          </screen>\n          <para>\n            You may set the Qt style for all Qt applications using the \n            <varname>QT_STYLE_OVERRIDE</varname> environment variable.\n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>-stylesheet=FILENAME</option></term>\n        <listitem>\n          <para>\n            This option allows for specifying a style sheet to alter the \n            appearance of some or all widgets of <command>qdmr</command>. \n          </para>\n        </listitem>\n      </varlistentry>\n      <varlistentry>\n        <term><option>--loglevel=LEVEL</option></term>\n        <listitem>\n          <para>\n            This option allows for specifying the log level for messages \n            printed to stderr. This does not affect the log level of messages\n            written to the log file. \n          </para>\n          <para>\n            Must be one of <token>debug</token>, <token>info</token>, \n            <token>warning</token>, <token>error</token> or \n            <token>fatal</token>. Default is <token>info</token>.\n          </para>\n        </listitem>\n      </varlistentry>\n    </variablelist>\n  </refsect1>\n\n  <refsect1>\n    <title>Logging</title>\n    <para>\n      <command>qdmr</command> writes a lot of debug and error messages to \n      the log file. This can be used to inspect some issues during\n      reading/writing the a codeplug to the device. The log file \n      containing these messages is usually found at \n      <filename>~/.local/share/DM3MAT/qdmr/qdmr.log</filename>.\n    </para>\n  </refsect1>\n\n  <refsect1>\n    <title>Bugs</title>\n    <para>\n      This program is still under development and may contain bugs that\n      may cause harm to the radios and may even destroy them. Hence you may\n      use this software on your own risk. If you want to have guaranties,\n      consider using the CPS (code-plug programming software) supplied with\n      your radio.\n    </para>\n    <para>\n      Writing a single application supporting several radios of different \n      manufacturers is a hard task. To this end, there are plenty of bugs to be\n      expected. If you stumble across one of them, consider opening an issue at\n      <ulink url=\"https://github.com/hmatuschek/qdmr/issues\"/>.\n    </para>\n  </refsect1>\n</refentry>\n"
  },
  {
    "path": "doc/reveng/README.md",
    "content": "# How to reverse engineer code-plugs?\n\nThis question is certainly not easy to answer. It heavily depends on the particular device. In \ngeneral, reverse engineering the code-plug requires two steps. First, the communication protocol\nbetween the host and the device must be known. This step can be skipped if the protocol is already\nknown (e.g., for Radioddity, Baofeng, Tytera, Retevis or AnyTone devices) or when the protocol is \nactually an industry standard (e.g., DFU). \n\nOnce you know how the host communicates with the device, the actual code-plug can be tackled: The \ngeneral approach is a so-called differential analysis. That is, using the original CPS, you start \nwith a compact code-plug that covers all features for the radio. Then you change only ONE thing \nusing the original CPS and compare the two results. \n\nSometimes, the manufacturer is nice to you and the binary code-plug saved by the original CPS is \nbasically a one-to-one image of the memory content written to the device. This is true for the \nRadioddity GD-77 code-plugs and to some lesser extend for the RD-5R, TyT MD-UV390 code-plugs. \n\nSometimes, however, the manufacturer CPS saves something that has basically nothing to do with \nwhat is written to the device. For example the AnyTone D-878UV. In these cases, the content \nbeing written to the device must be captured. This can be done using wireshark on a linux \nhost. That is, fire up a virtual machine running the original CPS. Start wireshark on the host \nsystem and monitor the USB interface using *usbmon*. Then, write a small script that extracts \nthe actual data written to the device from the capture files. These can then be compared to \nperform the differential analysis. An example script including captured traffic and extracted \ndata can be found in the anytone directory."
  },
  {
    "path": "doc/reveng/anytone/README.md",
    "content": "# Reverse engineering of AnyTone codeplugs\nThe transfer protocol of the AnyTone devices is based on the USB-Serial protocol (ACM-CDC) and is\nwell known. To this end, it is possible to *emulate* the AnyTone devices using a simple python \nscript. You will find a matching script in every subdirectory.\n\n## Emulation under windows\nThese scripts expect a serial loopback device to be present. Under windows, such a device can be\ncreated using a *virtual null-modem cable*. For example [com0com](http://com0com.sourceforge.net/).\nThis will create a pair of COM ports (e.g., COM5 and COM6). One will be used by the emulation and \nthe other will be used by the manufacturer CPS. \n\nFirst, start the emulation\n```\npython3 at_dxxx_emulator.py COM5\n```\nHere, the COM5 port will be used by the emulation. Then, start the CPS and select `COM6` as the \nport to be used.\n\n## Emulation under Linux\nIt might be possible to emulate the device under Linux and using Wine to run the CPS. First, a pair\nof *serial ports* (pts) must be created. This can be done using `socat` by calling \n```\nsocat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000\n```\nThis call creates a pair of pseudo-terminals, for example `/dev/pts/3` and `/dev/pts/4`. The \nemulation may then use one of them by calling\n```\npython3 at_dxxx_emulator.py /dev/pts/3\n```\n\nNow, the second must be associated with a COM port in the Wine emulation. This is done using the\nregistry editor. Run `wine regedit` and change \n```\nHKEY_LOCAL_MACHINE/Software/Wine/Port/COM1  /dev/pts/4\n```\nYou may need to create that key first. This will bind the Wine emulated COM port to the second \npseudo-terminal. \n\nNow, start the manufacturer CPS under Wine and select COM1.\n\n## Differential Codeplug Analysis\nCreate a small codeplug containing at least two channels, two contacts, a group list and a zone. \nSave that codeplug and write it to the device. The emulator will save that codeplug as a hex dump \nin a file called `codeplug_0000.hex`. Any further codeplug write will be saved as a separate \nhex dump with an incremented number. \n\nThe differential analysis of the codeplug allows to figure out how and where a specific setting is\nencoded within the binary codeplug. To do that, make a single change in the codeplug. E.g. changing\nthe first radio ID. Then write that changed codeplug to the emulation. The hexdump of the codeplug\nis then save as `codeplug_0001.hex`. \n\nYou can then use a diff tool to inspect the changes. E.g.\n```\n$ diff codeplug_0000.hex codeplug_0001.hex\n1530c1530\n< 02580000 : 01 23 45 67 00 52 61 64 69 6f 20 31 00 00 00 00 | .#Eg.Radio 1....\n---\n> 02580000 : 00 12 34 56 00 52 61 64 69 6f 20 31 00 00 00 00 | ..4V.Radio 1....\n```\nHere the first radio ID 1234567 was changed to 123456. The only differences between the codeplugs\nare at the address 2580000. Apparently, the first radio ID is stored there together with the radio\nname. The first 4 bytes at this address have changed from `01 23 45 67` to `00 12 34 56`. As the \nnumbers are readable in hex, they are stored in BCD and the most-significant number is stored \nfirst. So the radio ID is stored in an 8-digit BCD in big-endian. \n\nThis step is then repeated for all settings within the CPS to figure out the entire codeplug."
  },
  {
    "path": "doc/reveng/anytone/d578uv/at_d578uv_emulator.py",
    "content": "#!/usr/bin/env python3\n#\n# Emulate anytone d878uv radio to customer programming software. \n# Send intercepted data stream over network to server script for further investigation.\n#\n# This script connects to a virtual com port COM26 which is connected via a virtual\n# null modem cable to the virtual com port COM18 which is used by the programming software.\n# This virtual ports and cable can be provided by the COM0COM tool.\n#\n# Linux users can use\n# socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000\n# for emulating a virtual null modem cable.\n\nimport serial\nimport time\nimport sys\nimport struct \n\n\n# config\nfilebase = 'codeplug' \nfilecount = 0\ncomport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS\n\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\n# parameters?\nif len(sys.argv) == 2:\n   comport = sys.argv[1]\nelif len(sys.argv) >= 3:\n   print(\"Usage: \" + sys.argv[0] + ' [comport]')\n   exit()\n\n\n\n# open serial port\nserialPort = None\n\ntry:\n   print(\"Trying comport \" + comport)\n   serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000\nexcept (err):\n   print('ERR: Could not open port ' + comport)\n   print(\"Usage: \" + sys.argv[0] + ' servername [comport]: ' + err)\n   exit()\n\n\nout = None \nnextaddr = None \n\n# wait for data\n\ntry:\n   \n   while 1:\n\n      command = ''\n\n      command = serialPort.read()\n      while serialPort.in_waiting > 0:\n         command += serialPort.read()\n\n      # respond to command on com port\n      if ( len(command) == 0 ):\n         pass\n\n      elif ( command == b'PROGRAM'):\n         print(\"Program session requested.\")\n         resp = b'QX\\x06'\n         serialPort.write(resp)\n         filename = \"{}_{:04}.hex\".format(filebase, filecount)\n         out = open(filename, \"w\")\n         nextaddr = None \n         \n      elif ( command == b'\\x02' ):\n         print(\"Device info requested.\")\n         resp = b'ID578UV\\x00\\x00V110\\x00\\x00\\x06'\n         serialPort.write(resp)\n\n      elif ( command == b'R\\x02\\xfa\\x00\\x20\\x10' ):\n         print(\"Read special memory request.\")\n         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'\n         serialPort.write(resp)\n         \n      elif ( command[0:4] == b'R\\x02\\xfa\\x00' and command[5] == 16 ):\n         # 0x02fa00..\n         print(\"Read local information.\")\n         \n         resp = b'W\\x02\\xfa\\x00' + bytes([command[4]]) + b'\\x10'\n         \n         if ( command[4] == 0x00 ):\n            resp += b'\\x00\\x00\\x00\\x03\\x01\\x01\\x01\\x00\\x00\\x01\\x01\\x20\\x20\\x20\\x20\\xff'\n                                           \n         elif ( command[4] == 0x10 ): # Radio Type\n            resp += b'\\x44\\x38\\x37\\x38\\x55\\x56\\x00\\x01\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n\n         elif ( command[4] == 0x30 ): # Serial Number\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x40 ): # Production Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x50 ): # Manucfacture Code\n            resp += b'\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n         elif ( command[4] == 0x60 ): # Maintained Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x70 ): # Dealer Code\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x80 ): # Stock Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x90 ): # Sell Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xa0 ): # Seller\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         elif ( command[4] == 0xb0 ): # Maintained Description\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xc0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xd0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xe0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xf0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         else:\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\\x06'\n         #print(resp.hex())\n         serialPort.write(resp)\n\n      elif ( command[0] == ord('W') ) :\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         res = struct.unpack(\">cIB16sBB\", command)\n         addr = res[1]\n         if nextaddr != addr:\n            out.write((8+3+16*3+16+2)*\"-\" + \"\\n\")\n         out.write(\"{:08X} : {}\\n\".format(addr, hexDump(res[3])))\n         nextaddr = addr+16\n         \n      elif ( command == b'END' ):\n         print(\"End session.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         out.close()\n         filecount += 1\n\n      elif ( command == b'UPDATE' ):\n         # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys\n         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)\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command == b'\\x18' ):\n         print(\"Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command[0] == 0x01 ):\n         print(\"Firmware data.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n      else:\n         #print(\"> \" + str(command))\n         pass\n\n\n\nfinally:\n   print('QRT')\n   serialPort.close()\n\n"
  },
  {
    "path": "doc/reveng/anytone/d868uve/at_d868uv_emulator.py",
    "content": "#!/usr/bin/env python3\n#\n# Emulate anytone d878uv radio to customer programming software. \n# Send intercepted data stream over network to server script for further investigation.\n#\n# This script connects to a virtual com port COM26 which is connected via a virtual\n# null modem cable to the virtual com port COM18 which is used by the programming software.\n# This virtual ports and cable can be provided by the COM0COM tool.\n#\n# Linux users can use\n# socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000\n# for emulating a virtual null modem cable.\n\nimport serial\nimport time\nimport sys\nimport struct \n\n\n# config\nfilebase = 'codeplug' \nfilecount = 0\ncomport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS\n\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\n# parameters?\nif len(sys.argv) == 2:\n   comport = sys.argv[1]\nelif len(sys.argv) >= 3:\n   print(\"Usage: \" + sys.argv[0] + ' [comport]')\n   exit()\n\n\n\n# open serial port\nserialPort = None\n\ntry:\n   print(\"Trying comport \" + comport)\n   serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000\nexcept (err):\n   print('ERR: Could not open port ' + comport)\n   print(\"Usage: \" + sys.argv[0] + ' servername [comport]: ' + err)\n   exit()\n\n\nout = None \nnextaddr = None \n\n# wait for data\n\ntry:\n   \n   while 1:\n\n      command = ''\n\n      command = serialPort.read()\n      while serialPort.in_waiting > 0:\n         command += serialPort.read()\n\n      # respond to command on com port\n      if ( len(command) == 0 ):\n         pass\n\n      elif ( command == b'PROGRAM'):\n         print(\"Program session requested.\")\n         resp = b'QX\\x06'\n         serialPort.write(resp)\n         filename = \"{}_{:04}.hex\".format(filebase, filecount)\n         out = open(filename, \"w\")\n         nextaddr = None \n         \n      elif ( command == b'\\x02' ):\n         print(\"Device info requested.\")\n         resp = b'ID868UVE\\x00V102\\x00\\x00\\x06'\n         serialPort.write(resp)\n\n      elif ( command == b'R\\x02\\xfa\\x00\\x20\\x10' ):\n         print(\"Read special memory request.\")\n         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'\n         serialPort.write(resp)\n         \n      elif ( command[0:4] == b'R\\x02\\xfa\\x00' and command[5] == 16 ):\n         # 0x02fa00..\n         print(\"Read local information.\")\n         \n         resp = b'W\\x02\\xfa\\x00' + bytes([command[4]]) + b'\\x10'\n         \n         if ( command[4] == 0x00 ):\n            resp += b'\\x00\\x00\\x00\\x00\\x01\\x01\\x01\\x00\\x00\\x01\\x01\\x20\\x20\\x20\\x20\\xff'\n                                           \n         elif ( command[4] == 0x10 ): # Radio Type\n            resp += b'\\x44\\x38\\x37\\x38\\x55\\x56\\x00\\x01\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n\n         elif ( command[4] == 0x30 ): # Serial Number\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x40 ): # Production Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x50 ): # Manucfacture Code\n            resp += b'\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n         elif ( command[4] == 0x60 ): # Maintained Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x70 ): # Dealer Code\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x80 ): # Stock Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x90 ): # Sell Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xa0 ): # Seller\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         elif ( command[4] == 0xb0 ): # Maintained Description\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xc0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xd0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xe0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xf0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         else:\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\\x06'\n         #print(resp.hex())\n         serialPort.write(resp)\n\n      elif ( command[0] == ord('W') ) :\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         res = struct.unpack(\">cIB16sBB\", command)\n         addr = res[1]\n         if nextaddr != addr:\n            out.write((8+3+16*3+16+2)*\"-\" + \"\\n\")\n         out.write(\"{:08X} : {}\\n\".format(addr, hexDump(res[3])))\n         nextaddr = addr+16\n         \n      elif ( command == b'END' ):\n         print(\"End session.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         out.close()\n         filecount += 1\n\n      elif ( command == b'UPDATE' ):\n         # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys\n         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)\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command == b'\\x18' ):\n         print(\"Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command[0] == 0x01 ):\n         print(\"Firmware data.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n      else:\n         #print(\"> \" + str(command))\n         pass\n\n\n\nfinally:\n   print('QRT')\n   serialPort.close()\n\n"
  },
  {
    "path": "doc/reveng/anytone/d878uv/README.md",
    "content": "# How to reverse engineer the AnyTone code-plugs\nUnfortunately, AnyTone CPSs store the code-plug in a completely different format on the disc \ncompared to the content being written to the device. This requires to observe the communication\nbetween the host running the original CPS and the device. Moreover, a complete code-plug must \nbe programmed to the device for every option in the CPS, to be able to reverse engineer the \nformat. \n\nSo to reverse engineer the code-plug format on the device, you first need to get the original\nCPS running in a virtual machine on a Linux host (I use VirtualBox for that, but any other will\ndo too). Then the *usbmon* kernel module must be loaded to be able to grab all traffic between\nthe host and the radio. So call\n```\n $ sudo modprobe usbmon\n```\n\nThen fire up wireshark \n```\n $ sudo wireshark\n```\nand start monitoring on the *usbmon* device.\n\n  * Then start to upload a base code-plug to the device. Wireshark will capture everything that \n    gets sent to the device. \n  * Once the code-plug is written to the device, stop capturing in wireshark and  save the data \n    under a recognizable filename, e.g. *base.pcapng*. \n  * Change a single thing within the CPS, e.g., enable GPS. Remember, we are doing a differential \n    analysis, so change only one setting at a time.\n  * Restart capturing in wireshark and upload the modified code-plug to the device.\n  * Once the code-plug is written, stop capturing and save the data in another file, e.g. \n    *gps_enabled.pcapng*.\n  * Extract the data written to the device using the *extract.py* script with \n    `python3 extract.py PCAPNG_FILE > HEX_FILE` for both captured code-plugs.\n  * Using *diff* you can now compare the two hex files. As only one feature of the code-plug was\n    changed, the difference between the two hex files should be tiny. In fact if only a single \n    setting is changed, frequently only a single bit/byte will change in the code-plug. \n\nRepeat the steps above over and over again to find the memory location of each option, setting \nand feature of the original CPS. Over time, you will be able to reverse engineer the entire \ncode-plug. \n\nBut keep in mind, that you will not be able to reverse engineer every single bit of the code-plug.\nAt the end, there will be some reserved, fixed or hidden options left.\n\n## An example\nIn this directory, you will find two examples. The captured and extracted base code-plug \n(*capture_base.pcapng* and *d878uv_base.hex*) as well as a captured and extracted \ncode-plug, where only a single setting was changed (*capture_set_gps_on.pcapng* and \n*d878uv_set_gps_on.hex*). In this example, the GPS was enabled in the CPS. \n\nThe *diff* of the two hex files reveals\n```\n3029c3029\n< 02500020 : 01 00 00 00 00 00 04 01 00 01 01 00 00 01 02 01 | ................\n---\n> 02500020 : 01 00 00 00 00 00 04 01 01 01 01 00 00 01 02 01 | ................\n```\nThis tells me, that at the address 0x02500020 + 8, a single byte changed from 00 to 01. The entire\nremaining code-plug (about 100kB) stayed the same. Comparing the address 0x02500028 with the \n[coarse memory layout](https://dm3mat.darc.de/qdmr/libdmrconf/class_d878_u_v_codeplug.html) \nof the code-plug tells me, that byte 40 (hex 0x28) of the general settings section of the \ncode-plug enables or disables the GPS."
  },
  {
    "path": "doc/reveng/anytone/d878uv/cpsfileformat.md",
    "content": "# AnyTone CPS codeplug file format\n\nThe 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.\n\n\n## General layout\nThe 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. \n\nThe order of elements in the file is \n\n  * Header (fixed size)\n  * Channel list\n  * Radio ID list\n  * Zone list\n  * List of scan lists\n  * List of analog contacts\n\nThe format of each element is documented below.\n\n\n## Header\n```\n   0                               8                               16\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | CPS version       | Content size  | Model name                |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 | 0   0   0 | HW version    |                                   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n30 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n40 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n50 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n60 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n70 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n80 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n90 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\na0 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nb0 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nc0 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\nd0 |                                                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\ne0 |       |\n   +---+---+\n```\n\n| Field  | Description |\n|---|---|\n| CPS Version | Version number of the CPS as a string E.g., \"V1287\" |\n| Content size | Content size in bytes, little-endian. |\n| Model name | Model name as a string. E.g., \"D878UV\" |\n| HW Version | Hardware version as a string. E.g., \"V100\" |\n\n\n## Channel list\nThe channel list consists of a simple list of all channels concatenated together preceded by the number of channels encoded as little endian.\n\n```\n +---+---+---+---+---+---+ ... +\n | #Chan | Channel data    ... |\n +---+---+---+---+---+---+ ... +\n```\n\n### Channel encoding\nEach channel is encoded in a variable size binary blob as\n\n```\n   0                               8                               16\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n00 | ChIdx | RX frequency  |RPT| TX offset     |TYP|PWR|BWD| 0 |RXO|\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n10 |CAC|TKA| RXCTC |       | TXCTC |                               |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n20 |ADM|                           |CLC|   |TSL|IGS|       |WAL|   |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n   +---+---+---+---+---+---+...+---+\n30 | Channel name N chars   ...  0 |\n   +---+---+---+---+---+---+...+---+\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n   | 0 |RNG| ? | 0   0 |APT| 0 |FreqCor| 0 |SMC|ExR| 0   0   0   0 |\n   +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n   | 0 |DAK| 0   0 | \n   +---+---+---+---+\n\n```\n\n| Field  | Description |\n|---|---|\n| ChIdx | Specifies the channel index in little endian, 0-based. |\n| RX frequency | RX frequency in 10Hz, little endian. |\n| RPT | Repeater type, 0=simplex, 1=positive, 2=negative |\n| TX offset | TX offset frequency in 10Hz, little endian. Sign determined by RPT field. |\n| TPY | Channel type, 1=digital, 0=analog, ... |\n| PWR | Power setting 0=low, 1=mid, 2=high, 3=turbo. |\n| BWD | Analog band width 0=12.5kHz, 1=25kHz. |\n| RXO | RX only flag. |\n| CAC | Call confirm flag. |\n| TKA | Talkaround flag. |\n| RXCTC | RX CTCSS/DSC settings. |\n| TXCTC | RX CTCSS/DSC settings. |\n| ADM | Admit criterion, 0=always, 1=, 2=, 3=same color code. Also busy lock 1=repeater, 2=busy.|\n| COC | Color code. |\n| TSL | Time slot, 0=TS1, 1=TS2 |\n| IGS | Ignore time slot flag. |\n| WAL | Work alone flag. |\n| Channel name | Channel name of max 16 ASCII chars. Variable size (max. 16b?). Zero padded. |\n| RNG | Ranging flag. |\n| APT | APRS report type, 0=none, 1=analog, 2=digital. |\n| FreqCor | Frequency correction in Hz, little endian. |\n| SMC | SMS confirmation flag. |\n| ExR | Exclude from roaming flag. |\n| DAK | Data ACK flag. |\n\n\n## RadioID list\nAs usual, the radio ID list starts with the number of defined IDs (little endian) and is followed by the actual list of IDs.\n```\n +---+---+---+---+---+ ... +\n |#ID| RadioID data    ... |\n +---+---+---+---+---+ ... +\n```\n\n### Radio ID encoding\n```\n+---+---+---+---+---+---+ ... +---+\n|IDX| DMR ID    | Name          0 |\n+---+---+---+---+---+---+ ... +---+\n```\n\n| Field  | Description |\n|---|---|\n| IDX | Specifies the radio ID index in little endian, 0-based. |\n| DMR ID | The DMR ID as a 24bit little endian integer. |\n| Name | Variable length radio ID name. ASCII, 0-terminated. |\n\n\n## Zone List\nAs usual, the zone starts with the number of defined zones and is followed by the actual list of zones.\n```\n +---+---+---+---+ ... +\n |#ZO| Zone data   ... |\n +---+---+---+---+ ... +\n```\n\n### Zone encoding\n```\n+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+ ... +---+\n|ZIX|NCH| Ch00  | Ch01  | ... | Chn A | Chn B | Zone name   ...   0 |\n+---+---+---+---+---+---+ ... +---+---+---+---+---+---+---+ ... +---+\n```\n\n| Field  | Description |\n|---|---|\n| ZIX | Zone index. |\n| NCH | Number of channel in zone. |\n| ChXX | List of 16bit, little endian, 0-based channel indices |\n| Chn A | Zone channel A |\n| Chn B | Zone channel B |\n| Zone name | Name of the zone, ASCII 0-terminated. |\n\n\n## Scan lists\nAs usual, the scan lists starts with the number of scan lists and is followed by the actual scan lists.\n```\n +---+---+---+---+---+ ... +\n |#SL| Scan list data  ... |\n +---+---+---+---+---+ ... +\n```\n\n### Scan list encoding\n```\n+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+\n|SLI| Name    ...   0 | 0 |PCS|  PC1  |  PC2  |RVC|LBA|LBB|DOD|DWT|\n+---+---+---+ ... +---+---+---+---+---+---+---+---+---+---+---+---+\n+---+---+---+---+---+---+---+ ... + \n|NCH| 0   0 | Ch00  | Ch01  | ... |\n+---+---+---+---+---+---+---+ ... +\n```\n\n| Field  | Description |\n|---|---|\n| SLI | Scan list index. |\n| Name | Name of the scan list, max 16 x ASCII, 0-terminated. |\n| PCS | Priority channel select, 0=Off, 1=PC1, 2=PC2, 3=Both |\n| PC1 | Priority channel 1, 0=Off, 1=Current, else channel index + 2, little endian. |\n| PC2 | Priority channel 2, 0=Off, 1=Current, else channel index + 2, little endian. |\n| RVC | Revert channel, 0=Selected, 1=Selected+Talkback, 2=Last called, 3=Last used. |\n| LBA | Look back time A in 100ms. |\n| LBB | Look back time B in 100ms. |\n| DOD | Dropout delay in 100ms. |\n| DWT | Dwell time in 100ms. |\n| NCH | Number of channels in scan list. |\n\n\n## Analog contacts\nAs usual, the analog contact list starts with the number of contacts and is followed by the actual contact list.\n```\n +---+---+---+---+---+---+ ... +\n |#AC| Analog contact data ... |\n +---+---+---+---+---+---+ ... +\n```\n\n### Analog contact encoding\n```\n+---+---+---+---+ ... +---+---+ ... +---+\n|IDX|NoL| Number  ... | Name    ...   0 |\n+---+---+---+---+ ... +---+---+ ... +---+\n```\n| Field  | Description |\n|---|---|\n| IDX | Analog contact index. |\n| NoL | Number length. |\n| Name | Name of the contact, max 16 x ASCII, 0-terminated. |\n\n"
  },
  {
    "path": "doc/reveng/anytone/d878uv/d878uv_base.hex",
    "content": "-----------------------------------------------------------------------------\n00800000 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800010 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800020 : 01 00 00 44 42 30 4c 44 53 20 54 53 31 00 00 00 | ...DB0LDS TS1...\n00800030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800040 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800050 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800060 : 01 00 00 53 61 2f 54 68 20 44 42 30 4c 44 53 20 | ...Sa/Th DB0LDS \n00800070 : 54 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS1.............\n00800080 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800090 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008000A0 : 01 01 00 54 47 38 20 44 42 30 4c 44 53 20 54 53 | ...TG8 DB0LDS TS\n008000B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008000C0 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n008000D0 : cf 09 00 00 05 00 00 00 00 00 03 00 01 00 00 00 | ................\n008000E0 : 01 01 00 54 47 39 20 44 42 30 4c 44 53 20 54 53 | ...TG9 DB0LDS TS\n008000F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800100 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800110 : cf 09 00 00 06 00 00 00 00 00 03 00 01 00 00 00 | ................\n00800120 : 01 01 00 42 42 20 44 42 30 4c 44 53 20 54 53 32 | ...BB DB0LDS TS2\n00800130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800140 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800150 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800160 : 01 00 00 44 4d 30 54 5a 4e 20 54 53 31 00 00 00 | ...DM0TZN TS1...\n00800170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800180 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800190 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008001A0 : 01 01 00 54 47 38 20 44 4d 30 54 5a 4e 20 54 53 | ...TG8 DM0TZN TS\n008001B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008001C0 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008001D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008001E0 : 01 01 00 54 47 39 20 44 4d 30 54 5a 4e 20 54 53 | ...TG9 DM0TZN TS\n008001F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800200 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800210 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800220 : 01 01 00 42 42 20 44 4d 30 54 5a 4e 20 54 53 32 | ...BB DM0TZN TS2\n00800230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800240 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800250 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800260 : 01 00 00 44 42 30 4c 4f 53 20 54 53 31 00 00 00 | ...DB0LOS TS1...\n00800270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800280 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800290 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008002A0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 4f 53 20 | ...R/TG9 DB0LOS \n008002B0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n008002C0 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n008002D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008002E0 : 01 00 00 52 2f 54 47 39 20 44 4d 30 54 54 20 54 | ...R/TG9 DM0TT T\n008002F0 : 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S1..............\n00800300 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800310 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800320 : 01 01 00 54 47 38 20 44 4d 30 54 54 20 54 53 32 | ...TG8 DM0TT TS2\n00800330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800340 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800350 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800360 : 01 01 00 54 47 39 20 44 4d 30 54 54 20 54 53 32 | ...TG9 DM0TT TS2\n00800370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800380 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800390 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008003A0 : 01 01 00 42 42 20 44 4d 30 54 54 20 54 53 32 00 | ...BB DM0TT TS2.\n008003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008003C0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n008003D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008003E0 : 01 00 00 54 47 39 20 44 42 30 4b 4b 20 54 53 31 | ...TG9 DB0KK TS1\n008003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800400 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800410 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800420 : 01 01 00 42 42 20 44 42 30 4b 4b 20 54 53 32 00 | ...BB DB0KK TS2.\n00800430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800440 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800450 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800460 : 01 00 00 44 42 30 4f 55 44 20 54 53 31 00 00 00 | ...DB0OUD TS1...\n00800470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800480 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800490 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008004A0 : 01 01 00 54 47 38 20 44 42 30 4f 55 44 20 54 53 | ...TG8 DB0OUD TS\n008004B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008004C0 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008004D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008004E0 : 01 01 00 54 47 39 20 44 42 30 4f 55 44 20 54 53 | ...TG9 DB0OUD TS\n008004F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800500 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800510 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800520 : 01 01 00 42 42 20 44 42 30 4f 55 44 20 54 53 32 | ...BB DB0OUD TS2\n00800530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800540 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800550 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800560 : 01 00 00 54 47 39 20 44 42 30 54 55 20 54 53 31 | ...TG9 DB0TU TS1\n00800570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800580 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800590 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008005A0 : 01 01 00 42 42 20 44 42 30 54 55 20 54 53 32 00 | ...BB DB0TU TS2.\n008005B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008005C0 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008005D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008005E0 : 01 00 00 44 4d 30 4d 4f 54 20 54 53 31 00 00 00 | ...DM0MOT TS1...\n008005F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800600 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800610 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800620 : 01 01 00 54 47 38 20 44 4d 30 4d 4f 54 20 54 53 | ...TG8 DM0MOT TS\n00800630 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800640 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800650 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800660 : 01 01 00 54 47 39 20 44 4d 30 4d 4f 54 20 54 53 | ...TG9 DM0MOT TS\n00800670 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800680 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800690 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008006A0 : 01 01 00 42 42 20 44 4d 30 4d 4f 54 20 54 53 32 | ...BB DM0MOT TS2\n008006B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008006C0 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n008006D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008006E0 : 01 00 00 44 42 30 54 41 20 54 53 31 00 00 00 00 | ...DB0TA TS1....\n008006F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800700 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800710 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800720 : 01 01 00 54 47 38 20 44 42 30 54 41 20 54 53 32 | ...TG8 DB0TA TS2\n00800730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800740 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800750 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800760 : 01 01 00 54 47 39 20 44 42 30 54 41 20 54 53 32 | ...TG9 DB0TA TS2\n00800770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800780 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800790 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008007A0 : 01 01 00 42 42 20 44 42 30 54 41 20 54 53 32 00 | ...BB DB0TA TS2.\n008007B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008007C0 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n008007D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008007E0 : 01 00 00 44 42 30 46 58 20 54 53 31 00 00 00 00 | ...DB0FX TS1....\n008007F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800800 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800810 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800820 : 01 01 00 54 47 38 20 44 42 30 46 58 20 54 53 32 | ...TG8 DB0FX TS2\n00800830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800840 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800850 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800860 : 01 01 00 54 47 39 20 44 42 30 46 58 20 54 53 32 | ...TG9 DB0FX TS2\n00800870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800880 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800890 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008008A0 : 01 01 00 42 42 20 44 42 30 46 58 20 54 53 32 00 | ...BB DB0FX TS2.\n008008B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008008C0 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n008008D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008008E0 : 01 00 00 44 42 30 50 44 4d 20 54 53 31 00 00 00 | ...DB0PDM TS1...\n008008F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800900 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800910 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800920 : 01 01 00 54 47 38 20 44 42 30 50 44 4d 20 54 53 | ...TG8 DB0PDM TS\n00800930 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800940 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800950 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800960 : 01 01 00 54 47 39 20 44 42 30 50 44 4d 20 54 53 | ...TG9 DB0PDM TS\n00800970 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800980 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800990 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008009A0 : 01 01 00 42 42 20 44 42 30 50 44 4d 20 54 53 32 | ...BB DB0PDM TS2\n008009B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008009C0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n008009D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008009E0 : 01 00 00 44 42 30 42 52 42 20 54 53 31 00 00 00 | ...DB0BRB TS1...\n008009F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800A00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800A20 : 01 01 00 54 47 38 20 44 42 30 42 52 42 20 54 53 | ...TG8 DB0BRB TS\n00800A30 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800A40 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800A60 : 01 01 00 54 47 39 20 44 42 30 42 52 42 20 54 53 | ...TG9 DB0BRB TS\n00800A70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800A80 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800AA0 : 01 01 00 42 42 20 44 42 30 42 52 42 20 54 53 32 | ...BB DB0BRB TS2\n00800AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800AC0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800AD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800AE0 : 01 00 00 44 42 30 53 50 4e 20 54 53 31 00 00 00 | ...DB0SPN TS1...\n00800AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800B00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B10 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800B20 : 01 00 00 52 2f 54 47 39 20 44 42 30 53 50 4e 20 | ...R/TG9 DB0SPN \n00800B30 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800B40 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B50 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800B60 : 01 00 00 44 42 30 4e 4c 53 20 54 53 31 00 00 00 | ...DB0NLS TS1...\n00800B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800B80 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B90 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800BA0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4e 4c 53 20 | ...R/TG9 DB0NLS \n00800BB0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800BC0 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800BD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800BE0 : 01 00 00 44 42 30 4c 53 20 54 53 31 00 00 00 00 | ...DB0LS TS1....\n00800BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C00 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800C20 : 01 01 00 54 47 38 20 44 42 30 4c 53 20 54 53 32 | ...TG8 DB0LS TS2\n00800C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C40 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800C60 : 01 01 00 54 47 39 20 44 42 30 4c 53 20 54 53 32 | ...TG9 DB0LS TS2\n00800C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C80 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800CA0 : 01 01 00 42 42 20 44 42 30 4c 53 20 54 53 32 00 | ...BB DB0LS TS2.\n00800CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800CC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800CD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800CE0 : 01 00 00 44 4d 30 4c 43 20 54 53 31 00 00 00 00 | ...DM0LC TS1....\n00800CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D00 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D10 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800D20 : 01 01 00 54 47 38 20 44 4d 30 4c 43 20 54 53 32 | ...TG8 DM0LC TS2\n00800D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D40 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800D60 : 01 01 00 54 47 39 20 44 4d 30 4c 43 20 54 53 32 | ...TG9 DM0LC TS2\n00800D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D80 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D90 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800DA0 : 01 01 00 53 61 2f 54 68 20 44 4d 30 4c 43 20 54 | ...Sa/Th DM0LC T\n00800DB0 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2..............\n00800DC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800DD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800DE0 : 01 00 00 42 42 20 44 4d 30 4c 43 20 54 53 31 00 | ...BB DM0LC TS1.\n00800DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800E00 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800E20 : 01 00 00 44 42 30 46 4c 57 20 54 53 31 00 00 00 | ...DB0FLW TS1...\n00800E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800E40 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E50 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800E60 : 01 01 00 54 47 38 20 44 42 30 46 4c 57 20 54 53 | ...TG8 DB0FLW TS\n00800E70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800E80 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E90 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800EA0 : 01 01 00 54 47 39 20 44 42 30 46 4c 57 20 54 53 | ...TG9 DB0FLW TS\n00800EB0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800EC0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800ED0 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800EE0 : 01 01 00 53 61 2f 54 68 20 44 42 30 46 4c 57 20 | ...Sa/Th DB0FLW \n00800EF0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800F00 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800F10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800F20 : 01 00 00 44 42 30 4c 45 20 54 53 31 00 00 00 00 | ...DB0LE TS1....\n00800F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800F40 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800F50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800F60 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 45 20 54 | ...R/TG9 DB0LE T\n00800F70 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2..............\n00800F80 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00800F90 : cf 09 00 00 02 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800FA0 : 01 00 00 44 4c 20 44 42 30 41 46 5a 20 54 53 31 | ...DL DB0AFZ TS1\n00800FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800FC0 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00800FD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800FE0 : 01 00 00 42 42 20 44 42 30 41 46 5a 20 54 53 31 | ...BB DB0AFZ TS1\n00800FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801000 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00801010 : cf 09 00 00 05 00 00 00 00 00 03 ff 04 00 00 00 | ................\n00801020 : 01 01 00 54 47 39 20 44 42 30 41 46 5a 20 54 53 | ...TG9 DB0AFZ TS\n00801030 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00801040 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00801050 : cf 09 00 00 09 00 00 00 00 00 03 ff 04 00 00 00 | ................\n00801060 : 01 01 00 48 65 73 20 44 42 30 41 46 5a 20 54 53 | ...Hes DB0AFZ TS\n00801070 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00801080 : 43 34 50 00 00 00 00 00 09 00 00 00 00 00 00 00 | C4P.............\n00801090 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008010A0 : 01 00 00 44 4d 52 20 53 30 00 00 00 00 00 00 00 | ...DMR S0.......\n008010B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008010C0 : 43 36 12 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6..............\n008010D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008010E0 : 01 00 00 44 4d 52 20 53 31 00 00 00 00 00 00 00 | ...DMR S1.......\n008010F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801100 : 43 36 25 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6%.............\n00801110 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801120 : 01 00 00 44 4d 52 20 53 32 00 00 00 00 00 00 00 | ...DMR S2.......\n00801130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801140 : 43 36 38 00 00 00 00 00 09 00 00 00 00 00 00 00 | C68.............\n00801150 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801160 : 01 00 00 44 4d 52 20 53 33 00 00 00 00 00 00 00 | ...DMR S3.......\n00801170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801180 : 43 36 50 00 00 00 00 00 89 00 00 00 00 00 00 00 | C6P.............\n00801190 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008011A0 : 01 00 00 44 4d 52 20 53 34 00 00 00 00 00 00 00 | ...DMR S4.......\n008011B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008011C0 : 43 36 63 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6c.............\n008011D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008011E0 : 01 00 00 44 4d 52 20 53 35 00 00 00 00 00 00 00 | ...DMR S5.......\n008011F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801200 : 43 36 75 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6u.............\n00801210 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801220 : 01 00 00 44 4d 52 20 53 36 00 00 00 00 00 00 00 | ...DMR S6.......\n00801230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801240 : 43 36 88 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6..............\n00801250 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801260 : 01 00 00 44 4d 52 20 53 37 00 00 00 00 00 00 00 | ...DMR S7.......\n00801270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801280 : 43 95 62 50 00 76 00 00 88 05 01 01 00 00 00 00 | C.bP.v..........\n00801290 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................\n008012A0 : 00 00 00 44 42 30 4c 44 53 00 00 00 00 00 00 00 | ...DB0LDS.......\n008012B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008012C0 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v..........\n008012D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008012E0 : 00 00 00 44 42 30 52 41 47 00 00 00 00 00 00 00 | ...DB0RAG.......\n008012F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801300 : 43 85 75 00 00 76 00 00 88 05 01 01 00 00 00 00 | C.u..v..........\n00801310 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................\n00801320 : 00 00 00 44 42 30 4c 55 44 00 00 00 00 00 00 00 | ...DB0LUD.......\n00801330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801340 : 14 56 00 00 00 06 00 00 88 00 00 00 00 00 00 00 | .V..............\n00801350 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801360 : 00 00 00 44 42 30 53 50 2d 32 00 00 00 00 00 00 | ...DB0SP-2......\n00801370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801380 : 43 94 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801390 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008013A0 : 00 00 00 44 42 30 53 50 2d 37 30 00 00 00 00 00 | ...DB0SP-70.....\n008013B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008013C0 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............\n008013D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008013E0 : 00 00 00 44 42 30 5a 4f 44 2d 32 00 00 00 00 00 | ...DB0ZOD-2.....\n008013F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801400 : 43 87 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801410 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801420 : 00 00 00 44 42 30 5a 4f 44 2d 37 30 00 00 00 00 | ...DB0ZOD-70....\n00801430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801440 : 14 57 25 00 00 06 00 00 88 00 00 00 00 00 00 00 | .W%.............\n00801450 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801460 : 00 00 00 44 42 30 42 52 4c 00 00 00 00 00 00 00 | ...DB0BRL.......\n00801470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801480 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v..........\n00801490 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008014A0 : 00 00 00 44 42 30 42 4c 4f 00 00 00 00 00 00 00 | ...DB0BLO.......\n008014B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008014C0 : 43 90 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n008014D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008014E0 : 00 00 00 44 42 30 53 58 00 00 00 00 00 00 00 00 | ...DB0SX........\n008014F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801500 : 43 91 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801510 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801520 : 00 00 00 44 42 30 54 41 00 00 00 00 00 00 00 00 | ...DB0TA........\n00801530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801540 : 14 56 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Vu.............\n00801550 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801560 : 00 00 00 44 42 30 50 44 4d 00 00 00 00 00 00 00 | ...DB0PDM.......\n00801570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801580 : 43 88 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801590 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008015A0 : 00 00 00 44 42 30 43 42 53 00 00 00 00 00 00 00 | ...DB0CBS.......\n008015B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008015C0 : 43 86 37 50 00 76 00 00 88 00 00 00 00 00 00 00 | C.7P.v..........\n008015D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008015E0 : 00 00 00 44 42 30 41 46 00 00 00 00 00 00 00 00 | ...DB0AF........\n008015F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801600 : 43 89 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801610 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801620 : 00 00 00 44 42 30 4e 46 4c 00 00 00 00 00 00 00 | ...DB0NFL.......\n00801630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801640 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v..........\n00801650 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801660 : 00 00 00 44 42 30 4c 4d 4d 00 00 00 00 00 00 00 | ...DB0LMM.......\n00801670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801680 : 43 91 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801690 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008016A0 : 00 00 00 44 4d 30 4c 45 49 00 00 00 00 00 00 00 | ...DM0LEI.......\n008016B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008016C0 : 14 57 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Wu.............\n008016D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008016E0 : 00 00 00 44 42 30 4c 45 49 00 00 00 00 00 00 00 | ...DB0LEI.......\n008016F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801700 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v..........\n00801710 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801720 : 00 00 00 44 42 30 53 4d 4c 00 00 00 00 00 00 00 | ...DB0SML.......\n00801730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801740 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............\n00801750 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801760 : 00 00 00 44 42 30 4c 53 41 00 00 00 00 00 00 00 | ...DB0LSA.......\n00801770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801780 : 43 87 87 50 00 76 00 00 88 00 00 00 00 00 00 00 | C..P.v..........\n00801790 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008017A0 : 00 00 00 44 42 30 4c 45 00 00 00 00 00 00 00 00 | ...DB0LE........\n008017B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008017C0 : 14 46 75 00 00 00 00 00 48 00 00 00 00 00 00 00 | .Fu.....H.......\n008017D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008017E0 : 00 00 00 44 4f 4b 20 59 30 37 00 00 00 00 00 00 | ...DOK Y07......\n008017F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801800 : 14 46 50 00 00 00 00 00 08 00 00 00 00 00 00 00 | .FP.............\n00801810 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801820 : 00 00 00 44 4f 4b 20 44 32 30 00 00 00 00 00 00 | ...DOK D20......\n00801830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801840 : 14 53 75 00 00 00 00 00 08 00 00 00 00 00 00 00 | .Su.............\n00801850 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801860 : 00 00 00 44 4f 4b 20 44 32 33 00 00 00 00 00 00 | ...DOK D23......\n00801870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801880 : 14 47 25 00 00 00 00 00 08 00 00 00 00 00 00 00 | .G%.............\n00801890 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008018A0 : 00 00 00 44 4f 4b 20 53 33 31 00 00 00 00 00 00 | ...DOK S31......\n008018B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008018C0 : 14 53 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .S..............\n008018D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008018E0 : 00 00 00 53 32 30 00 00 00 00 00 00 00 00 00 00 | ...S20..........\n008018F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801900 : 14 55 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .U..............\n00801910 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801920 : 00 00 00 32 6d 20 4d 6f 62 69 6c 00 00 00 00 00 | ...2m Mobil.....\n00801930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801940 : 43 35 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | C5..............\n00801950 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801960 : 00 00 00 37 30 63 6d 20 4d 6f 62 69 6c 00 00 00 | ...70cm Mobil...\n00801970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801980 : 14 48 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .H..............\n00801990 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008019A0 : 00 00 00 41 50 52 53 00 00 00 00 00 00 00 00 00 | ...APRS.........\n008019B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008019C0 : 14 58 25 00 00 00 00 00 88 00 00 00 00 00 00 00 | .X%.............\n008019D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008019E0 : 00 00 00 49 53 53 20 41 50 52 53 00 00 00 00 00 | ...ISS APRS.....\n008019F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A00 : 43 70 00 00 29 10 10 00 88 04 01 00 00 00 00 00 | Cp..)...........\n00801A10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801A20 : 00 00 00 49 53 53 20 46 4d 20 52 65 70 00 00 00 | ...ISS FM Rep...\n00801A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A40 : 43 72 00 00 29 28 50 00 88 00 00 00 00 00 00 00 | Cr..)(P.........\n00801A50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801A60 : 00 00 00 43 41 53 2d 33 48 20 46 4d 00 00 00 00 | ...CAS-3H FM....\n00801A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A80 : 14 59 00 00 29 16 00 00 48 00 00 00 00 00 00 00 | .Y..)...H.......\n00801A90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801AA0 : 00 00 00 50 4f 2d 31 30 31 20 46 4d 00 00 00 00 | ...PO-101 FM....\n00801AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801AC0 : 43 67 90 00 29 09 40 00 88 04 01 00 00 00 00 00 | Cg..).@.........\n00801AD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801AE0 : 00 00 00 53 4f 2d 35 30 20 46 4d 00 00 00 00 00 | ...SO-50 FM.....\n00801AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B00 : 14 58 80 00 28 94 70 00 48 04 01 00 00 00 00 00 | .X..(.p.H.......\n00801B10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801B20 : 00 00 00 41 4f 2d 39 32 20 46 4d 00 00 00 00 00 | ...AO-92 FM.....\n00801B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B40 : 14 59 60 00 28 92 90 00 48 04 01 00 00 00 00 00 | .Y`.(...H.......\n00801B50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801B60 : 00 00 00 41 4f 2d 39 31 20 46 4d 00 00 00 00 00 | ...AO-91 FM.....\n00801B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B80 : 43 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | C...............\n00801B90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801BA0 : 00 00 00 55 30 00 00 00 00 00 00 00 00 00 00 00 | ...U0...........\n00801BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801BC0 : 43 02 25 00 00 00 00 00 40 00 00 00 00 00 00 00 | C.%.....@.......\n00801BD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801BE0 : 00 00 00 55 31 00 00 00 00 00 00 00 00 00 00 00 | ...U1...........\n00801BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C00 : 43 02 50 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.P.............\n00801C10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801C20 : 00 00 00 55 32 00 00 00 00 00 00 00 00 00 00 00 | ...U2...........\n00801C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C40 : 43 02 75 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.u.............\n00801C50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801C60 : 00 00 00 55 33 00 00 00 00 00 00 00 00 00 00 00 | ...U3...........\n00801C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C80 : 44 60 06 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@.......\n00801C90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801CA0 : 00 00 00 50 4d 52 20 31 00 00 00 00 00 00 00 00 | ...PMR 1........\n00801CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801CC0 : 44 60 18 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@.......\n00801CD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801CE0 : 00 00 00 50 4d 52 20 32 00 00 00 00 00 00 00 00 | ...PMR 2........\n00801CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D00 : 44 60 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`1%....@.......\n00801D10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801D20 : 00 00 00 50 4d 52 20 33 00 00 00 00 00 00 00 00 | ...PMR 3........\n00801D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D40 : 44 60 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | D`Cu............\n00801D50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801D60 : 00 00 00 50 4d 52 20 34 00 00 00 00 00 00 00 00 | ...PMR 4........\n00801D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D80 : 44 60 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | D`V%............\n00801D90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801DA0 : 00 00 00 50 4d 52 20 35 00 00 00 00 00 00 00 00 | ...PMR 5........\n00801DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801DC0 : 44 60 68 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`hu....@.......\n00801DD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801DE0 : 00 00 00 50 4d 52 20 36 00 00 00 00 00 00 00 00 | ...PMR 6........\n00801DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E00 : 44 60 81 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@.......\n00801E10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801E20 : 00 00 00 50 4d 52 20 37 00 00 00 00 00 00 00 00 | ...PMR 7........\n00801E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E40 : 44 60 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@.......\n00801E50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801E60 : 00 00 00 50 4d 52 20 38 00 00 00 00 00 00 00 00 | ...PMR 8........\n00801E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E80 : 44 61 06 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............\n00801E90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801EA0 : 00 00 00 50 4d 52 20 39 00 00 00 00 00 00 00 00 | ...PMR 9........\n00801EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801EC0 : 44 61 18 75 00 00 00 00 00 00 00 00 00 00 00 00 | Da.u............\n00801ED0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801EE0 : 00 00 00 50 4d 52 20 31 30 00 00 00 00 00 00 00 | ...PMR 10.......\n00801EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F00 : 44 61 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | Da1%....@.......\n00801F10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801F20 : 00 00 00 50 4d 52 20 31 31 00 00 00 00 00 00 00 | ...PMR 11.......\n00801F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F40 : 44 61 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | DaCu............\n00801F50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801F60 : 00 00 00 50 4d 52 20 31 32 00 00 00 00 00 00 00 | ...PMR 12.......\n00801F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F80 : 44 61 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | DaV%............\n00801F90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801FA0 : 00 00 00 50 4d 52 20 31 33 00 00 00 00 00 00 00 | ...PMR 13.......\n00801FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801FC0 : 44 61 68 75 00 00 00 00 00 00 00 00 00 00 00 00 | Dahu............\n00801FD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801FE0 : 00 00 00 50 4d 52 20 31 34 00 00 00 00 00 00 00 | ...PMR 14.......\n00801FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00840000 : 44 61 81 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............\n00840010 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00840020 : 00 00 00 50 4d 52 20 31 35 00 00 00 00 00 00 00 | ...PMR 15.......\n00840030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00840040 : 44 61 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | Da.u....@.......\n00840050 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00840060 : 00 00 00 50 4d 52 20 31 36 00 00 00 00 00 00 00 | ...PMR 16.......\n00840070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00842000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00FC0800 : 43 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | CQ%.............\n00FC0810 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &...............\n00FC0820 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 41 | ...Channel VFO A\n00FC0830 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC0840 : 14 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | .Q%.............\n00FC0850 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &...............\n00FC0860 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 42 | ...Channel VFO B\n00FC0870 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00FC2800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01000000 : 00 00 03 00 04 00 01 00 05 00 07 00 08 00 09 00 | ................\n01000010 : 0a 00 0b 00 0d 00 0e 00 4a 00 4b 00 4c 00 4d 00 | ........J.K.L.M.\n01000020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000200 : 5f 00 63 00 64 00 65 00 42 00 43 00 44 00 45 00 | _.c.d.e.B.C.D.E.\n01000210 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I.........\n01000220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000400 : 6e 00 6f 00 70 00 71 00 ff ff ff ff ff ff ff ff | n.o.p.q.........\n01000410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000600 : 3e 00 40 00 41 00 ff ff ff ff ff ff ff ff ff ff | >.@.A...........\n01000610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000800 : 64 00 42 00 43 00 44 00 45 00 46 00 47 00 48 00 | d.B.C.D.E.F.G.H.\n01000810 : 49 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff | I...............\n01000820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A00 : 0e 00 10 00 14 00 16 00 1a 00 1e 00 22 00 4d 00 | ............\".M.\n01000A10 : 4e 00 4f 00 50 00 51 00 52 00 53 00 54 00 ff ff | N.O.P.Q.R.S.T...\n01000A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C00 : 60 00 61 00 64 00 65 00 42 00 43 00 44 00 45 00 | `.a.d.e.B.C.D.E.\n01000C10 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I.........\n01000C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E00 : 23 00 24 00 25 00 26 00 27 00 28 00 29 00 2a 00 | #.$.%.&.'.(.).*.\n01000E10 : 4d 00 4e 00 55 00 ff ff ff ff ff ff ff ff ff ff | M.N.U...........\n01000E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001000 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G.\n01001010 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I.............\n01001020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001200 : 2b 00 2c 00 2d 00 2e 00 2f 00 30 00 31 00 32 00 | +.,.-.../.0.1.2.\n01001210 : 56 00 57 00 58 00 ff ff ff ff ff ff ff ff ff ff | V.W.X...........\n01001220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001400 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G.\n01001410 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I.............\n01001420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001600 : 33 00 35 00 36 00 37 00 38 00 3a 00 3b 00 3c 00 | 3.5.6.7.8.:.;.<.\n01001610 : 3d 00 59 00 5a 00 5b 00 5c 00 5d 00 5e 00 ff ff | =.Y.Z.[.\\.].^...\n01001620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001800 : 62 00 64 00 65 00 42 00 43 00 44 00 45 00 46 00 | b.d.e.B.C.D.E.F.\n01001810 : 47 00 48 00 49 00 ff ff ff ff ff ff ff ff ff ff | G.H.I...........\n01001820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A00 : 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff ff ff | h.m.l.k.j.i.....\n01001A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C00 : 67 00 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff | g.h.m.l.k.j.i...\n01001C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E00 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y.\n01001E10 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~.......\n01001E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002000 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y.\n01002010 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~.......\n01002020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n01040000 : 44 00 00 00 43 99 00 00 ff ff 00 00 00 00 00 00 | D...C...........\n01040010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040020 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040040 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040060 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01042000 : 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01042010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01042080 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01043000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01080000 : 00 01 00 00 ff ff 0f 00 19 00 1d 00 1d 00 00 4b | ...............K\n01080010 : 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | W...............\n01080020 : 03 00 04 00 4a 00 4b 00 5f 00 64 00 42 00 6e 00 | ....J.K._.d.B.n.\n01080030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080080 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01080200 : 00 00 ff ff ff ff 0f 00 19 00 1d 00 1d 00 00 42 | ...............B\n01080210 : 65 72 42 72 61 00 00 00 00 00 00 00 00 00 00 00 | erBra...........\n01080220 : 04 00 08 00 0e 00 10 00 14 00 16 00 1e 00 22 00 | ..............\".\n01080230 : 1a 00 26 00 2a 00 32 00 ff ff ff ff ff ff ff ff | ..&.*.2.........\n01080240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080280 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01640000 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640010 : 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640020 : 00 00 03 02 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640030 : 00 00 04 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640040 : 00 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01640800 : 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff | ................\n01640810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640860 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140000 : 48 65 6c 6c 6f 21 00 00 00 00 00 00 00 00 00 00 | Hello!..........\n02140010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140100 : 57 65 6c 63 6f 6d 65 21 00 00 00 00 00 00 00 00 | Welcome!........\n02140110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140200 : 54 68 61 6e 6b 20 79 6f 75 21 00 00 00 00 00 00 | Thank you!......\n02140210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140300 : 47 6f 6f 64 20 62 79 65 21 00 00 00 00 00 00 00 | Good bye!.......\n02140310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140400 : 48 61 70 70 79 20 65 76 65 72 79 20 64 61 79 21 | Happy every day!\n02140410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02480000 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02480200 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02480210 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02480220 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C0000 : 00 00 0e 46 12 34 56 78 9a bc de 00 00 00 00 00 | ...F.4Vx........\n024C0010 : 00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 00 | .........      .\n-----------------------------------------------------------------------------\n024C0C80 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C0D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1000 : c0 5d 68 29 50 2d 9c 31 b0 36 c4 3b 3c 41 7c 47 | .]h)P-.1.6.;<A|G\n024C1010 : 20 4e f0 55 60 6d a4 1f e4 25 92 22 90 65 54 24 |  N.U`m...%.\".eT$\n024C1020 : 00 00 00 05 46 01 02 03 04 05 00 00 14 00 64 14 | ....F.........d.\n024C1030 : 01 0d 0f 00 14 32 32 00 00 00 00 00 00 00 00 00 | .....22.........\n024C1040 : 00 00 00 46 00 00 00 00 00 00 00 00 00 00 00 00 | ...F............\n024C1050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1060 : 00 00 00 46 00 00 00 00 00 00 00 00 00 00 00 00 | ...F............\n024C1070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1080 : 0e 0a 00 32 14 64 00 00 01 01 14 00 00 00 00 00 | ...2.d..........\n024C1090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n024C1100 : 91 0c 41 24 00 00 00 00 00 00 00 00 00 00 00 00 | ..A$............\n-----------------------------------------------------------------------------\n024C1280 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1290 : 00 00 00 00 00 00 00 00 00 05 05 0a 0a 64 01 00 | .............d..\n-----------------------------------------------------------------------------\n024C1300 : ff ff 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1320 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1340 : 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1400 : 00 02 00 0a 0a 3c 00 00 01 01 00 0a 0a 3c 01 00 | .....<.......<..\n024C1410 : 01 01 09 09 00 01 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1440 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1460 : 00 00 00 12 34 56 78 00 00 00 00 00 00 00 00 00 | ....4Vx.........\n-----------------------------------------------------------------------------\n024C1500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C1510 : 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16F0 : 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1700 : 01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08 | ................\n024C1710 : 09 09 0a 0a 0b 0b 0c 0c 0d 0d 0e 0e 0f 0f 10 10 | ................\n024C1720 : 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 | ................\n024C1730 : 19 19 1a 1a 1b 1b 1c 1c 1d 1d 1e 1e 1f 1f 20 20 | ..............  \n-----------------------------------------------------------------------------\n024C1800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2000 : 60 ea 00 00 20 a1 07 00 00 00 00 00 00 00 00 00 | `... ...........\n024C2010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2400 : 91 0c 41 24 00 00 00 00 00 00 00 00 00 00 00 00 | ..A$............\n024C2410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2600 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C4000 : 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4020 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02500000 : 01 00 00 00 04 00 00 00 04 01 01 01 00 0f 01 02 | ................\n02500010 : 01 12 0c 13 08 00 00 01 02 05 05 05 05 00 01 00 | ................\n02500020 : 01 00 00 00 00 00 04 01 00 01 01 00 00 01 02 01 | ................\n02500030 : 14 00 01 01 00 00 00 02 01 01 00 05 00 00 00 00 | ................\n02500040 : 01 00 00 00 00 00 01 01 00 00 00 00 00 03 00 00 | ................\n02500050 : 02 01 05 00 00 00 00 00 00 5a 62 02 00 6c dc 02 | .........Zb..l..\n02500060 : 00 85 cf 00 c0 80 09 01 01 00 00 00 00 00 01 00 | ................\n02500070 : 00 01 bc 02 00 00 00 00 00 00 00 00 0f 00 00 00 | ................\n02500080 : 00 00 00 00 00 00 dc 05 58 02 00 00 00 00 00 00 | ........X.......\n02500090 : 0a 00 0a 00 00 00 00 00 00 00 6c 07 20 03 00 00 | ..........l. ...\n025000A0 : 00 00 00 00 0a 00 0a 00 00 00 00 00 00 00 00 01 | ................\n025000B0 : 00 00 00 00 00 05 01 01 00 00 00 00 06 00 0b 00 | ................\n025000C0 : 00 00 01 00 00 85 cf 00 c0 80 09 01 00 5a 62 02 | .............Zb.\n025000D0 : 00 6c dc 02 00 01 01 00 00 00 00 00 00 00 05 02 | .l..............\n025000E0 : 00 00 01 00 06 02 00 00 01 00 02 00 01 0a 00 00 | ................\n-----------------------------------------------------------------------------\n02500100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500600 : 4f 70 65 6e 20 47 44 37 37 00 00 00 00 00 00 00 | Open GD77.......\n02500610 : 44 4d 33 4d 41 54 00 00 00 00 00 00 00 00 00 00 | DM3MAT..........\n02500620 : 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 | 12345678........\n-----------------------------------------------------------------------------\n02501000 : ff 00 00 00 00 ff ff ff 00 00 ff ff ff ff ff ff | ................\n02501010 : ff ff ff ff ff 00 20 20 20 20 20 20 ff 20 20 20 | ......      .   \n02501020 : 20 20 20 ff 00 00 00 00 00 00 00 00 00 00 00 00 |    .............\n02501030 : 00 00 00 00 00 00 00 00 00 ff ff 03 ff 01 03 ff | ................\n02501040 : a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f | ................\n02501050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501070 : ff ff ff ff ff ff ff ff 01 00 00 00 00 00 00 00 | ................\n02501080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025012A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501420 : 00 00 01 00 00 85 cf 00 c0 80 09 01 00 5a 62 02 | .............Zb.\n02501430 : 00 6c dc 02 00 00 19 05 05 06 00 01 01 06 06 00 | .l..............\n02501440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02540000 : 4b 57 20 41 00 00 00 00 00 00 00 00 00 00 00 00 | KW A............\n02540010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540020 : 4b 57 20 42 00 00 00 00 00 00 00 00 00 00 00 00 | KW B............\n02540030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540040 : 54 48 20 57 69 6c 64 61 75 00 00 00 00 00 00 00 | TH Wildau.......\n02540050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540060 : 48 65 73 73 65 6e 20 41 00 00 00 00 00 00 00 00 | Hessen A........\n02540070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540080 : 48 65 73 73 65 6e 20 42 00 00 00 00 00 00 00 00 | Hessen B........\n02540090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400A0 : 42 65 72 6c 69 6e 20 41 00 00 00 00 00 00 00 00 | Berlin A........\n025400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400C0 : 42 65 72 6c 69 6e 20 42 00 00 00 00 00 00 00 00 | Berlin B........\n025400D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400E0 : 50 6f 74 73 64 61 6d 20 41 00 00 00 00 00 00 00 | Potsdam A.......\n025400F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540100 : 50 6f 74 73 64 61 6d 20 42 00 00 00 00 00 00 00 | Potsdam B.......\n02540110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540120 : 43 6f 74 74 62 75 73 20 41 00 00 00 00 00 00 00 | Cottbus A.......\n02540130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540140 : 43 6f 74 74 62 75 73 20 42 00 00 00 00 00 00 00 | Cottbus B.......\n02540150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540160 : 4c 65 69 70 7a 69 67 20 41 00 00 00 00 00 00 00 | Leipzig A.......\n02540170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540180 : 4c 65 69 70 7a 69 67 20 42 00 00 00 00 00 00 00 | Leipzig B.......\n02540190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401A0 : 53 61 74 20 41 00 00 00 00 00 00 00 00 00 00 00 | Sat A...........\n025401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401C0 : 53 61 74 20 42 00 00 00 00 00 00 00 00 00 00 00 | Sat B...........\n025401D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401E0 : 50 4d 52 20 41 00 00 00 00 00 00 00 00 00 00 00 | PMR A...........\n025401F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540200 : 50 4d 52 20 42 00 00 00 00 00 00 00 00 00 00 00 | PMR B...........\n02540210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02580000 : 02 62 13 70 00 44 4d 33 4d 41 54 00 00 00 00 00 | .b.p.DM3MAT.....\n02580010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n025C0000 : 00 ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 | ................\n025C0010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0100 : 53 74 61 74 75 73 20 4d 65 73 73 61 67 65 20 31 | Status Message 1\n025C0110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0500 : 01 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0530 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0560 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0590 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C05A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05C0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C05D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05F0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0620 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0650 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0680 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06B0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C06C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06E0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C06F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0710 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0740 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0770 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07A0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C07B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07D0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C07E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0800 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0830 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n025C0B00 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0B10 : 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ?...............\n025C0B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02600000 : 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 | ................\n02600010 : 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 | ................\n02600020 : 08 00 00 00 09 00 00 00 0a 00 00 00 0b 00 00 00 | ................\n02600030 : 0c 00 00 00 0d 00 00 00 0e 00 00 00 0f 00 00 00 | ................\n02600040 : 10 00 00 00 11 00 00 00 12 00 00 00 13 00 00 00 | ................\n02600050 : 14 00 00 00 15 00 00 00 16 00 00 00 17 00 00 00 | ................\n02600060 : 18 00 00 00 19 00 00 00 1a 00 00 00 ff ff ff ff | ................\n02600070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02640000 : 00 00 00 f8 ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404E0 : ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02680000 : 01 57 57 00 00 00 00 00 00 00 00 00 00 00 00 00 | .WW.............\n02680010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680020 : 00 00 00 00 00 00 91 00 00 00 00 00 00 00 00 00 | ................\n02680030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680060 : 00 00 00 00 01 45 55 00 00 00 00 00 00 00 00 00 | .....EU.........\n02680070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680080 : 00 00 00 00 00 00 00 00 00 00 92 00 00 00 00 00 | ................\n02680090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800C0 : 00 00 00 00 00 00 00 00 01 44 4c 00 00 00 00 00 | .........DL.....\n026800D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 02 62 00 | ..............b.\n026800F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680120 : 00 00 00 00 00 00 00 00 00 00 00 00 01 44 4c 20 | .............DL \n02680130 : 4d 4d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | MM..............\n02680140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680150 : 00 02 63 00 00 00 00 00 00 00 00 00 00 00 00 00 | ..c.............\n02680160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680190 : 01 52 65 67 69 6f 6e 61 6c 00 00 00 00 00 00 00 | .Regional.......\n026801A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801B0 : 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 | ................\n026801C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801F0 : 00 00 00 00 01 4c 6f 6b 61 6c 00 00 00 00 00 00 | .....Local......\n02680200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680210 : 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 | ................\n02680220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680250 : 00 00 00 00 00 00 00 00 01 42 6c 6e 2f 42 72 62 | .........Bln/Brb\n02680260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 26 21 00 | .............&!.\n02680280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802B0 : 00 00 00 00 00 00 00 00 00 00 00 00 01 53 61 2f | .............Sa/\n026802C0 : 54 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | Th..............\n026802D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802E0 : 00 26 29 00 00 00 00 00 00 00 00 00 00 00 00 00 | .&).............\n026802F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680320 : 01 53 41 2f 4d 56 00 00 00 00 00 00 00 00 00 00 | .SA/MV..........\n02680330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680340 : 00 00 00 00 00 26 20 00 00 00 00 00 00 00 00 00 | .....& .........\n02680350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680380 : 00 00 00 00 01 48 65 73 73 65 6e 00 00 00 00 00 | .....Hessen.....\n02680390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803A0 : 00 00 00 00 00 00 00 00 00 26 26 00 00 00 00 00 | .........&&.....\n026803B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803E0 : 00 00 00 00 00 00 00 00 02 41 6c 6c 20 43 61 6c | .........All Cal\n026803F0 : 6c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | l...............\n02680400 : 00 00 00 00 00 00 00 00 00 00 00 16 77 72 15 00 | ............wr..\n02680410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680440 : 00 00 00 00 00 00 00 00 00 00 00 00 01 53 69 6d | .............Sim\n02680450 : 70 6c 65 78 20 54 47 39 39 00 00 00 00 00 00 00 | plex TG99.......\n02680460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680470 : 00 00 99 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804B0 : 01 43 6c 20 42 72 61 6e 64 65 6e 62 75 72 67 00 | .Cl Brandenburg.\n026804C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804D0 : 00 00 00 00 02 62 09 00 00 00 00 00 00 00 00 00 | .....b..........\n026804E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680510 : 00 00 00 00 01 43 6c 20 42 65 72 6c 69 6e 00 00 | .....Cl Berlin..\n02680520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680530 : 00 00 00 00 00 00 00 00 02 62 12 00 00 00 00 00 | .........b......\n02680540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680570 : 00 00 00 00 00 00 00 00 00 52 65 66 6c 20 42 72 | .........Refl Br\n02680580 : 61 6e 64 65 6e 62 75 72 67 00 00 00 00 00 00 00 | andenburg.......\n02680590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 40 44 00 | .............@D.\n026805A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 52 65 66 | .............Ref\n026805E0 : 6c 20 42 65 72 6c 69 6e 00 00 00 00 00 00 00 00 | l Berlin........\n026805F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680600 : 00 40 16 00 00 00 00 00 00 00 00 00 00 00 00 00 | .@..............\n02680610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680640 : 00 42 4d 20 53 4d 53 00 00 00 00 00 00 00 00 00 | .BM SMS.........\n02680650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680660 : 00 00 00 00 26 29 93 00 00 00 00 00 00 00 00 00 | ....&)..........\n02680670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806A0 : 00 00 00 00 00 42 4d 20 44 41 50 4e 45 54 00 00 | .....BM DAPNET..\n026806B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806C0 : 00 00 00 00 00 00 00 00 26 29 94 00 00 00 00 00 | ........&)......\n026806D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680700 : 00 00 00 00 00 00 00 00 00 42 4d 20 50 61 72 72 | .........BM Parr\n02680710 : 6f 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ot..............\n02680720 : 00 00 00 00 00 00 00 00 00 00 00 00 26 29 97 00 | ............&)..\n02680730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 42 4d 20 | .............BM \n02680770 : 41 50 52 53 00 00 00 00 00 00 00 00 00 00 00 00 | APRS............\n02680780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680790 : 26 29 99 00 00 00 00 00 00 00 00 00 00 00 00 00 | &)..............\n026807A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807D0 : 00 44 47 32 52 4f 4e 20 52 6f 6e 6e 79 00 00 00 | .DG2RON Ronny...\n026807E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807F0 : 00 00 00 02 62 12 46 01 00 00 00 00 00 00 00 00 | ....b.F.........\n02680800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680830 : 00 00 00 00 00 44 4d 39 4b 53 20 53 69 6c 76 69 | .....DM9KS Silvi\n02680840 : 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | o...............\n02680850 : 00 00 00 00 00 00 00 02 62 10 45 01 00 00 00 00 | ........b.E.....\n02680860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680890 : 00 00 00 00 00 00 00 00 00 44 4d 33 4d 41 54 20 | .........DM3MAT \n026808A0 : 48 61 6e 6e 65 73 00 00 00 00 00 00 00 00 00 00 | Hannes..........\n026808B0 : 00 00 00 00 00 00 00 00 00 00 00 02 62 13 70 01 | ............b.p.\n026808C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 44 4c 32 | .............DL2\n02680900 : 42 51 46 20 54 68 6f 6d 61 73 00 00 00 00 00 00 | BQF Thomas......\n02680910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 | ................\n02680920 : 62 14 37 01 00 00 00 00 00 00 00 00 00 00 00 00 | b.7.............\n02680930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680960 : 00 44 4c 37 4a 4f 4d 20 4f 6c 61 66 00 00 00 00 | .DL7JOM Olaf....\n02680970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680980 : 00 00 00 02 62 10 04 01 00 00 00 00 00 00 00 00 | ....b...........\n02680990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809C0 : 00 00 00 00 00 44 47 31 52 50 48 20 50 65 74 65 | .....DG1RPH Pete\n026809D0 : 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | r...............\n026809E0 : 00 00 00 00 00 00 00 02 62 15 28 01 00 00 00 00 | ........b.(.....\n026809F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A20 : 00 00 00 00 00 00 00 00 00 44 4f 31 52 50 48 20 | .........DO1RPH \n02680A30 : 52 69 63 68 61 72 64 00 00 00 00 00 00 00 00 00 | Richard.........\n02680A40 : 00 00 00 00 00 00 00 00 00 00 00 02 63 41 60 00 | ............cA`.\n02680A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02900000 : 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02900100 : 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02940000 : 12 34 50 00 00 00 00 05 43 6f 6e 74 61 63 74 31 | .4P.....Contact1\n02940010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980000 : 00 00 00 00 01 00 00 00 02 00 00 00 ff ff ff ff | ................\n02980010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980100 : 44 4c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | DL..............\n02980110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980200 : 05 00 00 00 04 00 00 00 06 00 00 00 ff ff ff ff | ................\n02980210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980300 : 42 65 72 6c 69 6e 2f 42 72 61 6e 64 00 00 00 00 | Berlin/Brand....\n02980310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980400 : 04 00 00 00 05 00 00 00 07 00 00 00 ff ff ff ff | ................\n02980410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980500 : 53 61 2f 54 68 00 00 00 00 00 00 00 00 00 00 00 | Sa/Th...........\n02980510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980600 : 04 00 00 00 05 00 00 00 08 00 00 00 ff ff ff ff | ................\n02980610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980700 : 53 41 2f 4d 56 00 00 00 00 00 00 00 00 00 00 00 | SA/MV...........\n02980710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980800 : 04 00 00 00 05 00 00 00 09 00 00 00 ff ff ff ff | ................\n02980810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980900 : 48 65 73 73 65 6e 00 00 00 00 00 00 00 00 00 00 | Hessen..........\n02980910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980A00 : 05 00 00 00 0b 00 00 00 0a 00 00 00 ff ff ff ff | ................\n02980A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980B00 : 53 69 6d 70 6c 65 78 00 00 00 00 00 00 00 00 00 | Simplex.........\n02980B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n04340000 : 11 00 00 00 04 00 00 00 13 00 00 00 05 00 00 00 | ................\n04340010 : 23 01 00 00 00 00 00 00 25 01 00 00 01 00 00 00 | #.......%.......\n04340020 : 33 01 00 00 0b 00 00 00 c5 04 00 00 02 00 00 00 | 3...............\n04340030 : c7 04 00 00 03 00 00 00 41 4c 00 00 08 00 00 00 | ........AL......\n04340040 : 43 4c 00 00 06 00 00 00 4d 4c 00 00 09 00 00 00 | CL......ML......\n04340050 : 53 4c 00 00 07 00 00 00 2c 80 00 00 0f 00 00 00 | SL......,.......\n04340060 : 88 80 00 00 0e 00 00 00 13 c4 04 00 0c 00 00 00 | ................\n04340070 : 25 c4 04 00 0d 00 00 00 26 53 4c 00 10 00 00 00 | %.......&SL.....\n04340080 : 28 53 4c 00 11 00 00 00 2e 53 4c 00 12 00 00 00 | (SL......SL.....\n04340090 : 32 53 4c 00 13 00 00 00 08 20 c4 04 18 00 00 00 | 2SL...... ......\n043400A0 : 8a 20 c4 04 15 00 00 00 8c 24 c4 04 14 00 00 00 | . .......$......\n043400B0 : e0 26 c4 04 16 00 00 00 6e 28 c4 04 17 00 00 00 | .&......n(......\n043400C0 : 50 2a c4 04 19 00 00 00 c0 82 c6 04 1a 00 00 00 | P*..............\n043400D0 : 2a e4 ee 2c 0a 00 00 00 ff ff ff ff ff ff ff ff | *..,............\n"
  },
  {
    "path": "doc/reveng/anytone/d878uv/d878uv_set_gps_on.hex",
    "content": "-----------------------------------------------------------------------------\n00800000 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800010 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800020 : 01 00 00 44 42 30 4c 44 53 20 54 53 31 00 00 00 | ...DB0LDS TS1...\n00800030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800040 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800050 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800060 : 01 00 00 53 61 2f 54 68 20 44 42 30 4c 44 53 20 | ...Sa/Th DB0LDS \n00800070 : 54 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS1.............\n00800080 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800090 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008000A0 : 01 01 00 54 47 38 20 44 42 30 4c 44 53 20 54 53 | ...TG8 DB0LDS TS\n008000B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008000C0 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n008000D0 : cf 09 00 00 05 00 00 00 00 00 03 00 01 00 00 00 | ................\n008000E0 : 01 01 00 54 47 39 20 44 42 30 4c 44 53 20 54 53 | ...TG9 DB0LDS TS\n008000F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800100 : 43 95 62 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.bP.v..........\n00800110 : cf 09 00 00 06 00 00 00 00 00 03 00 01 00 00 00 | ................\n00800120 : 01 01 00 42 42 20 44 42 30 4c 44 53 20 54 53 32 | ...BB DB0LDS TS2\n00800130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800140 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800150 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800160 : 01 00 00 44 4d 30 54 5a 4e 20 54 53 31 00 00 00 | ...DM0TZN TS1...\n00800170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800180 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800190 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008001A0 : 01 01 00 54 47 38 20 44 4d 30 54 5a 4e 20 54 53 | ...TG8 DM0TZN TS\n008001B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008001C0 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008001D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008001E0 : 01 01 00 54 47 39 20 44 4d 30 54 5a 4e 20 54 53 | ...TG9 DM0TZN TS\n008001F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800200 : 43 88 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800210 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800220 : 01 01 00 42 42 20 44 4d 30 54 5a 4e 20 54 53 32 | ...BB DM0TZN TS2\n00800230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800240 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800250 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800260 : 01 00 00 44 42 30 4c 4f 53 20 54 53 31 00 00 00 | ...DB0LOS TS1...\n00800270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800280 : 43 84 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800290 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008002A0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 4f 53 20 | ...R/TG9 DB0LOS \n008002B0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n008002C0 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n008002D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008002E0 : 01 00 00 52 2f 54 47 39 20 44 4d 30 54 54 20 54 | ...R/TG9 DM0TT T\n008002F0 : 53 31 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S1..............\n00800300 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800310 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800320 : 01 01 00 54 47 38 20 44 4d 30 54 54 20 54 53 32 | ...TG8 DM0TT TS2\n00800330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800340 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800350 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800360 : 01 01 00 54 47 39 20 44 4d 30 54 54 20 54 53 32 | ...TG9 DM0TT TS2\n00800370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800380 : 43 90 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800390 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008003A0 : 01 01 00 42 42 20 44 4d 30 54 54 20 54 53 32 00 | ...BB DM0TT TS2.\n008003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008003C0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n008003D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008003E0 : 01 00 00 54 47 39 20 44 42 30 4b 4b 20 54 53 31 | ...TG9 DB0KK TS1\n008003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800400 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800410 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800420 : 01 01 00 42 42 20 44 42 30 4b 4b 20 54 53 32 00 | ...BB DB0KK TS2.\n00800430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800440 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800450 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800460 : 01 00 00 44 42 30 4f 55 44 20 54 53 31 00 00 00 | ...DB0OUD TS1...\n00800470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800480 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800490 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008004A0 : 01 01 00 54 47 38 20 44 42 30 4f 55 44 20 54 53 | ...TG8 DB0OUD TS\n008004B0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n008004C0 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008004D0 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008004E0 : 01 01 00 54 47 39 20 44 42 30 4f 55 44 20 54 53 | ...TG9 DB0OUD TS\n008004F0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800500 : 43 86 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800510 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800520 : 01 01 00 42 42 20 44 42 30 4f 55 44 20 54 53 32 | ...BB DB0OUD TS2\n00800530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800540 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800550 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800560 : 01 00 00 54 47 39 20 44 42 30 54 55 20 54 53 31 | ...TG9 DB0TU TS1\n00800570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800580 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800590 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008005A0 : 01 01 00 42 42 20 44 42 30 54 55 20 54 53 32 00 | ...BB DB0TU TS2.\n008005B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008005C0 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n008005D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008005E0 : 01 00 00 44 4d 30 4d 4f 54 20 54 53 31 00 00 00 | ...DM0MOT TS1...\n008005F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800600 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800610 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800620 : 01 01 00 54 47 38 20 44 4d 30 4d 4f 54 20 54 53 | ...TG8 DM0MOT TS\n00800630 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800640 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800650 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800660 : 01 01 00 54 47 39 20 44 4d 30 4d 4f 54 20 54 53 | ...TG9 DM0MOT TS\n00800670 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800680 : 43 95 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800690 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008006A0 : 01 01 00 42 42 20 44 4d 30 4d 4f 54 20 54 53 32 | ...BB DM0MOT TS2\n008006B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008006C0 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n008006D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008006E0 : 01 00 00 44 42 30 54 41 20 54 53 31 00 00 00 00 | ...DB0TA TS1....\n008006F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800700 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800710 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800720 : 01 01 00 54 47 38 20 44 42 30 54 41 20 54 53 32 | ...TG8 DB0TA TS2\n00800730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800740 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800750 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800760 : 01 01 00 54 47 39 20 44 42 30 54 41 20 54 53 32 | ...TG9 DB0TA TS2\n00800770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800780 : 43 94 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800790 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008007A0 : 01 01 00 42 42 20 44 42 30 54 41 20 54 53 32 00 | ...BB DB0TA TS2.\n008007B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008007C0 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n008007D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008007E0 : 01 00 00 44 42 30 46 58 20 54 53 31 00 00 00 00 | ...DB0FX TS1....\n008007F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800800 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800810 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800820 : 01 01 00 54 47 38 20 44 42 30 46 58 20 54 53 32 | ...TG8 DB0FX TS2\n00800830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800840 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800850 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800860 : 01 01 00 54 47 39 20 44 42 30 46 58 20 54 53 32 | ...TG9 DB0FX TS2\n00800870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800880 : 43 94 50 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.P..v..........\n00800890 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008008A0 : 01 01 00 42 42 20 44 42 30 46 58 20 54 53 32 00 | ...BB DB0FX TS2.\n008008B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008008C0 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n008008D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008008E0 : 01 00 00 44 42 30 50 44 4d 20 54 53 31 00 00 00 | ...DB0PDM TS1...\n008008F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800900 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800910 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800920 : 01 01 00 54 47 38 20 44 42 30 50 44 4d 20 54 53 | ...TG8 DB0PDM TS\n00800930 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800940 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800950 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800960 : 01 01 00 54 47 39 20 44 42 30 50 44 4d 20 54 53 | ...TG9 DB0PDM TS\n00800970 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800980 : 43 84 00 00 00 76 00 00 89 00 00 00 00 00 00 00 | C....v..........\n00800990 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n008009A0 : 01 01 00 42 42 20 44 42 30 50 44 4d 20 54 53 32 | ...BB DB0PDM TS2\n008009B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008009C0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n008009D0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n008009E0 : 01 00 00 44 42 30 42 52 42 20 54 53 31 00 00 00 | ...DB0BRB TS1...\n008009F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800A00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800A20 : 01 01 00 54 47 38 20 44 42 30 42 52 42 20 54 53 | ...TG8 DB0BRB TS\n00800A30 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800A40 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800A60 : 01 01 00 54 47 39 20 44 42 30 42 52 42 20 54 53 | ...TG9 DB0BRB TS\n00800A70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800A80 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800A90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800AA0 : 01 01 00 42 42 20 44 42 30 42 52 42 20 54 53 32 | ...BB DB0BRB TS2\n00800AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800AC0 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800AD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800AE0 : 01 00 00 44 42 30 53 50 4e 20 54 53 31 00 00 00 | ...DB0SPN TS1...\n00800AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800B00 : 43 94 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B10 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800B20 : 01 00 00 52 2f 54 47 39 20 44 42 30 53 50 4e 20 | ...R/TG9 DB0SPN \n00800B30 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800B40 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B50 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800B60 : 01 00 00 44 42 30 4e 4c 53 20 54 53 31 00 00 00 | ...DB0NLS TS1...\n00800B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800B80 : 43 95 12 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800B90 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800BA0 : 01 01 00 52 2f 54 47 39 20 44 42 30 4e 4c 53 20 | ...R/TG9 DB0NLS \n00800BB0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800BC0 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800BD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800BE0 : 01 00 00 44 42 30 4c 53 20 54 53 31 00 00 00 00 | ...DB0LS TS1....\n00800BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C00 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C10 : cf 09 00 00 04 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800C20 : 01 01 00 54 47 38 20 44 42 30 4c 53 20 54 53 32 | ...TG8 DB0LS TS2\n00800C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C40 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C50 : cf 09 00 00 05 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800C60 : 01 01 00 54 47 39 20 44 42 30 4c 53 20 54 53 32 | ...TG9 DB0LS TS2\n00800C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800C80 : 43 82 25 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.%..v..........\n00800C90 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800CA0 : 01 01 00 42 42 20 44 42 30 4c 53 20 54 53 32 00 | ...BB DB0LS TS2.\n00800CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800CC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800CD0 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800CE0 : 01 00 00 44 4d 30 4c 43 20 54 53 31 00 00 00 00 | ...DM0LC TS1....\n00800CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D00 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D10 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800D20 : 01 01 00 54 47 38 20 44 4d 30 4c 43 20 54 53 32 | ...TG8 DM0LC TS2\n00800D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D40 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800D60 : 01 01 00 54 47 39 20 44 4d 30 4c 43 20 54 53 32 | ...TG9 DM0LC TS2\n00800D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800D80 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800D90 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800DA0 : 01 01 00 53 61 2f 54 68 20 44 4d 30 4c 43 20 54 | ...Sa/Th DM0LC T\n00800DB0 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2..............\n00800DC0 : 43 93 87 50 00 76 00 00 89 00 00 00 00 00 00 00 | C..P.v..........\n00800DD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800DE0 : 01 00 00 42 42 20 44 4d 30 4c 43 20 54 53 31 00 | ...BB DM0LC TS1.\n00800DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800E00 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800E20 : 01 00 00 44 42 30 46 4c 57 20 54 53 31 00 00 00 | ...DB0FLW TS1...\n00800E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800E40 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E50 : cf 09 00 00 04 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800E60 : 01 01 00 54 47 38 20 44 42 30 46 4c 57 20 54 53 | ...TG8 DB0FLW TS\n00800E70 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800E80 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800E90 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800EA0 : 01 01 00 54 47 39 20 44 42 30 46 4c 57 20 54 53 | ...TG9 DB0FLW TS\n00800EB0 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00800EC0 : 43 95 37 50 00 76 00 00 89 00 00 00 00 00 00 00 | C.7P.v..........\n00800ED0 : cf 09 00 00 07 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800EE0 : 01 01 00 53 61 2f 54 68 20 44 42 30 46 4c 57 20 | ...Sa/Th DB0FLW \n00800EF0 : 54 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 | TS2.............\n00800F00 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800F10 : cf 09 00 00 00 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800F20 : 01 00 00 44 42 30 4c 45 20 54 53 31 00 00 00 00 | ...DB0LE TS1....\n00800F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800F40 : 43 95 75 00 00 76 00 00 89 00 00 00 00 00 00 00 | C.u..v..........\n00800F50 : cf 09 00 00 05 00 00 00 00 00 03 ff 02 00 00 00 | ................\n00800F60 : 01 01 00 52 2f 54 47 39 20 44 42 30 4c 45 20 54 | ...R/TG9 DB0LE T\n00800F70 : 53 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | S2..............\n00800F80 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00800F90 : cf 09 00 00 02 00 00 00 00 00 03 ff 00 00 00 00 | ................\n00800FA0 : 01 00 00 44 4c 20 44 42 30 41 46 5a 20 54 53 31 | ...DL DB0AFZ TS1\n00800FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00800FC0 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00800FD0 : cf 09 00 00 06 00 00 00 00 00 03 ff 01 00 00 00 | ................\n00800FE0 : 01 00 00 42 42 20 44 42 30 41 46 5a 20 54 53 31 | ...BB DB0AFZ TS1\n00800FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801000 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00801010 : cf 09 00 00 05 00 00 00 00 00 03 ff 04 00 00 00 | ................\n00801020 : 01 01 00 54 47 39 20 44 42 30 41 46 5a 20 54 53 | ...TG9 DB0AFZ TS\n00801030 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00801040 : 43 99 75 00 00 94 00 00 89 00 00 00 00 00 00 00 | C.u.............\n00801050 : cf 09 00 00 09 00 00 00 00 00 03 ff 04 00 00 00 | ................\n00801060 : 01 01 00 48 65 73 20 44 42 30 41 46 5a 20 54 53 | ...Hes DB0AFZ TS\n00801070 : 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 2...............\n00801080 : 43 34 50 00 00 00 00 00 09 00 00 00 00 00 00 00 | C4P.............\n00801090 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008010A0 : 01 00 00 44 4d 52 20 53 30 00 00 00 00 00 00 00 | ...DMR S0.......\n008010B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008010C0 : 43 36 12 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6..............\n008010D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008010E0 : 01 00 00 44 4d 52 20 53 31 00 00 00 00 00 00 00 | ...DMR S1.......\n008010F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801100 : 43 36 25 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6%.............\n00801110 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801120 : 01 00 00 44 4d 52 20 53 32 00 00 00 00 00 00 00 | ...DMR S2.......\n00801130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801140 : 43 36 38 00 00 00 00 00 09 00 00 00 00 00 00 00 | C68.............\n00801150 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801160 : 01 00 00 44 4d 52 20 53 33 00 00 00 00 00 00 00 | ...DMR S3.......\n00801170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801180 : 43 36 50 00 00 00 00 00 89 00 00 00 00 00 00 00 | C6P.............\n00801190 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008011A0 : 01 00 00 44 4d 52 20 53 34 00 00 00 00 00 00 00 | ...DMR S4.......\n008011B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008011C0 : 43 36 63 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6c.............\n008011D0 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n008011E0 : 01 00 00 44 4d 52 20 53 35 00 00 00 00 00 00 00 | ...DMR S5.......\n008011F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801200 : 43 36 75 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6u.............\n00801210 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801220 : 01 00 00 44 4d 52 20 53 36 00 00 00 00 00 00 00 | ...DMR S6.......\n00801230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801240 : 43 36 88 00 00 00 00 00 09 00 00 00 00 00 00 00 | C6..............\n00801250 : cf 09 00 00 0a 00 00 00 00 00 01 ff 05 00 00 00 | ................\n00801260 : 01 00 00 44 4d 52 20 53 37 00 00 00 00 00 00 00 | ...DMR S7.......\n00801270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801280 : 43 95 62 50 00 76 00 00 88 05 01 01 00 00 00 00 | C.bP.v..........\n00801290 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................\n008012A0 : 00 00 00 44 42 30 4c 44 53 00 00 00 00 00 00 00 | ...DB0LDS.......\n008012B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008012C0 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v..........\n008012D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008012E0 : 00 00 00 44 42 30 52 41 47 00 00 00 00 00 00 00 | ...DB0RAG.......\n008012F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801300 : 43 85 75 00 00 76 00 00 88 05 01 01 00 00 00 00 | C.u..v..........\n00801310 : cf 09 00 00 00 00 00 00 00 10 00 ff ff 00 00 00 | ................\n00801320 : 00 00 00 44 42 30 4c 55 44 00 00 00 00 00 00 00 | ...DB0LUD.......\n00801330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801340 : 14 56 00 00 00 06 00 00 88 00 00 00 00 00 00 00 | .V..............\n00801350 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801360 : 00 00 00 44 42 30 53 50 2d 32 00 00 00 00 00 00 | ...DB0SP-2......\n00801370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801380 : 43 94 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801390 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008013A0 : 00 00 00 44 42 30 53 50 2d 37 30 00 00 00 00 00 | ...DB0SP-70.....\n008013B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008013C0 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............\n008013D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008013E0 : 00 00 00 44 42 30 5a 4f 44 2d 32 00 00 00 00 00 | ...DB0ZOD-2.....\n008013F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801400 : 43 87 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801410 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801420 : 00 00 00 44 42 30 5a 4f 44 2d 37 30 00 00 00 00 | ...DB0ZOD-70....\n00801430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801440 : 14 57 25 00 00 06 00 00 88 00 00 00 00 00 00 00 | .W%.............\n00801450 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801460 : 00 00 00 44 42 30 42 52 4c 00 00 00 00 00 00 00 | ...DB0BRL.......\n00801470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801480 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v..........\n00801490 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008014A0 : 00 00 00 44 42 30 42 4c 4f 00 00 00 00 00 00 00 | ...DB0BLO.......\n008014B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008014C0 : 43 90 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n008014D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008014E0 : 00 00 00 44 42 30 53 58 00 00 00 00 00 00 00 00 | ...DB0SX........\n008014F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801500 : 43 91 25 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.%..v..........\n00801510 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801520 : 00 00 00 44 42 30 54 41 00 00 00 00 00 00 00 00 | ...DB0TA........\n00801530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801540 : 14 56 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Vu.............\n00801550 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801560 : 00 00 00 44 42 30 50 44 4d 00 00 00 00 00 00 00 | ...DB0PDM.......\n00801570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801580 : 43 88 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801590 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008015A0 : 00 00 00 44 42 30 43 42 53 00 00 00 00 00 00 00 | ...DB0CBS.......\n008015B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008015C0 : 43 86 37 50 00 76 00 00 88 00 00 00 00 00 00 00 | C.7P.v..........\n008015D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008015E0 : 00 00 00 44 42 30 41 46 00 00 00 00 00 00 00 00 | ...DB0AF........\n008015F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801600 : 43 89 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801610 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801620 : 00 00 00 44 42 30 4e 46 4c 00 00 00 00 00 00 00 | ...DB0NFL.......\n00801630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801640 : 43 93 00 00 00 76 00 00 88 00 00 00 00 00 00 00 | C....v..........\n00801650 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801660 : 00 00 00 44 42 30 4c 4d 4d 00 00 00 00 00 00 00 | ...DB0LMM.......\n00801670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801680 : 43 91 50 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.P..v..........\n00801690 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008016A0 : 00 00 00 44 4d 30 4c 45 49 00 00 00 00 00 00 00 | ...DM0LEI.......\n008016B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008016C0 : 14 57 75 00 00 06 00 00 88 00 00 00 00 00 00 00 | .Wu.............\n008016D0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008016E0 : 00 00 00 44 42 30 4c 45 49 00 00 00 00 00 00 00 | ...DB0LEI.......\n008016F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801700 : 43 92 75 00 00 76 00 00 88 00 00 00 00 00 00 00 | C.u..v..........\n00801710 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801720 : 00 00 00 44 42 30 53 4d 4c 00 00 00 00 00 00 00 | ...DB0SML.......\n00801730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801740 : 14 56 62 50 00 06 00 00 88 00 00 00 00 00 00 00 | .VbP............\n00801750 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801760 : 00 00 00 44 42 30 4c 53 41 00 00 00 00 00 00 00 | ...DB0LSA.......\n00801770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801780 : 43 87 87 50 00 76 00 00 88 00 00 00 00 00 00 00 | C..P.v..........\n00801790 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n008017A0 : 00 00 00 44 42 30 4c 45 00 00 00 00 00 00 00 00 | ...DB0LE........\n008017B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008017C0 : 14 46 75 00 00 00 00 00 48 00 00 00 00 00 00 00 | .Fu.....H.......\n008017D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008017E0 : 00 00 00 44 4f 4b 20 59 30 37 00 00 00 00 00 00 | ...DOK Y07......\n008017F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801800 : 14 46 50 00 00 00 00 00 08 00 00 00 00 00 00 00 | .FP.............\n00801810 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801820 : 00 00 00 44 4f 4b 20 44 32 30 00 00 00 00 00 00 | ...DOK D20......\n00801830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801840 : 14 53 75 00 00 00 00 00 08 00 00 00 00 00 00 00 | .Su.............\n00801850 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801860 : 00 00 00 44 4f 4b 20 44 32 33 00 00 00 00 00 00 | ...DOK D23......\n00801870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801880 : 14 47 25 00 00 00 00 00 08 00 00 00 00 00 00 00 | .G%.............\n00801890 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008018A0 : 00 00 00 44 4f 4b 20 53 33 31 00 00 00 00 00 00 | ...DOK S31......\n008018B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008018C0 : 14 53 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .S..............\n008018D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008018E0 : 00 00 00 53 32 30 00 00 00 00 00 00 00 00 00 00 | ...S20..........\n008018F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801900 : 14 55 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .U..............\n00801910 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801920 : 00 00 00 32 6d 20 4d 6f 62 69 6c 00 00 00 00 00 | ...2m Mobil.....\n00801930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801940 : 43 35 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | C5..............\n00801950 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801960 : 00 00 00 37 30 63 6d 20 4d 6f 62 69 6c 00 00 00 | ...70cm Mobil...\n00801970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801980 : 14 48 00 00 00 00 00 00 08 00 00 00 00 00 00 00 | .H..............\n00801990 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008019A0 : 00 00 00 41 50 52 53 00 00 00 00 00 00 00 00 00 | ...APRS.........\n008019B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008019C0 : 14 58 25 00 00 00 00 00 88 00 00 00 00 00 00 00 | .X%.............\n008019D0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n008019E0 : 00 00 00 49 53 53 20 41 50 52 53 00 00 00 00 00 | ...ISS APRS.....\n008019F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A00 : 43 70 00 00 29 10 10 00 88 04 01 00 00 00 00 00 | Cp..)...........\n00801A10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801A20 : 00 00 00 49 53 53 20 46 4d 20 52 65 70 00 00 00 | ...ISS FM Rep...\n00801A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A40 : 43 72 00 00 29 28 50 00 88 00 00 00 00 00 00 00 | Cr..)(P.........\n00801A50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801A60 : 00 00 00 43 41 53 2d 33 48 20 46 4d 00 00 00 00 | ...CAS-3H FM....\n00801A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801A80 : 14 59 00 00 29 16 00 00 48 00 00 00 00 00 00 00 | .Y..)...H.......\n00801A90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801AA0 : 00 00 00 50 4f 2d 31 30 31 20 46 4d 00 00 00 00 | ...PO-101 FM....\n00801AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801AC0 : 43 67 90 00 29 09 40 00 88 04 01 00 00 00 00 00 | Cg..).@.........\n00801AD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801AE0 : 00 00 00 53 4f 2d 35 30 20 46 4d 00 00 00 00 00 | ...SO-50 FM.....\n00801AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B00 : 14 58 80 00 28 94 70 00 48 04 01 00 00 00 00 00 | .X..(.p.H.......\n00801B10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801B20 : 00 00 00 41 4f 2d 39 32 20 46 4d 00 00 00 00 00 | ...AO-92 FM.....\n00801B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B40 : 14 59 60 00 28 92 90 00 48 04 01 00 00 00 00 00 | .Y`.(...H.......\n00801B50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801B60 : 00 00 00 41 4f 2d 39 31 20 46 4d 00 00 00 00 00 | ...AO-91 FM.....\n00801B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801B80 : 43 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | C...............\n00801B90 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801BA0 : 00 00 00 55 30 00 00 00 00 00 00 00 00 00 00 00 | ...U0...........\n00801BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801BC0 : 43 02 25 00 00 00 00 00 40 00 00 00 00 00 00 00 | C.%.....@.......\n00801BD0 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801BE0 : 00 00 00 55 31 00 00 00 00 00 00 00 00 00 00 00 | ...U1...........\n00801BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C00 : 43 02 50 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.P.............\n00801C10 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801C20 : 00 00 00 55 32 00 00 00 00 00 00 00 00 00 00 00 | ...U2...........\n00801C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C40 : 43 02 75 00 00 00 00 00 00 00 00 00 00 00 00 00 | C.u.............\n00801C50 : cf 09 00 00 00 00 00 00 00 00 00 ff ff 00 00 00 | ................\n00801C60 : 00 00 00 55 33 00 00 00 00 00 00 00 00 00 00 00 | ...U3...........\n00801C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801C80 : 44 60 06 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@.......\n00801C90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801CA0 : 00 00 00 50 4d 52 20 31 00 00 00 00 00 00 00 00 | ...PMR 1........\n00801CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801CC0 : 44 60 18 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@.......\n00801CD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801CE0 : 00 00 00 50 4d 52 20 32 00 00 00 00 00 00 00 00 | ...PMR 2........\n00801CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D00 : 44 60 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`1%....@.......\n00801D10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801D20 : 00 00 00 50 4d 52 20 33 00 00 00 00 00 00 00 00 | ...PMR 3........\n00801D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D40 : 44 60 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | D`Cu............\n00801D50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801D60 : 00 00 00 50 4d 52 20 34 00 00 00 00 00 00 00 00 | ...PMR 4........\n00801D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801D80 : 44 60 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | D`V%............\n00801D90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801DA0 : 00 00 00 50 4d 52 20 35 00 00 00 00 00 00 00 00 | ...PMR 5........\n00801DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801DC0 : 44 60 68 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`hu....@.......\n00801DD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801DE0 : 00 00 00 50 4d 52 20 36 00 00 00 00 00 00 00 00 | ...PMR 6........\n00801DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E00 : 44 60 81 25 00 00 00 00 40 00 00 00 00 00 00 00 | D`.%....@.......\n00801E10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801E20 : 00 00 00 50 4d 52 20 37 00 00 00 00 00 00 00 00 | ...PMR 7........\n00801E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E40 : 44 60 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | D`.u....@.......\n00801E50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801E60 : 00 00 00 50 4d 52 20 38 00 00 00 00 00 00 00 00 | ...PMR 8........\n00801E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801E80 : 44 61 06 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............\n00801E90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801EA0 : 00 00 00 50 4d 52 20 39 00 00 00 00 00 00 00 00 | ...PMR 9........\n00801EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801EC0 : 44 61 18 75 00 00 00 00 00 00 00 00 00 00 00 00 | Da.u............\n00801ED0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801EE0 : 00 00 00 50 4d 52 20 31 30 00 00 00 00 00 00 00 | ...PMR 10.......\n00801EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F00 : 44 61 31 25 00 00 00 00 40 00 00 00 00 00 00 00 | Da1%....@.......\n00801F10 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801F20 : 00 00 00 50 4d 52 20 31 31 00 00 00 00 00 00 00 | ...PMR 11.......\n00801F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F40 : 44 61 43 75 00 00 00 00 00 00 00 00 00 00 00 00 | DaCu............\n00801F50 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801F60 : 00 00 00 50 4d 52 20 31 32 00 00 00 00 00 00 00 | ...PMR 12.......\n00801F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801F80 : 44 61 56 25 00 00 00 00 00 00 00 00 00 00 00 00 | DaV%............\n00801F90 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801FA0 : 00 00 00 50 4d 52 20 31 33 00 00 00 00 00 00 00 | ...PMR 13.......\n00801FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00801FC0 : 44 61 68 75 00 00 00 00 00 00 00 00 00 00 00 00 | Dahu............\n00801FD0 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00801FE0 : 00 00 00 50 4d 52 20 31 34 00 00 00 00 00 00 00 | ...PMR 14.......\n00801FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008020F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008021F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008022F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008023F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008024F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008025F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008026F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008027F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008028F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008029F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00802FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008030F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008031F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008032F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008033F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008034F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008035F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008036F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008037F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008038F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n008039F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00803FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00840000 : 44 61 81 25 00 00 00 00 00 00 00 00 00 00 00 00 | Da.%............\n00840010 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00840020 : 00 00 00 50 4d 52 20 31 35 00 00 00 00 00 00 00 | ...PMR 15.......\n00840030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00840040 : 44 61 93 75 00 00 00 00 40 00 00 00 00 00 00 00 | Da.u....@.......\n00840050 : cf 09 00 00 00 00 00 00 00 00 01 ff ff 00 00 00 | ................\n00840060 : 00 00 00 50 4d 52 20 31 36 00 00 00 00 00 00 00 | ...PMR 16.......\n00840070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00842000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00842070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00FC0800 : 43 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | CQ%.............\n00FC0810 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &...............\n00FC0820 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 41 | ...Channel VFO A\n00FC0830 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC0840 : 14 51 25 00 00 01 00 00 1d 00 15 15 13 00 13 00 | .Q%.............\n00FC0850 : 26 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | &...............\n00FC0860 : 01 00 00 43 68 61 6e 6e 65 6c 20 56 46 4f 20 42 | ...Channel VFO B\n00FC0870 : 00 00 00 00 08 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n00FC2800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n00FC2870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01000000 : 00 00 03 00 04 00 01 00 05 00 07 00 08 00 09 00 | ................\n01000010 : 0a 00 0b 00 0d 00 0e 00 4a 00 4b 00 4c 00 4d 00 | ........J.K.L.M.\n01000020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010000F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010001F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000200 : 5f 00 63 00 64 00 65 00 42 00 43 00 44 00 45 00 | _.c.d.e.B.C.D.E.\n01000210 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I.........\n01000220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010002F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010003F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000400 : 6e 00 6f 00 70 00 71 00 ff ff ff ff ff ff ff ff | n.o.p.q.........\n01000410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010004F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000600 : 3e 00 40 00 41 00 ff ff ff ff ff ff ff ff ff ff | >.@.A...........\n01000610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000800 : 64 00 42 00 43 00 44 00 45 00 46 00 47 00 48 00 | d.B.C.D.E.F.G.H.\n01000810 : 49 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff | I...............\n01000820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A00 : 0e 00 10 00 14 00 16 00 1a 00 1e 00 22 00 4d 00 | ............\".M.\n01000A10 : 4e 00 4f 00 50 00 51 00 52 00 53 00 54 00 ff ff | N.O.P.Q.R.S.T...\n01000A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C00 : 60 00 61 00 64 00 65 00 42 00 43 00 44 00 45 00 | `.a.d.e.B.C.D.E.\n01000C10 : 46 00 47 00 48 00 49 00 ff ff ff ff ff ff ff ff | F.G.H.I.........\n01000C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E00 : 23 00 24 00 25 00 26 00 27 00 28 00 29 00 2a 00 | #.$.%.&.'.(.).*.\n01000E10 : 4d 00 4e 00 55 00 ff ff ff ff ff ff ff ff ff ff | M.N.U...........\n01000E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01000FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001000 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G.\n01001010 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I.............\n01001020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001200 : 2b 00 2c 00 2d 00 2e 00 2f 00 30 00 31 00 32 00 | +.,.-.../.0.1.2.\n01001210 : 56 00 57 00 58 00 ff ff ff ff ff ff ff ff ff ff | V.W.X...........\n01001220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001400 : 64 00 65 00 42 00 43 00 44 00 45 00 46 00 47 00 | d.e.B.C.D.E.F.G.\n01001410 : 48 00 49 00 ff ff ff ff ff ff ff ff ff ff ff ff | H.I.............\n01001420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001600 : 33 00 35 00 36 00 37 00 38 00 3a 00 3b 00 3c 00 | 3.5.6.7.8.:.;.<.\n01001610 : 3d 00 59 00 5a 00 5b 00 5c 00 5d 00 5e 00 ff ff | =.Y.Z.[.\\.].^...\n01001620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001800 : 62 00 64 00 65 00 42 00 43 00 44 00 45 00 46 00 | b.d.e.B.C.D.E.F.\n01001810 : 47 00 48 00 49 00 ff ff ff ff ff ff ff ff ff ff | G.H.I...........\n01001820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A00 : 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff ff ff | h.m.l.k.j.i.....\n01001A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C00 : 67 00 68 00 6d 00 6c 00 6b 00 6a 00 69 00 ff ff | g.h.m.l.k.j.i...\n01001C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E00 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y.\n01001E10 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~.......\n01001E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01001FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002000 : 72 00 73 00 74 00 75 00 76 00 77 00 78 00 79 00 | r.s.t.u.v.w.x.y.\n01002010 : 7a 00 7b 00 7c 00 7d 00 7e 00 7f 00 80 00 81 00 | z.{.|.}.~.......\n01002020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01002190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n010021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n01040000 : 44 00 00 00 43 99 00 00 ff ff 00 00 00 00 00 00 | D...C...........\n01040010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040020 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040040 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01040060 : 00 00 00 00 00 00 00 00 ff ff 00 00 00 00 00 00 | ................\n01040070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01042000 : 0f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01042010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01042080 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01043000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01043040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01043070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01080000 : 00 01 00 00 ff ff 0f 00 19 00 1d 00 1d 00 00 4b | ...............K\n01080010 : 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | W...............\n01080020 : 03 00 04 00 4a 00 4b 00 5f 00 64 00 42 00 6e 00 | ....J.K._.d.B.n.\n01080030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080080 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01080200 : 00 00 ff ff ff ff 0f 00 19 00 1d 00 1d 00 00 42 | ...............B\n01080210 : 65 72 42 72 61 00 00 00 00 00 00 00 00 00 00 00 | erBra...........\n01080220 : 04 00 08 00 0e 00 10 00 14 00 16 00 1e 00 22 00 | ..............\".\n01080230 : 1a 00 26 00 2a 00 32 00 ff ff ff ff ff ff ff ff | ..&.*.2.........\n01080240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01080280 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01640000 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640010 : 00 00 02 01 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640020 : 00 00 03 02 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640030 : 00 00 04 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640040 : 00 00 ff 04 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n01640800 : 00 00 00 00 00 ff ff ff ff ff ff ff ff ff ff ff | ................\n01640810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n01640860 : ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n01640880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140000 : 48 65 6c 6c 6f 21 00 00 00 00 00 00 00 00 00 00 | Hello!..........\n02140010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021400C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140100 : 57 65 6c 63 6f 6d 65 21 00 00 00 00 00 00 00 00 | Welcome!........\n02140110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021401C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140200 : 54 68 61 6e 6b 20 79 6f 75 21 00 00 00 00 00 00 | Thank you!......\n02140210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021402C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140300 : 47 6f 6f 64 20 62 79 65 21 00 00 00 00 00 00 00 | Good bye!.......\n02140310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021403C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02140400 : 48 61 70 70 79 20 65 76 65 72 79 20 64 61 79 21 | Happy every day!\n02140410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02140490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n021404C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02480000 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02480200 : 01 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02480210 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02480220 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C0000 : 00 00 0e 46 12 34 56 78 9a bc de 00 00 00 00 00 | ...F.4Vx........\n024C0010 : 00 00 00 00 00 00 00 00 00 20 20 20 20 20 20 00 | .........      .\n-----------------------------------------------------------------------------\n024C0C80 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C0D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C0EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1000 : c0 5d 68 29 50 2d 9c 31 b0 36 c4 3b 3c 41 7c 47 | .]h)P-.1.6.;<A|G\n024C1010 : 20 4e f0 55 60 6d a4 1f e4 25 92 22 90 65 54 24 |  N.U`m...%.\".eT$\n024C1020 : 00 00 00 05 46 01 02 03 04 05 00 00 14 00 64 14 | ....F.........d.\n024C1030 : 01 0d 0f 00 14 32 32 00 00 00 00 00 00 00 00 00 | .....22.........\n024C1040 : 00 00 00 46 00 00 00 00 00 00 00 00 00 00 00 00 | ...F............\n024C1050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1060 : 00 00 00 46 00 00 00 00 00 00 00 00 00 00 00 00 | ...F............\n024C1070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1080 : 0e 0a 00 32 14 64 00 00 01 01 14 00 00 00 00 00 | ...2.d..........\n024C1090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C10C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n024C1100 : 91 0c 41 24 00 00 00 00 00 00 00 00 00 00 00 00 | ..A$............\n-----------------------------------------------------------------------------\n024C1280 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1290 : 00 00 00 00 00 00 00 00 00 05 05 0a 0a 64 01 00 | .............d..\n-----------------------------------------------------------------------------\n024C1300 : ff ff 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1320 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1340 : 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1400 : 00 02 00 0a 0a 3c 00 00 01 01 00 0a 0a 3c 01 00 | .....<.......<..\n024C1410 : 01 01 09 09 00 01 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C1440 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1460 : 00 00 00 12 34 56 78 00 00 00 00 00 00 00 00 00 | ....4Vx.........\n-----------------------------------------------------------------------------\n024C1500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n024C1510 : 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C15F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C16F0 : 00 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1700 : 01 01 02 02 03 03 04 04 05 05 06 06 07 07 08 08 | ................\n024C1710 : 09 09 0a 0a 0b 0b 0c 0c 0d 0d 0e 0e 0f 0f 10 10 | ................\n024C1720 : 11 11 12 12 13 13 14 14 15 15 16 16 17 17 18 18 | ................\n024C1730 : 19 19 1a 1a 1b 1b 1c 1c 1d 1d 1e 1e 1f 1f 20 20 | ..............  \n-----------------------------------------------------------------------------\n024C1800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C18F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C19F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C1CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2000 : 60 ea 00 00 20 a1 07 00 00 00 00 00 00 00 00 00 | `... ...........\n024C2010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C20F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C21F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C22F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C23E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2400 : 91 0c 41 24 00 00 00 00 00 00 00 00 00 00 00 00 | ..A$............\n024C2410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C2420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C2600 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n024C4000 : 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4020 : 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C40F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C41F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C42F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C43F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C44F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C45F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C46F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C47F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C48F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C49F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C4FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C50F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C51F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C52F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C53F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C54F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C55F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C56F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C57F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C58F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C59F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C5FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C60F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C61F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C62F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C63F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C64F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C65F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C66F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C67F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C68F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C69F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C6FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7000 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C70F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C71F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C72F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C73F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C74F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7530 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C75F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C76F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7770 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C77F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7830 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7890 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C78F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7900 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7920 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7960 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7980 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C79F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7A90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7AF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7B90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7BF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7C90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7CF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7D90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7DF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7E90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7ED0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7EF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F30 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F40 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7F90 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FA0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FB0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FC0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FD0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FE0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n024C7FF0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02500000 : 01 00 00 00 04 00 00 00 04 01 01 01 00 0f 01 02 | ................\n02500010 : 01 12 0c 13 08 00 00 01 02 05 05 05 05 00 01 00 | ................\n02500020 : 01 00 00 00 00 00 04 01 01 01 01 00 00 01 02 01 | ................\n02500030 : 14 00 01 01 00 00 00 02 01 01 00 05 00 00 00 00 | ................\n02500040 : 01 00 00 00 00 00 01 01 00 00 00 00 00 03 00 00 | ................\n02500050 : 02 01 05 00 00 00 00 00 00 5a 62 02 00 6c dc 02 | .........Zb..l..\n02500060 : 00 85 cf 00 c0 80 09 01 01 00 00 00 00 00 01 00 | ................\n02500070 : 00 01 bc 02 00 00 00 00 00 00 00 00 0f 00 00 00 | ................\n02500080 : 00 00 00 00 00 00 dc 05 58 02 00 00 00 00 00 00 | ........X.......\n02500090 : 0a 00 0a 00 00 00 00 00 00 00 6c 07 20 03 00 00 | ..........l. ...\n025000A0 : 00 00 00 00 0a 00 0a 00 00 00 00 00 00 00 00 01 | ................\n025000B0 : 00 00 00 00 00 05 01 01 00 00 00 00 06 00 0b 00 | ................\n025000C0 : 00 00 01 00 00 85 cf 00 c0 80 09 01 00 5a 62 02 | .............Zb.\n025000D0 : 00 6c dc 02 00 01 01 00 00 00 00 00 00 00 05 02 | .l..............\n025000E0 : 00 00 01 00 06 02 00 00 01 00 02 00 01 0a 00 00 | ................\n-----------------------------------------------------------------------------\n02500100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025001F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025002F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025003F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025004F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02500500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n025005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02500600 : 4f 70 65 6e 20 47 44 37 37 00 00 00 00 00 00 00 | Open GD77.......\n02500610 : 44 4d 33 4d 41 54 00 00 00 00 00 00 00 00 00 00 | DM3MAT..........\n02500620 : 31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00 | 12345678........\n-----------------------------------------------------------------------------\n02501000 : ff 00 00 00 00 ff ff ff 00 00 ff ff ff ff ff ff | ................\n02501010 : ff ff ff ff ff 00 20 20 20 20 20 20 ff 20 20 20 | ......      .   \n02501020 : 20 20 20 ff 00 00 00 00 00 00 00 00 00 00 00 00 |    .............\n02501030 : 00 00 00 00 00 00 00 00 00 ff ff 03 ff 01 03 ff | ................\n02501040 : a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f a2 0f | ................\n02501050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501070 : ff ff ff ff ff ff ff ff 01 00 00 00 00 00 00 00 | ................\n02501080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025012A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02501400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501420 : 00 00 01 00 00 85 cf 00 c0 80 09 01 00 5a 62 02 | .............Zb.\n02501430 : 00 6c dc 02 00 00 19 05 05 06 00 01 01 06 06 00 | .l..............\n02501440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02501490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025014F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02540000 : 4b 57 20 41 00 00 00 00 00 00 00 00 00 00 00 00 | KW A............\n02540010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540020 : 4b 57 20 42 00 00 00 00 00 00 00 00 00 00 00 00 | KW B............\n02540030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540040 : 54 48 20 57 69 6c 64 61 75 00 00 00 00 00 00 00 | TH Wildau.......\n02540050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540060 : 48 65 73 73 65 6e 20 41 00 00 00 00 00 00 00 00 | Hessen A........\n02540070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540080 : 48 65 73 73 65 6e 20 42 00 00 00 00 00 00 00 00 | Hessen B........\n02540090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400A0 : 42 65 72 6c 69 6e 20 41 00 00 00 00 00 00 00 00 | Berlin A........\n025400B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400C0 : 42 65 72 6c 69 6e 20 42 00 00 00 00 00 00 00 00 | Berlin B........\n025400D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025400E0 : 50 6f 74 73 64 61 6d 20 41 00 00 00 00 00 00 00 | Potsdam A.......\n025400F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540100 : 50 6f 74 73 64 61 6d 20 42 00 00 00 00 00 00 00 | Potsdam B.......\n02540110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540120 : 43 6f 74 74 62 75 73 20 41 00 00 00 00 00 00 00 | Cottbus A.......\n02540130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540140 : 43 6f 74 74 62 75 73 20 42 00 00 00 00 00 00 00 | Cottbus B.......\n02540150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540160 : 4c 65 69 70 7a 69 67 20 41 00 00 00 00 00 00 00 | Leipzig A.......\n02540170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540180 : 4c 65 69 70 7a 69 67 20 42 00 00 00 00 00 00 00 | Leipzig B.......\n02540190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401A0 : 53 61 74 20 41 00 00 00 00 00 00 00 00 00 00 00 | Sat A...........\n025401B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401C0 : 53 61 74 20 42 00 00 00 00 00 00 00 00 00 00 00 | Sat B...........\n025401D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025401E0 : 50 4d 52 20 41 00 00 00 00 00 00 00 00 00 00 00 | PMR A...........\n025401F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02540200 : 50 4d 52 20 42 00 00 00 00 00 00 00 00 00 00 00 | PMR B...........\n02540210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02580000 : 02 62 13 70 00 44 4d 33 4d 41 54 00 00 00 00 00 | .b.p.DM3MAT.....\n02580010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n025C0000 : 00 ff 00 ff 00 ff 00 ff 00 00 00 00 00 00 00 00 | ................\n025C0010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0020 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0060 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0080 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C00F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0100 : 53 74 61 74 75 73 20 4d 65 73 73 61 67 65 20 31 | Status Message 1\n025C0110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0120 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0130 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0150 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0190 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C01F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0210 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0250 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C02F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0320 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0340 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0380 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C03F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0400 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0440 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0450 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0470 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C04F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0500 : 01 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0530 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0560 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0570 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0580 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0590 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C05A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05C0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C05D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C05F0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0600 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0620 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0640 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0650 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0660 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0680 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06B0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C06C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C06E0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C06F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0700 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0710 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0720 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0740 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0770 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0790 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07A0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C07B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07D0 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C07E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C07F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0800 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0830 : 00 01 00 00 ff ff ff ff ff 00 00 00 00 00 00 00 | ................\n025C0840 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0850 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n025C0B00 : 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n025C0B10 : 3f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ?...............\n025C0B20 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02600000 : 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 | ................\n02600010 : 04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 | ................\n02600020 : 08 00 00 00 09 00 00 00 0a 00 00 00 0b 00 00 00 | ................\n02600030 : 0c 00 00 00 0d 00 00 00 0e 00 00 00 0f 00 00 00 | ................\n02600040 : 10 00 00 00 11 00 00 00 12 00 00 00 13 00 00 00 | ................\n02600050 : 14 00 00 00 15 00 00 00 16 00 00 00 17 00 00 00 | ................\n02600060 : 18 00 00 00 19 00 00 00 1a 00 00 00 ff ff ff ff | ................\n02600070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026000F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026001F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026002F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026003F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026004F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026005F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026006F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026007F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026008F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026009F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02600FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026010F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026011F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026012F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026013F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026014F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026015F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026016F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026017F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026018F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026019F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02601FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026020F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026021F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026022F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026023F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026024F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026025F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026026F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026027F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026028F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026029F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02602FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026030F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026031F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026032F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026033F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026034F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026035F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026036F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026037F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026038F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026039F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02603FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026040F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026041F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026042F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026043F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026044F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026045F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026046F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026047F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026048F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026049F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02604FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026050F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026051F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026052F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026053F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026054F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026055F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026056F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026057F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026058F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026059F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02605FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026060F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026061F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026062F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026063F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026064F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026065F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026066F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026067F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026068F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026069F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02606FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026070F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026071F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026072F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026073F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026074F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026075F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026076F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026077F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026078F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026079F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02607FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026080F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026081F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026082F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026083F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026084F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026085F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026086F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026087F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026088F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026089F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608C90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608CF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608D90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608DF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608E90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608ED0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608EF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608F90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02608FF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609000 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026090F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026091F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026092F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026093F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026094F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609500 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609510 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609520 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609530 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609540 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609550 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609560 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609570 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609580 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609590 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026095F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609600 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026096F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609700 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609710 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609720 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609730 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609740 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609750 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609760 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609770 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609780 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609790 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026097F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026098F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609900 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609910 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609920 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609930 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609940 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609950 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609960 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609970 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609980 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609990 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026099F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609B90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609BF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C00 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02609C30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02640000 : 00 00 00 f8 ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026400F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640100 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640180 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640190 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026401F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640200 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026402F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640300 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640310 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640320 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640330 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640340 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640350 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640360 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640370 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640380 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640390 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026403F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640400 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02640490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n026404E0 : ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02680000 : 01 57 57 00 00 00 00 00 00 00 00 00 00 00 00 00 | .WW.............\n02680010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680020 : 00 00 00 00 00 00 91 00 00 00 00 00 00 00 00 00 | ................\n02680030 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680040 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680050 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680060 : 00 00 00 00 01 45 55 00 00 00 00 00 00 00 00 00 | .....EU.........\n02680070 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680080 : 00 00 00 00 00 00 00 00 00 00 92 00 00 00 00 00 | ................\n02680090 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800C0 : 00 00 00 00 00 00 00 00 01 44 4c 00 00 00 00 00 | .........DL.....\n026800D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026800E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 02 62 00 | ..............b.\n026800F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680100 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680120 : 00 00 00 00 00 00 00 00 00 00 00 00 01 44 4c 20 | .............DL \n02680130 : 4d 4d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | MM..............\n02680140 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680150 : 00 02 63 00 00 00 00 00 00 00 00 00 00 00 00 00 | ..c.............\n02680160 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680170 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680180 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680190 : 01 52 65 67 69 6f 6e 61 6c 00 00 00 00 00 00 00 | .Regional.......\n026801A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801B0 : 00 00 00 00 00 00 08 00 00 00 00 00 00 00 00 00 | ................\n026801C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026801F0 : 00 00 00 00 01 4c 6f 6b 61 6c 00 00 00 00 00 00 | .....Local......\n02680200 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680210 : 00 00 00 00 00 00 00 00 00 00 09 00 00 00 00 00 | ................\n02680220 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680230 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680240 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680250 : 00 00 00 00 00 00 00 00 01 42 6c 6e 2f 42 72 62 | .........Bln/Brb\n02680260 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680270 : 00 00 00 00 00 00 00 00 00 00 00 00 00 26 21 00 | .............&!.\n02680280 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680290 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802B0 : 00 00 00 00 00 00 00 00 00 00 00 00 01 53 61 2f | .............Sa/\n026802C0 : 54 68 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | Th..............\n026802D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026802E0 : 00 26 29 00 00 00 00 00 00 00 00 00 00 00 00 00 | .&).............\n026802F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680300 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680320 : 01 53 41 2f 4d 56 00 00 00 00 00 00 00 00 00 00 | .SA/MV..........\n02680330 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680340 : 00 00 00 00 00 26 20 00 00 00 00 00 00 00 00 00 | .....& .........\n02680350 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680360 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680370 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680380 : 00 00 00 00 01 48 65 73 73 65 6e 00 00 00 00 00 | .....Hessen.....\n02680390 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803A0 : 00 00 00 00 00 00 00 00 00 26 26 00 00 00 00 00 | .........&&.....\n026803B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026803E0 : 00 00 00 00 00 00 00 00 02 41 6c 6c 20 43 61 6c | .........All Cal\n026803F0 : 6c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | l...............\n02680400 : 00 00 00 00 00 00 00 00 00 00 00 16 77 72 15 00 | ............wr..\n02680410 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680420 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680430 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680440 : 00 00 00 00 00 00 00 00 00 00 00 00 01 53 69 6d | .............Sim\n02680450 : 70 6c 65 78 20 54 47 39 39 00 00 00 00 00 00 00 | plex TG99.......\n02680460 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680470 : 00 00 99 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680480 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680490 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804B0 : 01 43 6c 20 42 72 61 6e 64 65 6e 62 75 72 67 00 | .Cl Brandenburg.\n026804C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804D0 : 00 00 00 00 02 62 09 00 00 00 00 00 00 00 00 00 | .....b..........\n026804E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026804F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680500 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680510 : 00 00 00 00 01 43 6c 20 42 65 72 6c 69 6e 00 00 | .....Cl Berlin..\n02680520 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680530 : 00 00 00 00 00 00 00 00 02 62 12 00 00 00 00 00 | .........b......\n02680540 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680550 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680560 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680570 : 00 00 00 00 00 00 00 00 00 52 65 66 6c 20 42 72 | .........Refl Br\n02680580 : 61 6e 64 65 6e 62 75 72 67 00 00 00 00 00 00 00 | andenburg.......\n02680590 : 00 00 00 00 00 00 00 00 00 00 00 00 00 40 44 00 | .............@D.\n026805A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026805D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 52 65 66 | .............Ref\n026805E0 : 6c 20 42 65 72 6c 69 6e 00 00 00 00 00 00 00 00 | l Berlin........\n026805F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680600 : 00 40 16 00 00 00 00 00 00 00 00 00 00 00 00 00 | .@..............\n02680610 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680620 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680630 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680640 : 00 42 4d 20 53 4d 53 00 00 00 00 00 00 00 00 00 | .BM SMS.........\n02680650 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680660 : 00 00 00 00 26 29 93 00 00 00 00 00 00 00 00 00 | ....&)..........\n02680670 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680680 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680690 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806A0 : 00 00 00 00 00 42 4d 20 44 41 50 4e 45 54 00 00 | .....BM DAPNET..\n026806B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806C0 : 00 00 00 00 00 00 00 00 26 29 94 00 00 00 00 00 | ........&)......\n026806D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026806F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680700 : 00 00 00 00 00 00 00 00 00 42 4d 20 50 61 72 72 | .........BM Parr\n02680710 : 6f 74 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ot..............\n02680720 : 00 00 00 00 00 00 00 00 00 00 00 00 26 29 97 00 | ............&)..\n02680730 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680740 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680750 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680760 : 00 00 00 00 00 00 00 00 00 00 00 00 00 42 4d 20 | .............BM \n02680770 : 41 50 52 53 00 00 00 00 00 00 00 00 00 00 00 00 | APRS............\n02680780 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680790 : 26 29 99 00 00 00 00 00 00 00 00 00 00 00 00 00 | &)..............\n026807A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807D0 : 00 44 47 32 52 4f 4e 20 52 6f 6e 6e 79 00 00 00 | .DG2RON Ronny...\n026807E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026807F0 : 00 00 00 02 62 12 46 01 00 00 00 00 00 00 00 00 | ....b.F.........\n02680800 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680810 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680820 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680830 : 00 00 00 00 00 44 4d 39 4b 53 20 53 69 6c 76 69 | .....DM9KS Silvi\n02680840 : 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | o...............\n02680850 : 00 00 00 00 00 00 00 02 62 10 45 01 00 00 00 00 | ........b.E.....\n02680860 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680870 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680880 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680890 : 00 00 00 00 00 00 00 00 00 44 4d 33 4d 41 54 20 | .........DM3MAT \n026808A0 : 48 61 6e 6e 65 73 00 00 00 00 00 00 00 00 00 00 | Hannes..........\n026808B0 : 00 00 00 00 00 00 00 00 00 00 00 02 62 13 70 01 | ............b.p.\n026808C0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808D0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808E0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026808F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 44 4c 32 | .............DL2\n02680900 : 42 51 46 20 54 68 6f 6d 61 73 00 00 00 00 00 00 | BQF Thomas......\n02680910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 | ................\n02680920 : 62 14 37 01 00 00 00 00 00 00 00 00 00 00 00 00 | b.7.............\n02680930 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680940 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680950 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680960 : 00 44 4c 37 4a 4f 4d 20 4f 6c 61 66 00 00 00 00 | .DL7JOM Olaf....\n02680970 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680980 : 00 00 00 02 62 10 04 01 00 00 00 00 00 00 00 00 | ....b...........\n02680990 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809A0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809B0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n026809C0 : 00 00 00 00 00 44 47 31 52 50 48 20 50 65 74 65 | .....DG1RPH Pete\n026809D0 : 72 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | r...............\n026809E0 : 00 00 00 00 00 00 00 02 62 15 28 01 00 00 00 00 | ........b.(.....\n026809F0 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A00 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A20 : 00 00 00 00 00 00 00 00 00 44 4f 31 52 50 48 20 | .........DO1RPH \n02680A30 : 52 69 63 68 61 72 64 00 00 00 00 00 00 00 00 00 | Richard.........\n02680A40 : 00 00 00 00 00 00 00 00 00 00 00 02 63 41 60 00 | ............cA`.\n02680A50 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A60 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A70 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n02680A80 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02900000 : 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02900100 : 00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900110 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900120 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900130 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900140 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900150 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900160 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02900170 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n-----------------------------------------------------------------------------\n02940000 : 12 34 50 00 00 00 00 05 43 6f 6e 74 61 63 74 31 | .4P.....Contact1\n02940010 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980000 : 00 00 00 00 01 00 00 00 02 00 00 00 ff ff ff ff | ................\n02980010 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980020 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980030 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980040 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980050 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980060 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980070 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980080 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980090 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029800F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980100 : 44 4c 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | DL..............\n02980110 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980200 : 05 00 00 00 04 00 00 00 06 00 00 00 ff ff ff ff | ................\n02980210 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980220 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980230 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980240 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980250 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980260 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980270 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980280 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980290 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029802F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980300 : 42 65 72 6c 69 6e 2f 42 72 61 6e 64 00 00 00 00 | Berlin/Brand....\n02980310 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980400 : 04 00 00 00 05 00 00 00 07 00 00 00 ff ff ff ff | ................\n02980410 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980420 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980430 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980440 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980450 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980460 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980470 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980480 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980490 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029804F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980500 : 53 61 2f 54 68 00 00 00 00 00 00 00 00 00 00 00 | Sa/Th...........\n02980510 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980600 : 04 00 00 00 05 00 00 00 08 00 00 00 ff ff ff ff | ................\n02980610 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980620 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980630 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980640 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980650 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980660 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980670 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980680 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980690 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029806F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980700 : 53 41 2f 4d 56 00 00 00 00 00 00 00 00 00 00 00 | SA/MV...........\n02980710 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980800 : 04 00 00 00 05 00 00 00 09 00 00 00 ff ff ff ff | ................\n02980810 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980820 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980830 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980840 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980850 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980860 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980870 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980880 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980890 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808A0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808B0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808C0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808D0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808E0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n029808F0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980900 : 48 65 73 73 65 6e 00 00 00 00 00 00 00 00 00 00 | Hessen..........\n02980910 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n02980A00 : 05 00 00 00 0b 00 00 00 0a 00 00 00 ff ff ff ff | ................\n02980A10 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A20 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A30 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A40 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A50 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A60 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A70 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A80 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980A90 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AA0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AB0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AC0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AD0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AE0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980AF0 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff | ................\n02980B00 : 53 69 6d 70 6c 65 78 00 00 00 00 00 00 00 00 00 | Simplex.........\n02980B10 : 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................\n-----------------------------------------------------------------------------\n04340000 : 11 00 00 00 04 00 00 00 13 00 00 00 05 00 00 00 | ................\n04340010 : 23 01 00 00 00 00 00 00 25 01 00 00 01 00 00 00 | #.......%.......\n04340020 : 33 01 00 00 0b 00 00 00 c5 04 00 00 02 00 00 00 | 3...............\n04340030 : c7 04 00 00 03 00 00 00 41 4c 00 00 08 00 00 00 | ........AL......\n04340040 : 43 4c 00 00 06 00 00 00 4d 4c 00 00 09 00 00 00 | CL......ML......\n04340050 : 53 4c 00 00 07 00 00 00 2c 80 00 00 0f 00 00 00 | SL......,.......\n04340060 : 88 80 00 00 0e 00 00 00 13 c4 04 00 0c 00 00 00 | ................\n04340070 : 25 c4 04 00 0d 00 00 00 26 53 4c 00 10 00 00 00 | %.......&SL.....\n04340080 : 28 53 4c 00 11 00 00 00 2e 53 4c 00 12 00 00 00 | (SL......SL.....\n04340090 : 32 53 4c 00 13 00 00 00 08 20 c4 04 18 00 00 00 | 2SL...... ......\n043400A0 : 8a 20 c4 04 15 00 00 00 8c 24 c4 04 14 00 00 00 | . .......$......\n043400B0 : e0 26 c4 04 16 00 00 00 6e 28 c4 04 17 00 00 00 | .&......n(......\n043400C0 : 50 2a c4 04 19 00 00 00 c0 82 c6 04 1a 00 00 00 | P*..............\n043400D0 : 2a e4 ee 2c 0a 00 00 00 ff ff ff ff ff ff ff ff | *..,............\n"
  },
  {
    "path": "doc/reveng/anytone/d878uv/extract.py",
    "content": "#!/usr/bin/env python3\n\nimport pyshark\nimport struct\nimport sys\nimport binascii\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\ndef isWriteCommand(p):\n  if not isDataPacket(p):\n    return False\n  data = getData(p)\n  return 'W' == chr(data[0])\n\ndef isDataPacket(p):\n  return (\"host\" == p.usb.src) and (\"USB.CAPDATA\" in p)\n\ndef getData(p):\n  if not isDataPacket(p): \n    return None\n  return binascii.a2b_hex(p[\"USB.CAPDATA_RAW\"].value)\n\n\ndef dumpWriteCommand(p, nextaddr):\n  if not isWriteCommand(p):\n    return\n  data = getData(p)\n  res = struct.unpack(\">cIB16sBB\", data)\n  addr = res[1]\n  if nextaddr != addr:\n    print((8+3+16*3+16+2)*\"-\")\n  print(\"{:08X} : {}\".format(addr, hexDump(res[3])))\n  return addr+16\n\ncap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True)\nnextaddr = 0\nfor p in cap:\n  if isWriteCommand(p):\n    nextaddr = dumpWriteCommand(p, nextaddr)\n"
  },
  {
    "path": "doc/reveng/anytone/d878uv2/at_d878uv2_emulator.py",
    "content": "#!/usr/bin/env python3\n#\n# Emulate anytone d878uv radio to customer programming software. \n# Send intercepted data stream over network to server script for further investigation.\n#\n# This script connects to a virtual com port COM26 which is connected via a virtual\n# null modem cable to the virtual com port COM18 which is used by the programming software.\n# This virtual ports and cable can be provided by the COM0COM tool.\n#\n# Linux users can use\n# socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000\n# for emulating a virtual null modem cable.\n\nimport serial\nimport time\nimport sys\nimport struct \n\n\n# config\nfilebase = 'codeplug' \nfilecount = 0\ncomport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS\n\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\n# parameters?\nif len(sys.argv) == 2:\n   comport = sys.argv[1]\nelif len(sys.argv) >= 3:\n   print(\"Usage: \" + sys.argv[0] + ' [comport]')\n   exit()\n\n\n\n# open serial port\nserialPort = None\n\ntry:\n   print(\"Trying comport \" + comport)\n   serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000\nexcept (err):\n   print('ERR: Could not open port ' + comport)\n   print(\"Usage: \" + sys.argv[0] + ' servername [comport]: ' + err)\n   exit()\n\n\nout = None \nnextaddr = None \n\n# wait for data\n\ntry:\n   \n   while 1:\n\n      command = ''\n\n      command = serialPort.read()\n      while serialPort.in_waiting > 0:\n         command += serialPort.read()\n\n      # respond to command on com port\n      if ( len(command) == 0 ):\n         pass\n\n      elif ( command == b'PROGRAM'):\n         print(\"Program session requested.\")\n         resp = b'QX\\x06'\n         serialPort.write(resp)\n         filename = \"{}_{:04}.hex\".format(filebase, filecount)\n         out = open(filename, \"w\")\n         nextaddr = None \n         \n      elif ( command == b'\\x02' ):\n         print(\"Device info requested.\")\n         resp = b'ID878UV2\\x00V100\\x00\\x00\\x06'\n         serialPort.write(resp)\n\n      elif ( command == b'R\\x02\\xfa\\x00\\x20\\x10' ):\n         print(\"Read special memory request.\")\n         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'\n         serialPort.write(resp)\n         \n      elif ( command[0:4] == b'R\\x02\\xfa\\x00' and command[5] == 16 ):\n         # 0x02fa00..\n         print(\"Read local information.\")\n         \n         resp = b'W\\x02\\xfa\\x00' + bytes([command[4]]) + b'\\x10'\n         \n         if ( command[4] == 0x00 ):\n            resp += b'\\x00\\x00\\x00\\x00\\x01\\x01\\x01\\x00\\x00\\x01\\x01\\x20\\x20\\x20\\x20\\xff'\n                                           \n         elif ( command[4] == 0x10 ): # Radio Type\n            resp += b'\\x44\\x38\\x37\\x38\\x55\\x56\\x00\\x01\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n\n         elif ( command[4] == 0x30 ): # Serial Number\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x40 ): # Production Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x50 ): # Manucfacture Code\n            resp += b'\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n         elif ( command[4] == 0x60 ): # Maintained Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x70 ): # Dealer Code\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x80 ): # Stock Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x90 ): # Sell Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xa0 ): # Seller\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         elif ( command[4] == 0xb0 ): # Maintained Description\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xc0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xd0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xe0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xf0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         else:\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\\x06'\n         #print(resp.hex())\n         serialPort.write(resp)\n\n      elif ( command[0] == ord('W') ) :\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         res = struct.unpack(\">cIB16sBB\", command)\n         addr = res[1]\n         if nextaddr != addr:\n            out.write((8+3+16*3+16+2)*\"-\" + \"\\n\")\n         out.write(\"{:08X} : {}\\n\".format(addr, hexDump(res[3])))\n         nextaddr = addr+16\n         \n      elif ( command == b'END' ):\n         print(\"End session.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         out.close()\n         filecount += 1\n\n      elif ( command == b'UPDATE' ):\n         # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys\n         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)\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command == b'\\x18' ):\n         print(\"Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command[0] == 0x01 ):\n         print(\"Firmware data.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n      else:\n         #print(\"> \" + str(command))\n         pass\n\n\n\nfinally:\n   print('QRT')\n   serialPort.close()\n\n"
  },
  {
    "path": "doc/reveng/baofeng/d6x2uv/dmr_6x2uv_emulator.py",
    "content": "#!/usr/bin/env python3\n#\n# Emulate anytone d878uv radio to customer programming software. \n# Send intercepted data stream over network to server script for further investigation.\n#\n# This script connects to a virtual com port COM26 which is connected via a virtual\n# null modem cable to the virtual com port COM18 which is used by the programming software.\n# This virtual ports and cable can be provided by the COM0COM tool.\n#\n# Linux users can use\n# socat -d -d pty,raw,echo=0,b4000000 pty,raw,echo=0,b4000000\n# for emulating a virtual null modem cable.\n\nimport serial\nimport time\nimport sys\nimport struct \n\n\n# config\nfilebase = 'codeplug' \nfilecount = 0\ncomport = 'COM6' # connected to COM18 with com0com. use COM18 in CPS\n\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\n# parameters?\nif len(sys.argv) == 3:\n   filebase = sys.argv[1]\n   comport = sys.argv[2]\nelif len(sys.argv) == 2:\n   filebase = sys.argv[1]\nelif len(sys.argv) >3:\n   print(\"Usage: \" + sys.argv[0] + ' filebase [comport]')\n   exit()\n\n\n\n# open serial port\nserialPort = None\n\ntry:\n   print(\"Trying comport \" + comport)\n   serialPort = serial.Serial(port = comport, baudrate=4000000, bytesize=8, timeout=1, stopbits=serial.STOPBITS_ONE) # 115200 921600 4000000\nexcept (err):\n   print('ERR: Could not open port ' + comport)\n   print(\"Usage: \" + sys.argv[0] + ' servername [comport]: ' + err)\n   exit()\n\n\nout = None \nnextaddr = None \n\n# wait for data\n\ntry:\n   \n   while 1:\n\n      command = ''\n\n      command = serialPort.read()\n      while serialPort.in_waiting > 0:\n         command += serialPort.read()\n\n      # respond to command on com port\n      if ( len(command) == 0 ):\n         pass\n\n      elif ( command == b'PROGRAM'):\n         print(\"Program session requested.\")\n         resp = b'QX\\x06'\n         serialPort.write(resp)\n         filename = \"{}_{:04}.hex\".format(filebase, filecount)\n         out = open(filename, \"w\")\n         nextaddr = None \n         \n      elif ( command == b'\\x02' ):\n         print(\"Device info requested.\")\n         resp = b'ID6X2UV\\x00\\x00V102\\x00\\x00\\x06'\n         serialPort.write(resp)\n\n      elif ( command == b'R\\x02\\xfa\\x00\\x20\\x10' ):\n         print(\"Read special memory request.\")\n         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'\n         serialPort.write(resp)\n         \n      elif ( command[0:4] == b'R\\x02\\xfa\\x00' and command[5] == 16 ):\n         # 0x02fa00..\n         print(\"Read local information.\")\n         \n         resp = b'W\\x02\\xfa\\x00' + bytes([command[4]]) + b'\\x10'\n         \n         if ( command[4] == 0x00 ):\n            resp += b'\\x00\\x00\\x00\\x03\\x01\\x01\\x01\\x00\\x00\\x01\\x01\\x20\\x20\\x20\\x20\\xff'\n                                           \n         elif ( command[4] == 0x10 ): # Radio Type\n            resp += b'\\x44\\x38\\x37\\x38\\x55\\x56\\x00\\x01\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n\n         elif ( command[4] == 0x30 ): # Serial Number\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x40 ): # Production Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x50 ): # Manucfacture Code\n            resp += b'\\x31\\x32\\x33\\x34\\x35\\x36\\x37\\x38\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff'\n         elif ( command[4] == 0x60 ): # Maintained Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x70 ): # Dealer Code\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x80 ): # Stock Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0x90 ): # Sell Date\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xa0 ): # Seller\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         elif ( command[4] == 0xb0 ): # Maintained Description\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xc0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xd0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xe0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n         elif ( command[4] == 0xf0 ): \n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         else:\n            resp += b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\n         resp = resp + bytes( [sum(resp[1:]) & 0xff] ) + b'\\x06'\n         #print(resp.hex())\n         serialPort.write(resp)\n\n      elif ( command[0] == ord('W') ) :\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         res = struct.unpack(\">cIB16sBB\", command)\n         addr = res[1]\n         if nextaddr != addr:\n            out.write((8+3+16*3+16+2)*\"-\" + \"\\n\")\n         out.write(\"{:08X} : {}\\n\".format(addr, hexDump(res[3])))\n         nextaddr = addr+16\n         \n      elif ( command == b'END' ):\n         print(\"End session.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n         out.close()\n         filecount += 1\n\n      elif ( command == b'UPDATE' ):\n         # for firmware update the device has to be switched on while pressing PF3 (blue button on top) and PTT keys\n         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)\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command == b'\\x18' ):\n         print(\"Firmware Update Send Complete. Switch device on while pressing PF2 (top left side) and PTT keys to start installer.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n\n      elif ( command[0] == 0x01 ):\n         print(\"Firmware data.\")\n         resp = b'\\x06' # just ack\n         serialPort.write(resp)\n      else:\n         #print(\"> \" + str(command))\n         pass\n\n\n\nfinally:\n   print('QRT')\n   serialPort.close()\n\n"
  },
  {
    "path": "doc/reveng/baofeng/dr1801/extract.py",
    "content": "#!/usr/bin/env python3\n\nimport pyshark\nimport sys\nimport binascii\nfrom abc import ABCMeta\n\ndev_addr = 1\n\nif len(sys.argv) != 2:\n    print(\"Usage: extract.py PCAPNG_FILE\")\n\n\ndef hexDump(s: bytes, prefix: str = \"\", addr: int = 0) -> str:\n  \"\"\" Utility function to hex-dump binary data. \"\"\"\n  N = len(s)\n  Nb = N//16\n  if (N%16): Nb += 1\n  res = \"\"\n  for j in range(Nb):\n    a,b = j*16, min((j+1)*16,N)\n\n    h = \" \".join(map(\"{:02x}\".format, s[a:b]))\n    h += \"   \"*(16-(b-a))\n    t = \"\"\n    for i in range(a,b):\n      c = s[i]\n      if c>=0x20 and c<0x7f:\n        t += chr(c)\n      else:\n        t += \".\"\n\n  res += (prefix + \"{:08X} \".format(addr+16*j) + h + \" | \" + t + \"\\n\")\n  return res[:-1]\n\ndef isFromHost(p):\n    return (\"host\" == p.usb.src) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address))) \n\ndef isToHost(p):\n    return ((\"host\" == p.usb.dest) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address))))\n\ndef isDataPacket(p):\n  return (\"USB.CAPDATA\" in p)\n\ndef getData(p):\n  if not isDataPacket(p): \n    return None\n  return binascii.a2b_hex(p[\"USB.CAPDATA_RAW\"].value)\n\n\ncap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True)\nnextaddr = 0\nfor p in cap:\n  if isDataPacket(p):\n    if isFromHost(p):\n      print(hexDump(getData(p), \"< \"))\n    else:\n      print(hexDump(getData(p), \"> \"))\n      #print(p)\n        \n"
  },
  {
    "path": "doc/reveng/baofeng/dr1801/protocol.md",
    "content": "# DR-1801A6 (BF1801) - Protocol\n\nA new version of the popular DM-1801 by Baofeng. Is sold as DR-1801UV or DR-1801A6 and calls itself\nBF1801. The communication is based on USB CDC-ACM (serial over USB). \n\nThis device uses the AUCTUS A6 chip and thus its protocol. For a detailed description, see jhart99s \nbrilliant [article series](https://jhart99.com/a6-hidden-interface/) on this chip.\n\n## Basic structure\nThe first few packets exchanged at a read:\n```\n> aa 06 01 04 03 bb \n< aa 07 81 04 01 83 bb\n\n> aa 07 00 2b 00 2c bb \n< aa 07 80 2b 02 ae bb \n\n> aa 0a 01 00 00 01 c2 00 c8 bb \n< aa 15 81 00 01 00 01 dd 90 00 00 00 68 00 02 e6 9e 09 2d ef bb \n\n> aa 06 01 01 06 bb \n< ... only packets from device to host from now on with no structure. Just plain data.\n```\n\nThe first few packets exchanged at a write:\n```\n> aa 06 01 04 03 bb\n< aa 07 81 04 01 83 bb \n\n> aa 07 00 2b 00 2c bb \n< aa 07 80 2b 02 ae bb\n\n> aa 12 01 02 00 01 00 01 da 8c 7f f6 00 01 c2 00 0d bb\n< aa 08 81 02 01 00 8a bb \n\n> .... Codeplug is written in many large packets with no structure, just data.\n\n< aa 09 81 03 01 00 00 8a bb\n\n< 00 | .\n< 00 | .\n< 00 | .\n```\n\nThere appears to be two kind of messages command/response messages and status messages from the \ndevice. The status messages do not appear to be triggered by by the host. \n\n## Command/response messages\nThey start with `aah` and end with `bbh`. The general structure of command/response messages are\n```\n +---+---+---+---+---+...+---+---+---+\n |aah|LEN|  CMD  | Params    |CRC|bbh|\n +---+---+---+---+---+...+---+---+---+\n```\nThe `LEN` field contains the length of the entire packet, including start and stop bytes. The \nparameter field is optional and of variable length. The checksum is computed by simply xor-ing the \nentire payload byte-wise. The command field has the following sub-structure:\n```\n +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n |C/R| Command 15bit, big-endian                                 |\n +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n```\nThe most significant bit contains a flag, that is set for responses and cleared for requests.\n\nThere are many commands already known see jhart99s \n[list of commands](https://jhart99.com/atecps/#cps-commands).\n\n\n### 0000h Command -- Identify radio\nNot used by the manufacturer CPS but handled by the radio. This command is send without any \nparameters and the radio returns a string, identifying the radio. The response looks like\n```\naa 3a 80 00 01 20 2c 42   46 31 38 30 31 2c 41 36 \t..... ,B  F1801,A6\n2d 30 30 30 30 2d 58 58   58 58 2c 70 6f 72 74 61 \t-0000-XX  XX,porta\n62 6c 65 2c 31 33 36 4d   2d 31 37 34 4d 2c 34 30 \tble,136M  -174M,40\n30 4d 2d 34 38 30 4d 2c   fd bb                     0M-480M,  ..\n```\n\nThe response payload starts with a status byte (0x01 success) followed by an ASCII string, \ncontaining the information about the radio. The information is separated by comma (,). \nThe first entry is empty (single space), followed by the device name (BF1801), followed by\nthe firmware version (weird number here), device class (portable) and the frequency ranges. \nThere is likely space for three bands, however, the device only supports 136-174MHz and 400-480MHz.\n\n\n### 0104h Command -- Enter CPS mode (?)\n First command send without any parameters irrespective of codeplug read or write.  \n \n Response contains a single parameter `01h`, likely status byte like *success*. \n \n\n### 002bh Command -- Check Programming Password\nChecks the programming password. Request contains parameter `00h`. Likely, the length of the \npassword. Needs to be tested with a proper password. \n\nThe Response contains a single byte `02h`. \n\n### 0100h Command \nOnly send before reading the codeplug. Contains 4bytes of parameters. Seen \n`00 01 c2 00` = 115200 big endian. Set UART speed?!? \n\nResponse parameters are quiet long, e.g. \n`01 00 01 dd 90 00 00 00 68 00 02 e6 9e 09 2d`. 0x0001dd90 is the size of the codeplug file. Then,\nit appears like two uint32 numbers i.e., 0x00000068 and 0x0002e69e. These numbers do not change. The\nlast two bytes, however, appear to be a checksum.\n\n### 0101h Command\nOnly send right before reading of the codeplug starts. There is no actual response to this request,\nthe radio responds with the codeplug. No parameters send with this command.\n\n### 0102h Command  \nOnly send before writing the codeplug. Contains 12bytes of parameters. E.g., \n`00 01 00 01 da 8c 7f f6 00 01 c2 00`, again contains 0001c200h=115200.\nAs not the entire codeplug file is written to the device, `00 01 da 8c` may encode the amount to be\nwritten. \n\nResponse only contains two bytes. E.g., `01 00`.\n\n### 0103h Command\nOnly seen after codeplug write. Contains 3 parameter bytes `01 00 00`.\n\n"
  },
  {
    "path": "doc/reveng/cotre/README.md",
    "content": "# Cotre DMR radio\nThis is likely the cheapest DMR radio out there for about $45. \n\nThe 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?\n\n## Packet format\nRequests 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. \n\n```\n   0                               8                               16                              24\n  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n  | Preamble, fixed to 0xad       | 16 bit payload length, big endian                             | Payload, variable size     ... \n  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n   ...  However, only a few bytes are actually transferred here.                                  | Checksum?                     |\n  +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n```\n### Checksum\nThe checksum appears to be a simple XOR of all payload bytes.\n\n### Example\nThe very first request sent to the device when reading is \n```\n ad 00 07 ff 04 03 00 00 00 01 f9\n```\nThe would then decode into \n  * Preamble `ad` \n  * Length `0007`\n  * Payload `ff 04 03 00 00 00 01`\n  * CRC `f9` = `ff ^ 04 ^ 03 ^ 01`\n\n"
  },
  {
    "path": "doc/reveng/cotre/extract.py",
    "content": "#!/usr/bin/env python3\n\nimport pyshark\nimport struct\nimport sys\nimport binascii\n\ndev_addr = 6\n\n\ndef hexDump(s):\n  h = \" \".join(map(\"{:02x}\".format, s))\n  t = \"\"\n  for i in range(len(s)):\n    c = s[i]\n    if c>=0x20 and c<0x7f:\n      t += chr(c)\n    else:\n      t += \".\"\n  return( h + \" | \" + t)\n\ndef isFromHost(p):\n    return (\"host\" == p.usb.src) and ((None==dev_addr) or (dev_addr == int(p.usb.device_address))) \n\ndef isToHost(p):\n    return (\"host\" == p.usb.dest) \n\ndef isDataPacket(p):\n  return (\"USB.CAPDATA\" in p)\n\ndef getData(p):\n  if not isDataPacket(p): \n    return None\n  return binascii.a2b_hex(p[\"USB.CAPDATA_RAW\"].value)\n\n\ncap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True)\nnextaddr = 0\nfor p in cap:\n  if isDataPacket(p):\n    dump = hexDump(getData(p))\n    if isFromHost(p):\n      print(\"> \" + dump)\n    else:\n      print(\"< \" + dump)\n        \n"
  },
  {
    "path": "doc/reveng/gd77/callsign-db.md",
    "content": "# GD77 call-sign DB memory representation\nThe encoding appears to be super simple:\n\n## Header\n``` \n0                               8                               \n+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +\n|'I'|'D'|'-'|'V'|'0'|'0'|'1'| 0 | N entries     | Entries     ... |\n+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ... +\n```\nThe first 8 bytes (at address 0x0000) contains the 0-terminated string `ID-V001`, \nfollowed by the number of DB entries encoded as a 32bit little endian integer. After \nthis header all entries are just appended. These entries must be sorted by the DMR ID\nin ascending order. \n\nThere is no index table written to the device. \n\n\n## DB Entry\nEach entry of the table is encoded into 12bytes as\n``` \n0                               8                               \n+---+---+---+---+---+---+---+---+---+---+---+---+\n| DMR ID        | Name, ASCII                   |\n+---+---+---+---+---+---+---+---+---+---+---+---+\n``` \nwhere the DMR ID is encoded as 8 BCD numbers in litte-endian and the name is max 8 ASCII chars, 0-terminated and padded.\n"
  },
  {
    "path": "doc/reveng/gd77/dump.py",
    "content": "#!/usr/bin/env python3\n\n\nfrom json import dump\nfrom re import S\nimport pyshark\nimport struct\nimport sys\nimport binascii\n\ndevice = None # \"1.13.0\"\n\ndef hexDump(s: bytes, prefix=\"\", addr=0) -> str:\n  \"\"\" Utility function to hex-dump binary data. \"\"\"\n  N = len(s)\n  Nb = N//16\n  if (N%16): Nb += 1\n  res = \"\"\n  for j in range(Nb):\n    a,b = j*16, min((j+1)*16,N)\n\n    h = \" \".join(map(\"{:02x}\".format, s[a:b]))\n    h += \"   \"*(16-(b-a))\n    t = \"\"\n    for i in range(a,b):\n      c = s[i]\n      if c>=0x20 and c<0x7f:\n        t += chr(c)\n      else:\n        t += \".\"\n\n    res += (prefix + \"{:08X} \".format(addr+16*j) + h + \" | \" + t + \"\\n\")\n  return res[:-1]\n\ndef isFromHost(p):\n  b = (\"host\" == p.usb.src)\n  if device:\n    b = b and (device == p.usb.dst)\n  return b\n\ndef isFromDevice(p):\n  b = (\"host\" == p.usb.dst)\n  if device:\n    b = b and (device == p.usb.src)\n  return b\n\ndef isRequest(p):\n  return isFromHost(p) and (\"SETUP DATA\" == p.highest_layer) and (p.layers[-1].has_field(\"data_fragment\"))\n\ndef isResponse(p):\n  return isFromDevice(p) and (\"USB.CAPDATA\" in p) \n\ndef getData(p):\n  if isRequest(p):\n    return binascii.a2b_hex(p.layers[-1].data_fragment_raw[0])\n  elif isResponse(p): \n    return binascii.a2b_hex(p[\"USB.CAPDATA_RAW\"].value)\n  return None\n\n\nclass RawPayload:\n  def __init__(self, payload):\n    self._payload = payload\n\n  def __len__(self):\n    return len(self._payload)\n\n  def dump(self, prefix=\"\", addr=0):\n    return hexDump(self._payload, prefix, addr)\n  \n\nclass Package:\n  def __init__(self, payload):\n    self._type, self._length = struct.unpack(\"<HH\", payload[:4])\n    payload = payload[4:(4+self._length)]\n    if (0x52 == payload[0]) and len(payload)>=4:\n      self._payload = ReadOperation(payload)\n    elif (0x57 == payload[0]) and len(payload)>=4:\n      self._payload = WriteOperation(payload)\n    elif 0x41 == payload[0]:\n      self._payload = ACK(payload)\n    else:\n      self._payload = RawPayload(payload)\n\n  def type(self):\n    return self._type\n\n  def payload(self):\n    return self._payload\n\n  def dump(self, prefix=\"\"):\n    s = \"\"\n    if 1 == self._type:\n      s += prefix + \"Request, len=0x{0:02X}\".format(self._length) + \"\\n\"\n    elif 3 == self._type:\n      s += prefix + \"Response, len=0x{0:02X}\".format(self._length) + \"\\n\"\n    s += self._payload.dump(prefix+\"   | \")\n    return s\n\n\nclass ReadOperation:\n  def __init__(self, payload):\n    self._addr, self._length = struct.unpack(\">HB\", payload[1:4])\n    self._payload = None\n    if 4 < len(payload):\n      self._payload = RawPayload(payload[4:])\n\n  def dump(self, prefix=\"\"):\n    s = prefix + \"Read addr=0x{0:04X}, len=0x{1:02X}\".format(self._addr, self._length)\n    if None != self._payload:\n      s += \"\\n\" + self._payload.dump(prefix + \"   | \")\n    return s \n\nclass WriteOperation:\n  def __init__(self, payload):\n    self._addr, self._length = struct.unpack(\">HB\", payload[1:4])\n    self._payload = None\n    if 4 < len(payload):\n      self._payload = RawPayload(payload[4:])\n\n  def payload(self):\n    return self._payload\n  def addr(self):\n    return self._addr\n\n  def dump(self, prefix=\"\"):\n    s = prefix + \"Write addr=0x{0:04X}, len=0x{1:02X}\".format(self._addr, self._length)\n    if None != self._payload:\n      s += \"\\n\" + self._payload.dump(prefix + \"   | \")\n    return s \n\n\nclass ACK: \n  def __init__(self, payload):\n    pass \n\n  def dump(self, prefix=\"\"):\n    return prefix+\"ACK\"\n\nif 2 == len(sys.argv):\n  cap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True)\n  for p in cap:\n    if isRequest(p) and len(getData(p))>=4:\n      P = Package(getData(p))\n      print(P.dump(\" > \"))\n      print(\"\")\n    elif isResponse(p) and len(getData(p))>=4:\n      P = Package(getData(p))\n      print(P.dump(\" < \"))\n      print(\"\")\n    elif isRequest(p): \n      print(hexDump(getData(p), \" > \"))\n    elif isResponse(p):\n      print(hexDump(getData(p), \" < \"))\n\nelif (3 == len(sys.argv)) and (\"write\" == sys.argv[1]):\n  print(\"Dump written memory from file {0}:\".format(sys.argv[2]))\n  cap = pyshark.FileCapture(sys.argv[2], include_raw=True, use_json=True)\n  addr = 0\n  for p in cap:\n    if isRequest(p) and len(getData(p))>=4:\n      P = Package(getData(p))\n      if isinstance(P.payload(), WriteOperation):\n        W = P.payload()\n        if (addr != W.addr()):\n          print(\"\")\n          print (\"-\"*80)\n          print(\"\")\n          addr = W.addr()\n        print(W.payload().dump(\" \", addr))\n        addr += len(W.payload())\n      else:\n        print(\"\")\n        print(P.dump(\" > \"))\n        print(\"\")\n\nelse:\n  print(\"oops\")"
  },
  {
    "path": "doc/reveng/pinspect/__init__.py",
    "content": "from packethandler import PacketHandler\nfrom devicefilter import DeviceFilter"
  },
  {
    "path": "doc/reveng/pinspect/auctus_a6.py",
    "content": "from devicefilter import DeviceFilter\nfrom streamhandler import StreamHandler\nfrom cdcacmfilter import CDCACMFilter\nfrom rawstreamdump import RawStreamDump\nfrom datagram import Datagram, RawDatagramDump, DatagramHandler\nfrom struct import unpack\nfrom utilities import hexDump\nfrom binascii import crc_hqx\n\ndef crc16_simple_sum8(data:bytearray):\n  return sum(data)&0xffff\n\ndef crc16_simple_sum16(data:bytearray):\n  s = 0\n  for i in range(len(data)//2):\n    s += unpack(\">H\", data[2*i:(2*(i+1))])[0]\n  return s&0xffff\n\ndef crc16_simple_xor16(data:bytearray):\n  s = 0\n  for i in range(len(data)//2):\n    s ^= unpack(\">H\", data[2*i:(2*(i+1))])[0]\n  return s\n\ndef crc_summary(data:bytearray):\n  print(\"CRC16-HQX  : {:04X}\".format(crc_hqx(data, 0)))\n  print(\"CRC16-SUM8 : {:04X}\".format(crc16_simple_sum8(data)))\n  print(\"CRC16-SUM16: {:04X}\".format(crc16_simple_sum16(data)))\n  print(\"CRC16-XOR16: {:04X}\".format(crc16_simple_xor16(data)))\n\n\nclass AuctusA6Datagram(Datagram):\n  def __init__(self, command, parameters, crc):\n    self._isRequest = (0 == (command & 0x8000))\n    self._command = (command & 0x7fff)\n    self._parameters = parameters \n    self._crc = crc \n\n  def isRequest(self):\n    return self._isRequest\n\n  def isResponse(self):\n    return not self.isRequest()\n\n  def command(self):\n    return self._command \n\n  def parameters(self):\n    return self._parameters\n\n  def format(self, prefix=\"\"):\n    res = prefix\n    res += \"type={}, command={:04X}, CRC={:02X}\".format(\n      (\"Req\" if self._isRequest else \"Resp\"), self._command, self._crc)\n    if len(self._parameters):\n      res += \"\\n\"+hexDump(self._parameters, prefix=\" \"*len(prefix)+\" |\")\n    return res \n\n  def __repr__(self):\n    return self.format()\n    \n\nclass AuctusA6Handler(StreamHandler):\n  MODE_PASS     = 0x00\n  MODE_DATAGRAM = 0x01\n\n  def __init__(self):\n    super().__init__()\n    self._tobuffer = bytearray()\n    self._frombuffer = bytearray()\n    self._mode = AuctusA6Handler.MODE_DATAGRAM\n    self._dataLeft = None\n    self._handler = []\n\n  def attach(self, handler):\n    self._handler.append(handler)\n    return self \n\n  def handleToHost(self, data:bytearray):\n    self._tobuffer.extend(data)\n    if AuctusA6Handler().MODE_DATAGRAM == self._mode: \n      while AuctusA6Handler().hasDatagram(self._tobuffer):\n        dgram = AuctusA6Handler().popDatagram(self._tobuffer)\n        self._processToHost(dgram)\n        for handler in self._handler:\n          if isinstance(handler, DatagramHandler):\n            handler.handleToHost(dgram)\n    else:\n      self._dataLeft -= len(data)\n      if 0 == self._dataLeft:\n        self._mode = AuctusA6Handler.MODE_DATAGRAM\n        for handler in self._handler:\n          if isinstance(handler, StreamHandler):\n            handler.handleToHost(self._tobuffer)\n        crc_summary(self._tobuffer)\n        self._tobuffer.clear()\n\n  def handleFromHost(self, data:bytearray):\n    self._frombuffer.extend(data)\n    if AuctusA6Handler().MODE_DATAGRAM == self._mode: \n      while AuctusA6Handler().hasDatagram(self._frombuffer):\n        dgram = AuctusA6Handler().popDatagram(self._frombuffer)\n        self._processFromHost(dgram)\n        for handler in self._handler:\n          if isinstance(handler, DatagramHandler):\n            handler.handleFromHost(dgram)\n    else:\n      self._dataLeft -= len(data)\n      if 0 == self._dataLeft:\n        self._mode = AuctusA6Handler.MODE_DATAGRAM\n        for handler in self._handler:\n          if isinstance(handler, StreamHandler):\n            handler.handleFromHost(self._frombuffer)\n        crc_summary(self._frombuffer)\n        self._frombuffer.clear()\n\n  @staticmethod \n  def hasDatagram(buffer: bytearray):\n    if not buffer.startswith(b'\\xaa'): return False \n    if len(buffer) < 6: return False\n    length, command = unpack(\">xBH\", buffer[:4])\n    return len(buffer) >= length\n\n  @staticmethod\n  def popDatagram(buffer: bytearray):\n    length, command = unpack(\">xBH\", buffer[:4])\n    length, command, params, crc = unpack(\">xBH{}sBx\".format(length-6), buffer[:length])\n    del buffer[:length]\n    return AuctusA6Datagram(command, params, crc)\n\n  def _processFromHost(self, dgram:AuctusA6Datagram):\n    if dgram.isRequest() and (0x0102 == dgram.command()):\n      self._dataLeft, baudRate = unpack(\">xxIxxI\", dgram.parameters())\n    elif dgram.isRequest() and (0x0101 == dgram.command()):\n      self._mode = AuctusA6Handler.MODE_PASS\n\n  def _processToHost(self, dgram:AuctusA6Datagram):\n    if dgram.isResponse() and (0x0102 == dgram.command()):\n      self._mode = AuctusA6Handler.MODE_PASS\n    elif dgram.isResponse() and (0x0100 == dgram.command()):\n      self._dataLeft, = unpack(\">xI10x\", dgram.parameters())\n\nif \"__main__\" == __name__:\n  import sys\n  stream = DeviceFilter(14).attach(\n        CDCACMFilter().attach(\n          AuctusA6Handler()\n            .attach(RawDatagramDump())\n            .attach(RawStreamDump())))\n  stream.process(sys.argv[1])\n"
  },
  {
    "path": "doc/reveng/pinspect/cdcacmfilter.py",
    "content": "from packethandler import PacketHandler\nfrom streamhandler import StreamHandler\nfrom binascii import a2b_hex\n\n\nclass CDCACMFilter(PacketHandler):\n  \n  USB_URB_INTERRUPT = 0x01\n  USB_URB_BULK      = 0x03\n\n  def __init__(self):\n    super().__init__()\n    pass \n\n  def attach(self, handler:StreamHandler):\n    self._handler.append(handler)\n    return self\n\n  def handle(self, packet):\n    if (int(packet.usb.transfer_type,base=16) != CDCACMFilter.USB_URB_BULK) or (\"USB.CAPDATA\" not in packet):\n      return\n    for handler in self._handler:\n      if PacketHandler.isFromHost(packet):\n        handler.handleFromHost(a2b_hex(packet[\"USB.CAPDATA_RAW\"].value))\n      else:\n        handler.handleToHost(a2b_hex(packet[\"USB.CAPDATA_RAW\"].value))\n"
  },
  {
    "path": "doc/reveng/pinspect/datagram.py",
    "content": "from abc import ABCMeta, abstractmethod\n\n\nclass Datagram(metaclass=ABCMeta):\n  \"\"\" Base class of all data grams. \"\"\"\n\n  def __init__(self):\n    pass \n\n  @abstractmethod\n  def format(self, prefix=\"\"):\n    pass\n\n\nclass DatagramHandler(metaclass=ABCMeta):\n\n  def __init__(self):\n    pass \n\n  @abstractmethod\n  def handleFromHost(self, dgram: Datagram):\n    pass \n\n  @abstractmethod \n  def handleToHost(self, dgram: Datagram):\n    pass \n\n\nclass RawDatagramDump(DatagramHandler):\n  def __init__(self):\n    super().__init__()\n\n  def handleFromHost(self, dgram:Datagram):\n    print(dgram.format(\"< \"))\n\n  def handleToHost(self, dgram:Datagram):\n    print(dgram.format(\"> \"))\n"
  },
  {
    "path": "doc/reveng/pinspect/devicefilter.py",
    "content": "from packethandler import PacketHandler\n\nclass DeviceFilter(PacketHandler):\n  \"\"\" Filters by device address. \"\"\"\n\n  def __init__(self, device: int = None):\n    \"\"\" :param device: Specifies the USB device address of radio. \"\"\"\n    super().__init__()\n    self._source = device\n\n  def handle(self, packet):\n    if ((None == self._source) or (int(packet.usb.device_address) == self._source)):\n      super().handle(packet)\n"
  },
  {
    "path": "doc/reveng/pinspect/packethandler.py",
    "content": "#!/usr/bin/env python3\n\n\"\"\" \nTHis module implements some basic packet handler, that can filter and process packets captured by\nwireshark. \n\"\"\"\n\nfrom abc import ABCMeta, abstractmethod\nfrom pyshark import FileCapture\n\n\nclass PacketHandler(metaclass=ABCMeta):\n  \"\"\" Abstract base class for all packet handlers. \"\"\"\n\n  def __init__(self):\n    self._handler = []\n\n  def attach(self, handler):\n    self._handler.append(handler)\n    return self\n\n  @staticmethod\n  def isFromHost(p):\n    return \"host\" == p.usb.src\n\n  @staticmethod\n  def isToHost(p):\n    return \"host\" == p.usb.dest\n\n  def handle(self, packet):\n    for handler in self._handler:\n      handler.handle(packet)\n\n  def process(self, filename:str):\n    for packet in FileCapture(filename, include_raw=True, use_json=True):\n      self.handle(packet)\n    "
  },
  {
    "path": "doc/reveng/pinspect/rawstreamdump.py",
    "content": "from utilities import hexDump\nfrom streamhandler import StreamHandler\n\n\nclass RawStreamDump(StreamHandler):\n  def __init__(self):\n    super().__init__()\n    self._toAddr = 0\n    self._fromAddr = 0\n\n  def handleFromHost(self, data):\n    print(hexDump(data, \"< \", self._fromAddr))\n    self._fromAddr+=len(data)\n\n  def handleToHost(self, data):\n    print(hexDump(data, \"> \", self._toAddr))\n    self._toAddr+=len(data)\n"
  },
  {
    "path": "doc/reveng/pinspect/streamhandler.py",
    "content": "from abc import ABCMeta, abstractmethod\n\nclass StreamHandler(metaclass=ABCMeta):\n  def __init__(self):\n    pass \n  \n  @abstractmethod\n  def handleFromHost(self, data:bytearray):\n    pass \n\n  @abstractmethod\n  def handleToHost(self, data:bytearray):\n    pass \n\n"
  },
  {
    "path": "doc/reveng/pinspect/utilities.py",
    "content": "def hexDump(s: bytes, prefix: str = \"\", addr: int = 0) -> str:\n  \"\"\" Utility function to hex-dump binary data. \"\"\"\n  N = len(s)\n  Nb = N//16\n  if (N%16): Nb += 1\n  res = \"\"\n  for j in range(Nb):\n    a,b = j*16, min((j+1)*16,N)\n\n    h = \" \".join(map(\"{:02x}\".format, s[a:b]))\n    h += \"   \"*(16-(b-a))\n    t = \"\"\n    for i in range(a,b):\n      c = s[i]\n      if c>=0x20 and c<0x7f:\n        t += chr(c)\n      else:\n        t += \".\"\n    res += (prefix + \"{:08X} \".format(addr+16*j) + h + \" | \" + t + \"\\n\")\n  return res[:-1]\n"
  },
  {
    "path": "doc/reveng/radioddity/gd73/README.md",
    "content": "# Reverse engineering of the GD73 serial protocol\n\nSee `protocol.md` for details."
  },
  {
    "path": "doc/reveng/radioddity/gd73/extract.py",
    "content": "#!/usr/bin/env python3\n\nimport pyshark\nimport struct\nimport sys\nimport binascii\n\ndevice = \"1.255\"\ndevice_in = \"{}.2\".format(device)\ndevice_out = \"{}.1\".format(device)\n\ndef hexDump(s: bytes, prefix:str=\"\", addr:int=0, compact:bool=False) -> str:\n  \"\"\" Utility function to hex-dump binary data. \"\"\"\n  N = len(s)\n  Nb = N//16\n  if (N%16): Nb += 1\n  res = \"\"\n  last_line = None\n  compressing = False\n  for j in range(Nb):\n    a,b = j*16, min((j+1)*16,N)\n    line = s[a:b]\n    if (not compact) or (last_line != line):\n      h = \" \".join(map(\"{:02x}\".format, line))\n      h += \"   \"*(16-(b-a))\n      t = \"\"\n      for i in range(a,b):\n        c = s[i]\n        if c>=0x20 and c<0x7f:\n          t += chr(c)\n        else:\n          t += \".\"\n      res += (prefix + \"{:08X} \".format(addr+16*j) + h + \" | \" + t + \"\\n\")\n      last_line = line\n      compressing = False\n    elif not compressing: \n      res += (prefix + \"*\\n\")\n      compressing = True \n  return res[:-1]\n\ndef isDataPacket(p):\n  return (\"USB.CAPDATA\" in p)\n\ndef isFromHost(p):\n  return ((\"host\" == p.usb.src) and (device_in == p.usb.dst))\n\ndef isFromDevice(p):\n  return ((\"host\" == p.usb.dst) and (device_out == p.usb.src))\n\ndef getData(p):\n  if not isDataPacket(p): \n    return None\n  return binascii.a2b_hex(p[\"USB.CAPDATA_RAW\"].value)\n\n\nclass Packet:\n  def __init__(self, data:bytes):\n    self._flag, self._cmd, self._sub, self._pcrc, size = struct.unpack(\"<xBBBHH\", data[:8])\n    self._payload = bytes()\n    if size: self._payload = data[8:(8+size)]\n    self._crc = 0xffff\n    for i in range(len(data)//2):\n      v = struct.unpack(\"<H\", data[(2*i):(2*(i+1))])[0]\n      if v > self._crc: self._crc += 0xffff;\n      self._crc -= v    \n    if len(data)%2:\n      v = struct.unpack(\"B\", data[-1:])[0]\n      if v > self._crc: self._crc +=0xffff;\n      self._crc -= v;\n    \n\n  def __repr__(self):\n    if 0 == len(self._payload):\n      return \"Packet flag={:02x}, cmd={:02x}, sub={:02x}, crc={:04x}.\".format(self._flag, self._cmd, self._sub, self._crc)\n    return \"Packet flag={:02x}, cmd={:02x}, sub={:02x}, crc={:04x}\\n{}\".format(self._flag, self._cmd, self._sub, self._crc, hexDump(self._payload, \" \"))\n\ncap = pyshark.FileCapture(sys.argv[1], include_raw=True, use_json=True)\nnextaddr = 0\nfor p in cap:\n  if isDataPacket(p):\n    print(Packet(getData(p)))\n"
  },
  {
    "path": "doc/reveng/radioddity/gd73/protocol.md",
    "content": "# GD73 Serial Protocol\n\n## General Request/Response Frame Format\n```\n     0   1   2   3   4   5   6   7   8      n-2 n-1  bytes\n   +---+---+---+---+---+---+---+---+---+...+---+---+\n00 |68h|FLG|CMD|SUB| CRC?  | Size  |  Payload  |10h|\n   +---+---+---+---+---+---+---+---+---+...+---+---+ \n```\n\n| Bytes  | Value | Description |\n|:-------|:------|:------------|\n| 0      | 68    | Start of frame byte. Fixed. |\n| 1      | 0f    | Flags? Fixed. |\n| 2:3    | 01 04 | First command send on read. Probably *enter prog mode*. No payload. |\n|        | 00 02 | Response to *enter prog mode* |\n|        | 01 02 | Start read. |\n|        | 00 00 | Read response, contains sequence number and 35h bytes codeplug data. |\n|        | 04 01 | Read ACK, triggers read of next segment. |\n|        | 01 00 | Write request, usually 55bytes payload. |\n|        | 00 01 | Write ACK. |\n| 4:5    |       | CRC |\n| 6:7    |       | The payload size in little endian. |\n| 8:(n-2)|       | The request/response payload. |\n| n-1    | 10    | The end-of-frame byte. |\n\n\n### CRC\nThe CRC is computed over the entire packet, including start and end bytes. For the computation, \nthe packet is interpreted as a sequence of little-endian unsigned 16-bit integer. If the packet \ndoes not align with 16bits, it gets 0-padded to a multiple of 16bit. \n\nLet `v[i]` the i-th of `N` unsigned 16bit integer formed by the 0-padded packet, the CRC is then \ncomputed as\n```\nuint32_t crc = 0xffff;\nfor (int i=0; i<N; i++) {\n  if (crc < v[i])\n    crc += 0xffff;\n  crc -= v[i];\n}\n```\nTo check the CRC, the resulting value of `crc` must be 0, to compute the CRC, just set the CRC field\nto 0, compute the CRC over the entire packet and then set the field within the packet to the value \nof the result.\n\n\n## Enter program mode request (0104h)\nThe request is send before reading and writing the codeplug. The radio responses with 21h bytes in a \n0002h response. The content/format of the response is unknown\n\n## Start read request (0102h)\nThis request initializes the codeplug read process. The radio responds with the first (seq. number \n0000h) 35h byte-segment of the codeplug. To receive further segments, the host must ACK the received\nsegment with a read-ACK request containing the number of the received segment.\n```\n Host                                Device\n  | --- Read request (no payload)  ---> |\n  | <----- Response 0000 + Data ------- |\n  | --------- ACK seq. 0000 ----------> |\n  | <----- Response 0001 + Data ------- |\n  | --------- ACK seq. 0001 ----------> |\n ...                                   ...\n  | <----- Response 0a42 + Data ------- |\n  | --------- ACK seq. 0a42 ----------> |\n  | <----- Response 0a43 + Data ------- |\n```\n\n## Write request (0100h)\nThe write request sends a sequence number and codeplug segment to the device. The device then ACKs \nthe written segment.\n\n\n"
  },
  {
    "path": "doc/reveng/retevis/rt84/extract.py",
    "content": "#!/usr/bin/env python3 \n\nimport struct \nimport sys \n\n\ndef hexDump(s: bytes, prefix=\"\", addr=0, compact=False) -> str:\n  \"\"\" Utility function to hex-dump binary data. \"\"\"\n  N = len(s)\n  Nb = N//16\n  if (N%16): Nb += 1\n  res = \"\"\n  last_line = None\n  compressing = False\n  for j in range(Nb):\n    a,b = j*16, min((j+1)*16,N)\n    line = s[a:b]\n    if (not compact) or (last_line != line):\n      h = \" \".join(map(\"{:02x}\".format, line))\n      h += \"   \"*(16-(b-a))\n      t = \"\"\n      for i in range(a,b):\n        c = s[i]\n        if c>=0x20 and c<0x7f:\n          t += chr(c)\n        else:\n          t += \".\"\n      res += (prefix + \"{:08X} \".format(addr+16*j) + h + \" | \" + t + \"\\n\")\n      last_line = line\n      compressing = False\n    elif not compressing: \n      res += (prefix + \"*\\n\")\n      compressing = True \n  return res[:-1]\n\n\nif 2 > len(sys.argv):\n  print(\"USAGE: extract.py CODEPLUG.rdt\")\nrdt_filename = sys.argv[1]\n\n# list of sections: (file offset, size, destination address)\nsections = [\n  (0x002225, 0x03e000, 0x002000),\n  (0x040230, 0x090000, 0x110000),\n]\n\n# Read all\ndata = open(rdt_filename, \"br\").read()\nfor section in sections:\n  offset, length, addr = section\n  print(hexDump(data[offset:(offset+length)], \"\", addr, True))\n\n\n"
  },
  {
    "path": "examples/BER AirBand.yaml",
    "content": "---\nversion: 0.14.0\nsettings:\n  introLine1: Berlin Air\n  introLine2: \"\"\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\nradioIDs:\n  []\ncontacts:\n  []\ngroupLists:\n  []\nchannels:\n  - am:\n      id: ch1\n      name: BER EM\n      rxOnly: true\n      rxFrequency: 121.5 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - am:\n      id: ch2\n      name: BER ATIS\n      rxOnly: true\n      rxFrequency: 123.78 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - am:\n      id: ch3\n      name: BER ARR\n      rxOnly: true\n      rxFrequency: 121.13 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - am:\n      id: ch4\n      name: BER Tower S\n      rxOnly: true\n      rxFrequency: 118.805 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - am:\n      id: ch5\n      name: BER Tower N\n      rxOnly: true\n      rxFrequency: 120.03 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\nzones:\n  - id: zone1\n    name: BER Air\n    A: [ch1, ch2, ch3, ch4, ch5]\n    B: []\ncommercial:\n  encryptionKeys:\n    []\nsms:\n  format: DMR\n  templates:\n    []\n...\n"
  },
  {
    "path": "examples/kw.conf",
    "content": "#\n# Configuration generated So. Juni 27 17:49:02 2021 by qdrm, version 0.8.0\n# see https://dm3mat.darc.de/qdmr for details.\n#\n\n# Unique DMR ID and name (quoted) of this radio.\nID: 2621370\nName: \"DM3MAT\"\n\n# Text displayed when the radio powers up (quoted).\nIntroLine1: \"qDMR\"\nIntroLine2: \"DM3MAT\"\n\n# Microphone amplification, value 1..10:\nMICLevel: 6\n\n# Speech-synthesis ('On' or 'Off'):\nSpeech: Off\n\n# Table of digital channels.\n# 1) Channel number.\n# 2) Name in quotes. E.g., \"NAME\" \n# 3) Receive frequency in MHz\n# 4) Transmit frequency or +/- offset in MHz\n# 5) Transmit power: Max, High, Mid, Low or Min\n# 6) Scan list: - or index in Scanlist table\n# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n# 8) Receive only: -, +\n# 9) Admit criteria: -, Free, Color\n# 10) Color code: 0, 1, 2, 3... 15\n# 11) Time slot: 1 or 2\n# 12) Receive group list: - or index in Grouplist table\n# 13) Contact for transmit: - or index in Contacts table\n# 14) GPS System: - or index in GPS table.\n# 15) Roaming zone: -, + or index in roaming-zone table.\n# 16) Radio ID: -, index in radio ID list above. - means default ID.\n#\nDigital Name                Receive    Transmit   Power Scan TOT RO Admit  CC TS RxGL TxC GPS Roam ID\n1       \"DB0LDS TS1\"        439.56250  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n2       \"Sa/Th DB0LDS TS1\"  439.56250  -7.60000   High  -    -   -  Color  1  1  3    8   -   +    -  # Sa/Th\n3       \"TG8 DB0LDS TS2\"    439.56250  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n4       \"TG9 DB0LDS TS2\"    439.56250  -7.60000   High  1    -   -  Color  1  2  2    6   -   +    -  # Local\n5       \"BB DB0LDS TS2\"     439.56250  -7.60000   High  1    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n6       \"HRV DB0LDS TS1\"    439.56250  -7.60000   High  1    -   -  Color  1  1  7    11  -   +    -  # HamRadio Village\n7       \"DM0TZN TS1\"        438.82500  -7.60000   High  -    -   -  Color  1  1  1    1   -   +    -  # WW\n8       \"TG8 DM0TZN TS2\"    438.82500  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n9       \"TG9 DM0TZN TS2\"    438.82500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n10      \"BB DM0TZN TS2\"     438.82500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n11      \"DB0LOS TS1\"        438.47500  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n12      \"R/TG9 DB0LOS TS2\"  438.47500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n13      \"R/TG9 DM0TT TS1\"   439.08750  -7.60000   High  -    -   -  Color  1  1  2    6   -   +    -  # Local\n14      \"TG8 DM0TT TS2\"     439.08750  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n15      \"TG9 DM0TT TS2\"     439.08750  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n16      \"BB DM0TT TS2\"      439.08750  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n17      \"TG9 DB0KK TS1\"     439.53750  -7.60000   High  -    -   -  Color  1  1  2    6   -   +    -  # Local\n18      \"BB DB0KK TS2\"      439.53750  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n19      \"DB0OUD TS1\"        438.62500  -7.60000   High  -    -   -  Color  1  1  1    1   -   +    -  # WW\n20      \"TG8 DB0OUD TS2\"    438.62500  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n21      \"TG9 DB0OUD TS2\"    438.62500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n22      \"BB DB0OUD TS2\"     438.62500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n23      \"TG9 DB0TU TS1\"     439.57500  -7.60000   High  -    -   -  Color  1  1  2    6   -   +    -  # Local\n24      \"BB DB0TU TS2\"      439.57500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n25      \"DM0MOT TS1\"        439.52500  -7.60000   High  -    -   -  Color  1  1  1    1   -   +    -  # WW\n26      \"TG8 DM0MOT TS2\"    439.52500  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n27      \"TG9 DM0MOT TS2\"    439.52500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n28      \"BB DM0MOT TS2\"     439.52500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n29      \"DB0TA TS1\"         439.47500  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n30      \"TG8 DB0TA TS2\"     439.47500  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n31      \"TG9 DB0TA TS2\"     439.47500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n32      \"BB DB0TA TS2\"      439.47500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n33      \"DB0FX TS1\"         439.45000  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n34      \"TG8 DB0FX TS2\"     439.45000  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n35      \"TG9 DB0FX TS2\"     439.45000  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n36      \"BB DB0FX TS2\"      439.45000  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n37      \"DB0PDM TS1\"        438.40000  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n38      \"TG8 DB0PDM TS2\"    438.40000  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n39      \"TG9 DB0PDM TS2\"    438.40000  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n40      \"BB DB0PDM TS2\"     438.40000  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n41      \"DB0BRB TS1\"        439.48750  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n42      \"TG8 DB0BRB TS2\"    439.48750  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n43      \"TG9 DB0BRB TS2\"    439.48750  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n44      \"BB DB0BRB TS2\"     439.48750  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n45      \"DB0SPN TS1\"        439.48750  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n46      \"R/TG9 DB0SPN TS2\"  439.48750  -7.60000   High  -    -   -  Color  1  1  2    6   -   +    -  # Local\n47      \"DB0NLS TS1\"        439.51250  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n48      \"R/TG9 DB0NLS TS2\"  439.51250  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n49      \"DB0LS TS1\"         438.22500  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n50      \"TG8 DB0LS TS2\"     438.22500  -7.60000   High  -    -   -  Color  1  2  2    5   -   +    -  # Regional\n51      \"TG9 DB0LS TS2\"     438.22500  -7.60000   High  -    -   -  Color  1  2  2    6   -   +    -  # Local\n52      \"BB DB0LS TS2\"      438.22500  -7.60000   High  -    -   -  Color  1  2  2    7   -   +    -  # Bln/Brb\n53      \"DM0LC TS1\"         439.38750  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n54      \"TG8 DM0LC TS2\"     439.38750  -7.60000   High  -    -   -  Color  1  2  3    5   -   +    -  # Regional\n55      \"TG9 DM0LC TS2\"     439.38750  -7.60000   High  -    -   -  Color  1  2  3    6   -   +    -  # Local\n56      \"Sa/Th DM0LC TS2\"   439.38750  -7.60000   High  -    -   -  Color  1  2  3    8   -   +    -  # Sa/Th\n57      \"BB DM0LC TS1\"      439.38750  -7.60000   High  -    -   -  Color  1  1  2    7   -   +    -  # Bln/Brb\n58      \"DB0FLW TS1\"        439.53750  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n59      \"TG8 DB0FLW TS2\"    439.53750  -7.60000   High  -    -   -  Color  1  2  3    5   -   +    -  # Regional\n60      \"TG9 DB0FLW TS2\"    439.53750  -7.60000   High  -    -   -  Color  1  2  3    6   -   +    -  # Local\n61      \"Sa/Th DB0FLW TS2\"  439.53750  -7.60000   High  -    -   -  Color  1  2  3    8   -   +    -  # Sa/Th\n62      \"DB0LE TS1\"         439.57500  -7.60000   High  -    -   -  Color  1  1  1    6   -   +    -  # Local\n63      \"R/TG9 DB0LE TS2\"   439.57500  -7.60000   High  -    -   -  Color  1  2  3    6   -   +    -  # Local\n64      \"DL DB0AFZ TS1\"     439.97500  -9.40000   High  -    -   -  Color  1  1  1    3   -   +    -  # DL\n65      \"BB DB0AFZ TS1\"     439.97500  -9.40000   High  -    -   -  Color  1  1  2    7   -   +    -  # Bln/Brb\n66      \"TG9 DB0AFZ TS2\"    439.97500  -9.40000   High  -    -   -  Color  1  2  5    6   -   +    -  # Local\n67      \"Hes DB0AFZ TS2\"    439.97500  -9.40000   High  -    -   -  Color  1  2  5    10  -   +    -  # Hessen\n68      \"DMR S0\"            433.45000  433.45000  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n69      \"DMR S1\"            433.61200  433.61200  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n70      \"DMR S2\"            433.62500  433.62500  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n71      \"DMR S3\"            433.63800  433.63800  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n72      \"DMR S4\"            433.65000  433.65000  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n73      \"DMR S5\"            433.66300  433.66300  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n74      \"DMR S6\"            433.67500  433.67500  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n75      \"DMR S7\"            433.68800  433.68800  High  -    -   -  Free   1  1  6    12  -   +    -  # All Call\n\n# Table of analog channels.\n# 1) Channel number.\n# 2) Name in quotes.\n# 3) Receive frequency in MHz\n# 4) Transmit frequency or +/- offset in MHz\n# 5) Transmit power: Max, High, Mid, Low or Min\n# 6) Scan list: - or index\n# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n# 8) Receive only: -, +\n# 9) Admit criteria: -, Free, Tone\n# 10) Squelch level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9\n# 11) CTCSS/DCS for receive: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable\n# 12) CTCSS/DCS for transmit: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable\n# 13) Bandwidth in kHz: 12.5, 25\n# 14) APRS system: - or index\n#\nAnalog  Name                Receive   Transmit   Power Scan TOT RO Admit  Squelch RxTone TxTone Width APRS\n76      \"DB0LDS\"            439.56250 -7.60000   High  -    -   -  -      1       67.0   67.0   12.5  -    \n77      \"DB0RAG\"            439.30000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n78      \"DB0LUD\"            438.57500 -7.60000   High  -    -   -  -      1       67.0   67.0   12.5  -    \n79      \"DB0SP-2\"           145.60000 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n80      \"DB0SP-70\"          439.42500 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n81      \"DB0ZOD-2\"          145.66250 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n82      \"DB0ZOD-70\"         438.72500 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n83      \"DB0BRL\"            145.72500 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n84      \"DB0BLO\"            439.27500 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n85      \"DB0SX\"             439.05000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n86      \"DB0TA\"             439.12500 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n87      \"DB0PDM\"            145.67500 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n88      \"DB0CBS\"            438.85000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n89      \"DB0AF\"             438.63750 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n90      \"DB0NFL\"            438.95000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n91      \"DB0LMM\"            439.30000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n92      \"DM0LEI\"            439.15000 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n93      \"DB0LEI\"            145.77500 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n94      \"DB0SML\"            439.27500 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n95      \"DB0LSA\"            145.66250 -0.60000   High  -    -   -  -      1       -      -      12.5  -    \n96      \"DB0LE\"             438.78750 -7.60000   High  -    -   -  -      1       -      -      12.5  -    \n97      \"DOK Y07\"           144.67500 144.67500  High  -    -   -  Free   1       -      -      12.5  -    \n98      \"DOK D20\"           144.65000 144.65000  High  -    -   -  Free   1       -      -      12.5  -    \n99      \"DOK D23\"           145.37500 145.37500  High  -    -   -  Free   1       -      -      12.5  -    \n100     \"DOK S31\"           144.72500 144.72500  High  -    -   -  Free   1       -      -      12.5  -    \n101     \"S20\"               145.30000 145.30000  High  -    -   -  Free   1       -      -      12.5  -    \n102     \"2m Mobil\"          145.50000 145.50000  High  -    -   -  Free   1       -      -      12.5  -    \n103     \"70cm Mobil\"        433.50000 433.50000  High  -    -   -  Free   1       -      -      12.5  -    \n104     \"APRS\"              144.80000 144.80000  High  -    -   -  Free   1       -      -      12.5  2    \n105     \"ISS APRS\"          145.82500 145.82500  High  -    -   -  Free   1       -      -      12.5  -    \n106     \"ISS FM Rep\"        437.80000 -291.81000 High  -    -   -  -      1       -      67.0   12.5  -    \n107     \"CAS-3H FM\"         437.20000 -292.85000 High  -    -   -  -      1       -      -      12.5  -    \n108     \"PO-101 FM\"         145.90000 437.50000  High  -    -   -  -      1       -      -      12.5  -    \n109     \"SO-50 FM\"          436.79000 -290.94000 High  -    -   -  -      1       -      67.0   12.5  -    \n110     \"AO-92 FM\"          145.88000 435.35000  High  -    -   -  -      1       -      67.0   12.5  -    \n111     \"AO-91 FM\"          145.96000 435.25000  High  -    -   -  -      1       -      67.0   12.5  -    \n112     \"U0\"                430.20000 430.20000  Low   -    -   -  -      1       -      -      12.5  -    \n113     \"U1\"                430.22500 430.22500  Low   -    -   -  -      1       -      -      12.5  -    \n114     \"U2\"                430.25000 430.25000  Low   -    -   -  -      1       -      -      12.5  -    \n115     \"U3\"                430.27500 430.27500  Low   -    -   -  -      1       -      -      12.5  -    \n116     \"PMR 1\"             446.00625 446.00625  Low   -    -   -  Free   1       -      -      12.5  -    \n117     \"PMR 2\"             446.01875 446.01875  Low   -    -   -  Free   1       -      -      12.5  -    \n118     \"PMR 3\"             446.03125 446.03125  Low   -    -   -  Free   1       -      -      12.5  -    \n119     \"PMR 4\"             446.04375 446.04375  Low   -    -   -  Free   1       -      -      12.5  -    \n120     \"PMR 5\"             446.05625 446.05625  Low   -    -   -  Free   1       -      -      12.5  -    \n121     \"PMR 6\"             446.06875 446.06875  Low   -    -   -  Free   1       -      -      12.5  -    \n122     \"PMR 7\"             446.08125 446.08125  Low   -    -   -  Free   1       -      -      12.5  -    \n123     \"PMR 8\"             446.09375 446.09375  Low   -    -   -  Free   1       -      -      12.5  -    \n124     \"PMR 9\"             446.10625 446.10625  Low   -    -   -  Free   1       -      -      12.5  -    \n125     \"PMR 10\"            446.11875 446.11875  Low   -    -   -  Free   1       -      -      12.5  -    \n126     \"PMR 11\"            446.13125 446.13125  Low   -    -   -  Free   1       -      -      12.5  -    \n127     \"PMR 12\"            446.14375 446.14375  Low   -    -   -  Free   1       -      -      12.5  -    \n128     \"PMR 13\"            446.15625 446.15625  Low   -    -   -  Free   1       -      -      12.5  -    \n129     \"PMR 14\"            446.16875 446.16875  Low   -    -   -  Free   1       -      -      12.5  -    \n130     \"PMR 15\"            446.18125 446.18125  Low   -    -   -  Free   1       -      -      12.5  -    \n131     \"PMR 16\"            446.19375 446.19375  Low   -    -   -  Free   1       -      -      12.5  -    \n\n# Table of channel zones.\n# 1) Zone number\n# 2) Name in quotes. \n# 3) VFO: Either A or B.\n# 4) List of channels: numbers and ranges (N-M) separated by comma\n#\nZone    Name                VFO Channels\n1       \"KW\"                A   1,4,5,2,6,11,12,13,15,16,76,77,78,79\n1       \"KW\"                B   97,101,102,103,68,69,70,71,72,73,74,75\n2       \"TH Wildau\"         A   112,113,114,115\n3       \"Hessen\"            A   64,66,67\n3       \"Hessen\"            B   102,68,69,70,71,72,73,74,75\n4       \"Berlin\"            A   16,18,22,24,28,32,36,79,80,81,82,83,84,85,86\n4       \"Berlin\"            B   98,99,102,103,68,69,70,71,72,73,74,75\n5       \"Potsdam\"           A   37,38,39,40,41,42,43,44,79,80,87\n5       \"Potsdam\"           B   102,103,68,69,70,71,72,73,74,75\n6       \"Cottbus\"           A   45,46,47,48,49,50,51,52,88,89,90\n6       \"Cottbus\"           B   102,103,68,69,70,71,72,73,74,75\n7       \"Leipzig\"           A   53,55,56,57,58,60,61,62,63,91,92,93,94,95,96\n7       \"Leipzig\"           B   100,102,103,68,69,70,71,72,73,74,75\n8       \"Sat\"               A   106,111,110,109,108,107\n8       \"Sat\"               B   105,106,111,110,109,108,107\n9       \"PMR\"               A   116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131\n9       \"PMR\"               B   116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131\n\n# Table of scan lists.\n# 1) Scan list number.\n# 2) Name in quotes.\n# 3) Priority channel 1 (50% of scans): -, Sel or index\n# 4) Priority channel 2 (25% of scans): -, Sel or index\n# 5) Designated transmit channel: Last, Sel or index\n# 6) List of channels: numbers, Sel or ranges (N-M) separated by comma\n#\nScanlist Name                PCh1 PCh2 TxCh Channels\n1        \"KW\"                -    -    -    4,5,76,77,97,102,68,112\n2        \"BerBra\"            -    -    -    5,10,16,18,22,24,32,36,28,40,44,52\n\n# Table of GPS systems.\n# 1) GPS system ID\n# 2) Name in quotes.\n# 3) Destination contact ID.\n# 4) Update period: period in ms\n# 5) Revert channel ID or '-'.\n#\nGPS  Name                Dest Period Revert\n1    \"BM ARPS\"           21   300    -     \n\n# Table of APRS systems (there is usually only one).\n# 1) APRS system ID\n# 2) Name in quotes.\n# 3) Transmit channel ID.\n# 4) Transmit period in seconds.\n# 5) Your (source) call and SSID as CALLSIGN-SSID.\n# 6) Destination call and SSID as CALLSIGN-SSID.\n# 7) Path string or '-'.\n# 7) Icon name.\n# 8) Message, optional message in quotes.\n#\nAPRS Name                Channel Period Source      Destination Path                   Icon        Message\n2    \"APRS APAT81\"       104     300    DM3MAT-7    APAT81-0    \"WIDE1-1WIDE2-1\"       \"Jogger\"    \"Y07, QRG 144.675\"\n\n# Table of contacts.\n# 1) Contact number.\n# 2) Name in quotes.\n# 3) Call type: Group, Private, All or DTMF\n# 4) Call ID: 1...16777215 or string with DTMF number\n# 5) Call receive tone: -, +\n#\nContact Name                Type    ID          RxTone\n1       \"WW\"                Group   91          -     \n2       \"EU\"                Group   92          -     \n3       \"DL\"                Group   262         -     \n4       \"DL MM\"             Group   263         -     \n5       \"Regional\"          Group   8           -     \n6       \"Local\"             Group   9           -     \n7       \"Bln/Brb\"           Group   2621        -     \n8       \"Sa/Th\"             Group   2629        -     \n9       \"SA/MV\"             Group   2620        -     \n10      \"Hessen\"            Group   2626        -     \n11      \"HamRadio Village\"  Group   3177826     -     \n12      \"All Call\"          All     16777215    -     \n13      \"Simplex TG99\"      Group   99          -     \n14      \"Cl Brandenburg\"    Group   26209       -     \n15      \"Cl Berlin\"         Group   26212       -     \n16      \"Refl Brandenburg\"  Private 4044        -     \n17      \"Refl Berlin\"       Private 4016        -     \n18      \"BM SMS\"            Private 262993      -     \n19      \"BM DAPNET\"         Private 262994      -     \n20      \"BM Parrot\"         Private 262997      -     \n21      \"BM APRS\"           Private 262999      -     \n22      \"DG2RON Ronny\"      Private 2621246     +     \n23      \"DM9KS Silvio\"      Private 2621045     +     \n24      \"DM3MAT Hannes\"     Private 2621370     +     \n25      \"DL2BQF Thomas\"     Private 2621437     +     \n26      \"DL7JOM Olaf\"       Private 2621004     +     \n27      \"DG1RPH Peter\"      Private 2621528     +     \n28      \"DO1RPH Richard\"    Private 2634160     -     \n\n# Table of group lists.\n# 1) Group list number.\n# 2) Name in quotes.\n# 3) List of contacts: numbers and ranges (N-M) separated by comma\n#\nGrouplist Name                Contacts\n1         \"DL\"                1,2,3\n2         \"Berlin/Brand\"      6,5,7\n3         \"Sa/Th\"             5,6,8\n4         \"SA/MV\"             5,6,9\n5         \"Hessen\"            5,6,10\n6         \"Simplex\"           6,13,12\n7         \"Ham Radio Village\" 11\n\n# Table of roaming zones.\n# 1) Roaming zone number\n# 2) Name in quotes. \n# 3) List of digital channels: numbers and ranges (N-M) separated by comma\n#\nRoaming Name                Channels\n1       \"Berlin/Brand\"      3,8,14,18,20,24,26,30,34,38,42,50\n\n"
  },
  {
    "path": "examples/kw.yaml",
    "content": "---\nversion: 0.14.1\nsettings:\n  introLine1: qDMR\n  introLine2: DM3MAT\n  micLevel: 6\n  speech: false\n  power: High\n  squelch: 1\n  vox: off\n  defaultID: id1\n  gnss:\n    fixedPositionEnabled: false\n    fixedPosition: \"\"\n    systems:\n      - GPS\n    units: Metric\n  dmr:\n    privateCallMatch: true\n    groupCallMatch: true\n    privateCallHangTime: 5 s\n    groupCallHangTime: 3 s\n    sendTalkerAlias: false\n    talkerAliasEncoding: Iso8\n    preamble: 100 ms\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: WW, ring: false, type: GroupCall, number: 91}\n  - dmr: {id: cont2, name: EU, ring: false, type: GroupCall, number: 92}\n  - dmr: {id: cont3, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont4, name: DL MM, ring: false, type: GroupCall, number: 263}\n  - dmr: {id: cont5, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont6, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont7, name: Bln/Brb, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont8, name: HamRadio Village, ring: false, type: GroupCall, number: 98638}\n  - dmr: {id: cont9, name: Chaoswelle, ring: false, type: GroupCall, number: 26223}\n  - dmr: {id: cont10, name: All Call, ring: false, type: AllCall, number: 16777215}\n  - dmr: {id: cont11, name: Simplex TG99, ring: false, type: GroupCall, number: 99}\n  - dmr: {id: cont12, name: Cl Brandenburg, ring: false, type: GroupCall, number: 26209}\n  - dmr: {id: cont13, name: Cl Berlin, ring: false, type: GroupCall, number: 26212}\n  - dmr: {id: cont14, name: Refl Brandenburg, ring: false, type: PrivateCall, number: 4044}\n  - dmr: {id: cont15, name: Refl Berlin, ring: false, type: PrivateCall, number: 4016}\n  - dmr: {id: cont16, name: BM SMS, ring: false, type: PrivateCall, number: 262993}\n  - dmr: {id: cont17, name: BM DAPNET, ring: false, type: PrivateCall, number: 262994}\n  - dmr: {id: cont18, name: BM Parrot, ring: false, type: PrivateCall, number: 262997}\n  - dmr: {id: cont19, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\ngroupLists:\n  - {id: grp1, name: DL, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: Berlin/Brand, contacts: [cont6, cont5, cont7, cont8]}\n  - {id: grp3, name: Simplex, contacts: [cont6, cont11]}\n  - {id: grp4, name: Ham Radio Village, contacts: [cont8, cont9]}\nchannels:\n  - dmr:\n      id: ch1\n      name: DB0LDS TS1\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp1\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch2\n      name: TG8 DB0LDS TS2\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont5\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch3\n      name: TG9 DB0LDS TS2\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch4\n      name: BB DB0LDS TS2\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont7\n      aprs: aprs1\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch5\n      name: HRV DB0LDS TS1\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp4\n      contact: cont8\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch6\n      name: ChW DB0LDS TS1\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp4\n      contact: cont9\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch7\n      name: BB DB0VIP TS2\n      rxFrequency: 431.75 MHz\n      txFrequency: 439.35 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont7\n      aprs: aprs1\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: !<!default> \"\"\n  - dmr:\n      id: ch8\n      name: DM0TZN TS1\n      rxFrequency: 438.825 MHz\n      txFrequency: 431.225 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp1\n      contact: cont1\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch9\n      name: TG8 DM0TZN TS2\n      rxFrequency: 438.825 MHz\n      txFrequency: 431.225 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont5\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch10\n      name: TG9 DM0TZN TS2\n      rxFrequency: 438.825 MHz\n      txFrequency: 431.225 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch11\n      name: BB DM0TZN TS2\n      rxFrequency: 438.825 MHz\n      txFrequency: 431.225 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont7\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch12\n      name: DB0LOS TS1\n      rxFrequency: 438.475 MHz\n      txFrequency: 430.875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp1\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch13\n      name: R/TG9 DB0LOS TS2\n      rxFrequency: 438.475 MHz\n      txFrequency: 430.875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch14\n      name: R/TG9 DM0TT TS1\n      rxFrequency: 439.0875 MHz\n      txFrequency: 431.4875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: ColorCode\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont6\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch15\n      name: DMR S0\n      rxFrequency: 433.45 MHz\n      txFrequency: 433.45 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch16\n      name: DMR S1\n      rxFrequency: 433.612 MHz\n      txFrequency: 433.612 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch17\n      name: DMR S2\n      rxFrequency: 433.625 MHz\n      txFrequency: 433.625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch18\n      name: DMR S3\n      rxFrequency: 433.638 MHz\n      txFrequency: 433.638 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch19\n      name: DMR S4\n      rxFrequency: 433.65 MHz\n      txFrequency: 433.65 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch20\n      name: DMR S5\n      rxFrequency: 433.663 MHz\n      txFrequency: 433.663 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch21\n      name: DMR S6\n      rxFrequency: 433.675 MHz\n      txFrequency: 433.675 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - dmr:\n      id: ch22\n      name: DMR S7\n      rxFrequency: 433.688 MHz\n      txFrequency: 433.688 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Free\n      colorCode: 1\n      timeSlot: TS1\n      radioId: !<!default> \"\"\n      groupList: grp3\n      contact: cont10\n      roaming: !<!default> \"\"\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: High\n  - fm:\n      id: ch23\n      name: DB0LDS\n      rxFrequency: 439.5625 MHz\n      txFrequency: 431.9625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch24\n      name: DB0RAG\n      rxFrequency: 439.3 MHz\n      txFrequency: 431.7 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch25\n      name: DB0LUD\n      rxFrequency: 438.575 MHz\n      txFrequency: 430.975 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch26\n      name: DB0SP-2\n      rxFrequency: 145.6 MHz\n      txFrequency: 145 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch27\n      name: DB0SP-70\n      rxFrequency: 439.425 MHz\n      txFrequency: 431.825 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch28\n      name: S20\n      rxFrequency: 145.3 MHz\n      txFrequency: 145.3 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch29\n      name: 2m Mobil\n      rxFrequency: 145.5 MHz\n      txFrequency: 145.5 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch30\n      name: 70cm Mobil\n      rxFrequency: 433.5 MHz\n      txFrequency: 433.5 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch31\n      name: APRS\n      rxFrequency: 144.8 MHz\n      txFrequency: 144.8 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: High\n  - fm:\n      id: ch32\n      name: U0\n      rxFrequency: 430.2 MHz\n      txFrequency: 430.2 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: Low\n  - fm:\n      id: ch33\n      name: U1\n      rxFrequency: 430.225 MHz\n      txFrequency: 430.225 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: Low\n  - fm:\n      id: ch34\n      name: U2\n      rxFrequency: 430.25 MHz\n      txFrequency: 430.25 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: Low\n  - fm:\n      id: ch35\n      name: U3\n      rxFrequency: 430.275 MHz\n      txFrequency: 430.275 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: Low\nzones:\n  - id: zone1\n    name: KW\n    A: [ch1, ch3, ch4, ch7, ch5, ch6, ch12, ch13, ch14, ch23, ch24, ch25, ch26]\n    B: [ch28, ch29, ch30, ch15, ch16, ch17, ch18, ch19, ch20, ch21, ch22]\n  - id: zone2\n    name: TH Wildau\n    A: [ch32, ch33, ch34, ch35]\n    B: []\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch3, ch4, ch23, ch24, ch29, ch15, ch32]\n  - id: scan2\n    name: BerBra\n    channels: [ch4, ch11]\n  - id: scan3\n    name: PMR\n    channels: []\npositioning:\n  - dmr:\n      id: aprs1\n      name: BM ARPS\n      contact: cont19\n      revert: !<!selected> \"\"\n      period: 5 min\n  - aprs:\n      id: aprs2\n      name: APRS APAT81\n      revert: ch31\n      icon: Jogger\n      message: Y07, QRG 144.675\n      period: 5 min\n      destination: APAT81-0\n      source: DM3MAT-7\n      path: [WIDE1-1, WIDE2-1]\nroamingChannels:\n  - id: rch1\n    name: R DB0LDS\n    rxFrequency: 439.5625 MHz\n    txFrequency: 431.9625 MHz\n    colorCode: 1\n  - id: rch2\n    name: R DM0TZN\n    rxFrequency: 438.825 MHz\n    txFrequency: 431.225 MHz\n    colorCode: 1\nroamingZones:\n  - id: roam1\n    name: Berlin/Brand\n    channels:\n      - rch1\n      - rch2\ncommercial:\n  encryptionKeys:\n    []\nsms:\n  format: DMR\n  templates:\n    []\n...\n"
  },
  {
    "path": "examples/minimal.conf",
    "content": "#\n# Configuration generated Sa. März 27 11:44:12 2021 by qdrm, version 0.6.2\n# see https://dm3mat.darc.de/qdmr for details.\n#\n\n# Unique DMR ID and name (quoted) of this radio.\nID: 2621370\nName: \"DM3MAT\"\n\n# Text displayed when the radio powers up (quoted).\nIntroLine1: \"\"\nIntroLine2: \"\"\n\n# Microphone amplification, value 1..10:\nMICLevel: 2\n\n# Speech-synthesis ('On' or 'Off'):\nSpeech: Off\n\n# Table of digital channels.\n# 1) Channel number.\n# 2) Name in quotes. E.g., \"NAME\" \n# 3) Receive frequency in MHz\n# 4) Transmit frequency or +/- offset in MHz\n# 5) Transmit power: Max, High, Mid, Low or Min\n# 6) Scan list: - or index in Scanlist table\n# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n# 8) Receive only: -, +\n# 9) Admit criteria: -, Free, Color\n# 10) Color code: 0, 1, 2, 3... 15\n# 11) Time slot: 1 or 2\n# 12) Receive group list: - or index in Grouplist table\n# 13) Contact for transmit: - or index in Contacts table\n# 14) GPS System: - or index in GPS table.\n# 15) Roaming zone: -, + or index in roaming-zone table.\n#\nDigital Name                Receive    Transmit   Power Scan TOT RO Admit  CC TS RxGL TxC GPS Roam\n1       \"L9 DB0LDS\"         439.56250  -7.60000   High  -    -   -  -      1  2  1    1   -   -    # Local\n2       \"BB DB0LDS\"         439.56250  -7.60000   High  -    -   -  -      1  2  1    3   -   -    # BB\n3       \"DL DB0LDS\"         439.56250  -7.60000   High  -    -   -  -      1  1  2    4   -   -    # DL\n\n# Table of analog channels.\n# 1) Channel number.\n# 2) Name in quotes.\n# 3) Receive frequency in MHz\n# 4) Transmit frequency or +/- offset in MHz\n# 5) Transmit power: Max, High, Mid, Low or Min\n# 6) Scan list: - or index\n# 7) Transmit timeout timer in seconds: 0, 15, 30, 45... 555\n# 8) Receive only: -, +\n# 9) Admit criteria: -, Free, Tone\n# 10) Squelch level: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9\n# 11) CTCSS/DCS for receive: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable\n# 12) CTCSS/DCS for transmit: frequency (e.g, 67.0), DCS number (e.g., n023 or i023) or '-' to disable\n# 13) Bandwidth in kHz: 12.5, 25\n# 14) APRS system: - or index\n#\nAnalog  Name                Receive   Transmit   Power Scan TOT RO Admit  Squelch RxTone TxTone Width APRS\n\n# Table of channel zones.\n# 1) Zone number\n# 2) Name in quotes. \n# 3) VFO: Either A or B.\n# 4) List of channels: numbers and ranges (N-M) separated by comma\n#\nZone    Name                VFO Channels\n1       \"Zu Hause\"          A   1,2\n1       \"Zu Hause\"          B   3\n\n# Table of scan lists.\n# 1) Scan list number.\n# 2) Name in quotes.\n# 3) Priority channel 1 (50% of scans): -, Sel or index\n# 4) Priority channel 2 (25% of scans): -, Sel or index\n# 5) Designated transmit channel: Last, Sel or index\n# 6) List of channels: numbers, Sel or ranges (N-M) separated by comma\n#\nScanlist Name                PCh1 PCh2 TxCh Channels\n\n# Table of GPS systems.\n# 1) GPS system ID\n# 2) Name in quotes.\n# 3) Destination contact ID.\n# 4) Update period: period in ms\n# 5) Revert channel ID or '-'.\n#\nGPS  Name                Dest Period Revert\n\n# Table of APRS systems (there is usually only one).\n# 1) APRS system ID\n# 2) Name in quotes.\n# 3) Transmit channel ID.\n# 4) Transmit period in seconds.\n# 5) Your (source) call and SSID as CALLSIGN-SSID.\n# 6) Destination call and SSID as CALLSIGN-SSID.\n# 7) Path string or '-'.\n# 7) Icon name.\n# 8) Message, optional message in quotes.\n#\nAPRS Name                Channel Period Source      Destination Path                   Icon        Message\n\n# Table of contacts.\n# 1) Contact number.\n# 2) Name in quotes.\n# 3) Call type: Group, Private, All or DTMF\n# 4) Call ID: 1...16777215 or string with DTMF number\n# 5) Call receive tone: -, +\n#\nContact Name                Type    ID          RxTone\n1       \"Local\"             Group   9           -     \n2       \"Regional\"          Group   8           -     \n3       \"BB\"                Group   2621        -     \n4       \"DL\"                Group   262         -     \n\n# Table of group lists.\n# 1) Group list number.\n# 2) Name in quotes.\n# 3) List of contacts: numbers and ranges (N-M) separated by comma\n#\nGrouplist Name                Contacts\n1         \"Local\"             1,2,3\n2         \"DL\"                4\n\n# Table of roaming zones.\n# 1) Roaming zone number\n# 2) Name in quotes. \n# 3) List of digital channels: numbers and ranges (N-M) separated by comma\n#\nRoaming Name                Channels\n\n"
  },
  {
    "path": "examples/minimal.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - digital:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - digital:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - digital:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - digital:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "examples/pmr.yaml",
    "content": "---\nversion: 0.14.1\nsettings:\n  introLine1: qDMR\n  introLine2: DM3MAT\n  micLevel: 6\n  speech: false\n  power: High\n  squelch: 1\n  vox: off\n  gnss:\n    fixedPositionEnabled: false\n    fixedPosition: \"\"\n    systems:\n      - GPS\n    units: Metric\n  dmr:\n    privateCallMatch: true\n    groupCallMatch: true\n    privateCallHangTime: 5 s\n    groupCallHangTime: 3 s\n    sendTalkerAlias: false\n    talkerAliasEncoding: Iso8\n    preamble: 100 ms\nradioIDs:\n  []\ncontacts:\n  []\ngroupLists:\n  []\nchannels:\n  - fm:\n      id: ch1\n      name: PMR 1\n      rxFrequency: 446.00625 MHz\n      txFrequency: 446.00625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch2\n      name: PMR 2\n      rxFrequency: 446.01875 MHz\n      txFrequency: 446.01875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch3\n      name: PMR 3\n      rxFrequency: 446.03125 MHz\n      txFrequency: 446.03125 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch4\n      name: PMR 4\n      rxFrequency: 446.04375 MHz\n      txFrequency: 446.04375 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch5\n      name: PMR 5\n      rxFrequency: 446.05625 MHz\n      txFrequency: 446.05625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch6\n      name: PMR 6\n      rxFrequency: 446.06875 MHz\n      txFrequency: 446.06875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch7\n      name: PMR 7\n      rxFrequency: 446.08125 MHz\n      txFrequency: 446.08125 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch8\n      name: PMR 8\n      rxFrequency: 446.09375 MHz\n      txFrequency: 446.09375 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch9\n      name: PMR 9\n      rxFrequency: 446.10625 MHz\n      txFrequency: 446.10625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch10\n      name: PMR 10\n      rxFrequency: 446.11875 MHz\n      txFrequency: 446.11875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch11\n      name: PMR 11\n      rxFrequency: 446.13125 MHz\n      txFrequency: 446.13125 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch12\n      name: PMR 12\n      rxFrequency: 446.14375 MHz\n      txFrequency: 446.14375 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch13\n      name: PMR 13\n      rxFrequency: 446.15625 MHz\n      txFrequency: 446.15625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch14\n      name: PMR 14\n      rxFrequency: 446.16875 MHz\n      txFrequency: 446.16875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch15\n      name: PMR 15\n      rxFrequency: 446.18125 MHz\n      txFrequency: 446.18125 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch16\n      name: PMR 16\n      rxFrequency: 446.19375 MHz\n      txFrequency: 446.19375 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      scanListRef: scan1\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\nzones:\n  - id: zone1\n    name: PMR\n    A: [ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16]\n    B: [ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16]\nscanLists:\n  - id: scan1\n    name: PMR\n    channels: [ch1, ch2, ch3, ch4, ch5, ch6, ch7, ch8, ch9, ch10, ch11, ch12, ch13, ch14, ch15, ch16]\ncommercial:\n  encryptionKeys:\n    []\nsms:\n  format: DMR\n  templates:\n    []\n...\n"
  },
  {
    "path": "examples/potsdam.yaml",
    "content": "---\nversion: 0.14.0\nsettings:\n  introLine1: qDMR\n  introLine2: DM3MAT\n  micLevel: 5\n  speech: false\n  power: High\n  squelch: 2\n  vox: off\n  defaultID: id1\n  gnss:\n    fixedPositionEnabled: false\n    fixedPosition: \"\"\n    systems:\n      - GPS\n    units: Metric\n  dmr:\n    privateCallMatch: true\n    groupCallMatch: true\n    privateCallHangTime: 5 s\n    groupCallHangTime: 3 s\n    sendTalkerAlias: false\n    talkerAliasEncoding: Iso8\n    preamble: 100 ms\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont2, name: Emcom EU, ring: false, type: GroupCall, number: 9112}\n  - dmr: {id: cont3, name: Berlin/Brandenburg, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont5, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont6, name: BM SMS, ring: false, type: PrivateCall, number: 262993}\n  - dmr: {id: cont7, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont8, name: TAC1, ring: false, type: GroupCall, number: 26200}\n  - dmr: {id: cont9, name: TAC 2, ring: false, type: GroupCall, number: 26299}\n  - dmr: {id: cont10, name: TAC 3, ring: false, type: GroupCall, number: 26233}\n  - dmr: {id: cont11, name: TAC 4, ring: false, type: GroupCall, number: 26266}\ngroupLists:\n  - {id: grp1, name: DL, contacts: [cont1, cont2, cont8, cont9, cont10, cont11]}\n  - {id: grp2, name: BB, contacts: [cont3, cont4, cont5]}\nchannels:\n  - fm:\n      id: ch1\n      name: DB0PDM\n      rxFrequency: 145.675 MHz\n      txFrequency: 145.075 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - dmr:\n      id: ch2\n      name: DL DB0PDM TS1\n      rxFrequency: 438.4 MHz\n      txFrequency: 430.8 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      radioId: id1\n      groupList: grp1\n      contact: cont1\n      aprs: aprs1\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: !<!default> \"\"\n  - dmr:\n      id: ch3\n      name: BB DB0PDM TS2\n      rxFrequency: 438.4 MHz\n      txFrequency: 430.8 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      radioId: id1\n      groupList: grp2\n      contact: cont3\n      aprs: aprs1\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: !<!default> \"\"\n  - dmr:\n      id: ch4\n      name: L9 DB0PDM TS2\n      rxFrequency: 438.4 MHz\n      txFrequency: 430.8 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      radioId: id1\n      groupList: grp2\n      contact: cont4\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: !<!default> \"\"\n  - dmr:\n      id: ch5\n      name: BB DB0AVH TS2\n      rxFrequency: 438.45 MHz\n      txFrequency: 430.85 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      radioId: !<!default> \"\"\n      groupList: grp2\n      contact: cont3\n      extended:\n        talkaround: false\n        privateCallConfirm: false\n        sms: true\n        smsConfirm: false\n        dataConfirm: true\n        dcdm: false\n        loneWorker: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch6\n      name: DB0FES\n      rxFrequency: 438.225 MHz\n      txFrequency: 430.625 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch7\n      name: DB0SP VHF\n      rxFrequency: 145.6 MHz\n      txFrequency: 145 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch8\n      name: DB0SP UHF\n      rxFrequency: 439.425 MHz\n      txFrequency: 431.825 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch9\n      name: DB0LUD\n      rxFrequency: 438.575 MHz\n      txFrequency: 430.975 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone:\n        ctcss: 67.0 Hz\n      txTone:\n        ctcss: 67.0 Hz\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch10\n      name: DB0BRB\n      rxFrequency: 439.4875 MHz\n      txFrequency: 431.8875 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch11\n      name: 2m Mobil\n      rxFrequency: 145.5 MHz\n      txFrequency: 145.5 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch12\n      name: 70cm Mobil\n      rxFrequency: 433.5 MHz\n      txFrequency: 433.5 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      aprs: aprs2\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\n  - fm:\n      id: ch13\n      name: APRS\n      rxFrequency: 144.8 MHz\n      txFrequency: 144.8 MHz\n      timeout: !<!default> \"\"\n      rxOnly: false\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n      admit: Free\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      extended:\n        talkaround: false\n        reverseBurst: false\n      power: !<!default> \"\"\nzones:\n  - id: zone1\n    name: Potsdam\n    A: [ch1, ch2, ch3, ch4, ch6, ch7, ch8, ch9, ch10, ch11, ch12]\n    B: []\npositioning:\n  - dmr:\n      id: aprs1\n      name: BM APRS\n      contact: cont7\n      revert: !<!selected> \"\"\n      period: 5 min\n  - aprs:\n      id: aprs2\n      name: FM APRS\n      revert: ch13\n      icon: Jogger\n      message: Y07\n      destination: APAT81-0\n      source: DM3MAT-7\n      path: [WIDE1-1, WIDE2-1]\ncommercial:\n  encryptionKeys:\n    []\nsms:\n  format: DMR\n  templates:\n    []\n...\n"
  },
  {
    "path": "examples/sat.yaml",
    "content": "---\nversion: 0.12.0\nsettings:\n  introLine1: FM Sat\n  introLine2: DM3MAT\n  micLevel: 6\n  speech: false\n  power: Max\n  squelch: 0\n  vox: 0\n  tot: 0\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  []\ngroupLists:\n  []\nchannels:\n  - analog:\n      id: ch1\n      name: ISS APRS\n      rxOnly: false\n      admit: Free\n      bandwidth: Narrow\n      rxFrequency: 145.825 MHz\n      txFrequency: 145.825 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch2\n      name: ISS AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.81 MHz\n      txFrequency: 145.9875 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch3\n      name: ISS UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.805 MHz\n      txFrequency: 145.99 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch4\n      name: ISS APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.8 MHz\n      txFrequency: 145.99 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch5\n      name: ISS DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.795 MHz\n      txFrequency: 145.99 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch6\n      name: ISS LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.79 MHz\n      txFrequency: 145.9925 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch7\n      name: CAS-3H AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.21 MHz\n      txFrequency: 144.3525 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch8\n      name: CAS-3H UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.205 MHz\n      txFrequency: 144.35 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch9\n      name: CAS-3H APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.2 MHz\n      txFrequency: 144.35 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch10\n      name: CAS-3H DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.195 MHz\n      txFrequency: 144.35 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch11\n      name: CAS-3H LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.19 MHz\n      txFrequency: 144.3475 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch12\n      name: CAS-5A AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.61 MHz\n      txFrequency: 145.9225 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch13\n      name: CAS-5A UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.605 MHz\n      txFrequency: 145.925 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch14\n      name: CAS-5A APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.6 MHz\n      txFrequency: 145.925 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch15\n      name: CAS-5A DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.595 MHz\n      txFrequency: 145.925 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch16\n      name: CAS-5A LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.59 MHz\n      txFrequency: 145.9275 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch17\n      name: CAS-7A AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.7 MHz\n      txFrequency: 145.8975 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - analog:\n      id: ch18\n      name: CAS-7A UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.695 MHz\n      txFrequency: 145.9 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - analog:\n      id: ch19\n      name: CAS-7A APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.69 MHz\n      txFrequency: 145.9 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - analog:\n      id: ch20\n      name: CAS-7A DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.685 MHz\n      txFrequency: 145.9 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - analog:\n      id: ch21\n      name: CAS-7A LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 435.68 MHz\n      txFrequency: 145.9025 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: !<!default> \"\"\n  - analog:\n      id: ch22\n      name: PO-101 AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9025 MHz\n      txFrequency: 437.59 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 141.300003}\n      squelch: 0\n  - analog:\n      id: ch23\n      name: PO-101 UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9 MHz\n      txFrequency: 437.495 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 141.300003}\n      squelch: 0\n  - analog:\n      id: ch24\n      name: PO-101 APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9 MHz\n      txFrequency: 437.5 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 141.300003}\n      squelch: 0\n  - analog:\n      id: ch25\n      name: PO-101 DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9 MHz\n      txFrequency: 437.505 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 141.300003}\n      squelch: 0\n  - analog:\n      id: ch26\n      name: PO-101 LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.8975 MHz\n      txFrequency: 437.51 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 141.300003}\n      squelch: 0\n  - analog:\n      id: ch27\n      name: SO-50 EN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.79 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 74.4000015}\n      squelch: 0\n  - analog:\n      id: ch28\n      name: SO-50 AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.8 MHz\n      txFrequency: 145.8525 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch29\n      name: SO-50 UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.795 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch30\n      name: SO-50 APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.79 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch31\n      name: SO-50 DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.785 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch32\n      name: SO-50 LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.77 MHz\n      txFrequency: 145.8475 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch33\n      name: AO-27 AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.805 MHz\n      txFrequency: 145.8475 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch34\n      name: AO-27 UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.8 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch35\n      name: AO-27 APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.795 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch36\n      name: AO-27 DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.79 MHz\n      txFrequency: 145.85 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch37\n      name: AO-27 LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.785 MHz\n      txFrequency: 145.8525 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch38\n      name: AO-92 AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.8825 MHz\n      txFrequency: 435.34 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch39\n      name: AO-92 UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.88 MHz\n      txFrequency: 435.345 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch40\n      name: AO-92 APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.88 MHz\n      txFrequency: 435.35 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch41\n      name: AO-92 DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.88 MHz\n      txFrequency: 435.355 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch42\n      name: AO-92 LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.8775 MHz\n      txFrequency: 435.36 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch43\n      name: AO-91 AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9625 MHz\n      txFrequency: 435.24 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch44\n      name: AO-91 UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.96 MHz\n      txFrequency: 435.245 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch45\n      name: AO-91 APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.96 MHz\n      txFrequency: 435.25 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch46\n      name: AO-91 DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.96 MHz\n      txFrequency: 435.255 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch47\n      name: AO-91 LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 145.9575 MHz\n      txFrequency: 435.26 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      txTone: {ctcss: 67}\n      squelch: 0\n  - analog:\n      id: ch48\n      name: TEVEL AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.41 MHz\n      txFrequency: 145.9675 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch49\n      name: TEVEL UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.405 MHz\n      txFrequency: 145.97 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch50\n      name: TEVEL APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.4 MHz\n      txFrequency: 145.97 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch51\n      name: TEVEL DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.395 MHz\n      txFrequency: 145.97 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch52\n      name: TEVEL LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 436.39 MHz\n      txFrequency: 145.9725 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch53\n      name: UVSQ AOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.03 MHz\n      txFrequency: 145.9025 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch54\n      name: UVSQ UP\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.025 MHz\n      txFrequency: 145.905 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch55\n      name: UVSQ APO\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.02 MHz\n      txFrequency: 145.905 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch56\n      name: UVSQ DWN\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.015 MHz\n      txFrequency: 145.905 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\n  - analog:\n      id: ch57\n      name: UVSQ LOS\n      rxOnly: false\n      admit: Always\n      bandwidth: Narrow\n      rxFrequency: 437.01 MHz\n      txFrequency: 145.9075 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: !<!default> \"\"\n      squelch: 0\nzones:\n  - id: zone1\n    name: AO-27\n    A: [ch33, ch34, ch35, ch36, ch37]\n    B: []\n  - id: zone2\n    name: AO-91 FOX-1B\n    A: [ch43, ch44, ch45, ch46, ch47]\n    B: []\n  - id: zone3\n    name: AO-92 FOX-1D\n    A: [ch38, ch39, ch40, ch41, ch42]\n    B: []\n  - id: zone4\n    name: CAS-3H Lilac-2\n    A: [ch7, ch8, ch9, ch10, ch11]\n    B: []\n  - id: zone5\n    name: CAS-5A FO-118\n    A: [ch12, ch13, ch14, ch15, ch16]\n    B: []\n  - id: zone6\n    name: CAS-7A\n    A: [ch17, ch18, ch19, ch20, ch21]\n    B: []\n  - id: zone7\n    name: ISS FM Rep\n    A: [ch2, ch3, ch4, ch5, ch6]\n    B: []\n  - id: zone8\n    name: PO-101 Diwata-2\n    A: [ch22, ch23, ch24, ch25, ch26]\n    B: []\n  - id: zone9\n    name: SO-50 SaudiSat-1C\n    A: [ch27, ch28, ch29, ch30, ch31, ch32]\n    B: []\n  - id: zone10\n    name: TEVEL 1-8\n    A: [ch48, ch49, ch50, ch51, ch52]\n    B: []\n  - id: zone11\n    name: UVSQ\n    A: [ch53, ch54, ch55, ch56, ch57]\n    B: []\ncommercial:\n  encryptionKeys:\n    []\n...\n"
  },
  {
    "path": "i18n/Makefile",
    "content": "update:\n\t/usr/lib/qt6/bin/lupdate ../src/*.cc ../src/*.hh ../src/*.ui ../shared/ui/*.ui -noobsolete -ts *.ts\n"
  },
  {
    "path": "i18n/de.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"de\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation>Rauschsperre</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>APRS System erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>APRS System bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation>[Ausgewählt]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>Über qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Unterstützte Geräte</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Nicht gespeicherte Änderungen am Codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Es gibt nicht gespeicherte Änderungen am Codeplug. Diese gehen verloren, wenn Sie fortfahren.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Codeplug öffnen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>Codeplug Dateien (*.yaml);;Codeplug Dateien, altes Format (*.conf *.csv *.txt);;Alle Dateien (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Kann Datei nicht öffnen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Kann Codeplug nicht aus Datei &apos;%1&apos; lesen: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Kann Codeplug nicht lesen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Codeplug speichern</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Codeplug Dateien (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Nutze bitte das neue YAML-Format.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Das Speichern der Config im alten tabellenbasierten Format wurde mit Version 0.9.0 deaktiviert. Das Einlesen dieser Dateien funktioniert nach wie vor.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Kann Codeplug nicht in Datei &apos;%1&apos; speichern: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Kann Codeplug nicht speichern</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Kann Codeplug nicht in Datei &apos;%1&apos; speichern.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation>Codeplug exportieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>CHIRP CSV Dateien (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation>Kann Codeplug nicht exportieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation>Kann Codeplug nich in die Datei &apos;%1&apos; exportieren:\n%2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation>Codeplug importieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation>CHIRP CSV Dateien (*.csv);;YAML Dateien (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation>Kann Codeplug nicht importieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation>Kann Codeplug nicht aus Datei &apos;%1&apos; importieren: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation>Kann Datei &apos;%1&apos; nicht verabreiten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>Kein passendes Gerät gefunden.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Verbindung zum Radio nicht möglich</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Kann keine Verbindung zum Gerät herstellen: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Gerät gefunden</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Gerät &apos;%1&apos; gefunden.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Verifizierung erfolgreich</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>Der aktuelle Codeplug passt zum Gerät &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Lese …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Lesefehler</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Gelesen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Schreibe …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Kann Rufzeichendatenbank nicht schreiben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>Das Gerät &apos;%1&apos; unterstützt keine Rufzeichendatenbank.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>Das Gerät &apos;%1&apos; unterstützt eine Rufzeichendatenbank. Diese Feature wurde jedoch noch nicht implementiert.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>qdmr kuratiert die Rufzeichendatenbank anhand Ihrer DMR ID. Es wurde keine Standard ID definiert.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Schreibe Rufzeichendatenbank …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation>Kann Satelliteneinstellungen nicht schreiben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation>Das erkannte Funkgerät &apos;%1&apos; unterstützt kein Statellitentracking.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation>Das erkannte Funkgerät bietet zwar Satellitentracking, jedoch ist das Feature noch nicht implementiert.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation>Satellitenkonfiguration schreiben …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Schreibfehler</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Geschrieben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (Alias für %2 %3)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Kein Gerät erkannt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>Kein bekanntes Gerät erkannt.</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Schmal (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Breit (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation>Kanal bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hinweis:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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;amp; TX Frequenzen sowie die CTCSS Subtoneinstellungen ausgefüllt, insofern vorhanden.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;ausblenden&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">RX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">TX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\">TX Ablage</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Leistung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Maximum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Hoch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Mittel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Niedrig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Standard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">TX Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation>VOX Empfindlichkeit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Nur RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Scanliste</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\">Keine Ablage (simplex)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\">Positive Ablage</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\">Negative Ablage</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Wählen sie zunächst einen Kanal aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>Um einen Kanal zu klonen, wählen Sie zunächst einen Kanal aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Kann Kanal nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Kann Kanal nicht löschen: Wählen Sie zunächst einen Kanal aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Kanal löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Kanal &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>%1 Kanäle löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation>Kanal hinzufügen …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Kanal klonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Kanal löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation>FM Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation>Fügt einen neuen FM Kanal hinzu</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation>DMR Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation>Fügt einen neuen DMR Kanal hinzu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation>AM Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation>Fügt einen neuen AM Kanal hinzu.</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation>AM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>[Standard]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Maximum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Hoch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Mittel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Niedrig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Ein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Immer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Frei</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Farbcode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Offen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Breit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Schmal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>RX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>TX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Leistung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>Nur RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Sendevoraussetzung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Scanliste</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Zonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>Empfangsgruppe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>TX Kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Rauschsperre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>RX Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>TX Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Bandbreite</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Wählen Sie einen Kanal aus:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation>Codeplugs vereinigen …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Konfliktlösungsstrategien:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Wenn einige importierte Objekte (Kanäle, Kontakte, …) schon existieren, wähle aus, wie diese Konflikte aufgelöst werden für einzelne Objekte und Listen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation>Objekte sind atomische Elemente wie Radio IDs, Kanäle, Kontakte und Roamingkamäle.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation>Objekte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation>Ignorieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation>Überschreiben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation>Duplizieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation>Listen sind alle Elemente, die andere Objekte entalten wie Empfangsgruppen, Scanlisten und Roamingzonen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation>Listen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation>Vereinigen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation>Doppelungen ignorieren.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation>Ersetzt jedes doppelte Objekt mit dem importieten Objekt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation>Importiert jedes doppelte Objekt mit einem veränderten Namen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation>Ignoriert jede Doppelung.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation>Ersetzt jede doppelte Liste mit der importierten Liste.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation>Importiert doppelte Listen unter veränderten Namen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation>Vereinigt doppelte Listen.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Kann Elemente nicht verschieben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Kann Elemente nicht verschieben: Wählen Sie zunächst mind. ein Element aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Verschiebe die ausgewählten Elemente an den Anfang.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Verschiebe die ausgewählten Elemente zehn Positionen nach oben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Verschiebe die ausgewählten Elemente um eine Position nach oben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Verschiebe die ausgewählten Elemente um eine Position nach unten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Verschiebe die ausgewählten Elemente um zehn Position nach unten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Verschiebe die ausgewählten Elemente ans Ende.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Kann Elemente nicht verschieben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Kann Elemente nicht verschieben: Wählen Sie zunächst mind. ein Element aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation>Kann Element nicht verschieben, solange Filter oder Sortierungen angewendet werden.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Verschiebe die ausgewählten Elemente an den Anfang.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Verschiebe die ausgewählten Elemente zehn Positionen nach oben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Verschiebe die ausgewählten Elemente um eine Position nach oben.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Verschiebe die ausgewählten Elemente um eine Position nach unten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Verschiebe die ausgewählten Elemente um zehn Position nach unten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Verschiebe die ausgewählten Elemente ans Ende.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation>Filter und Sortierung ein- &amp; ausschalten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation>Filter und Sortierungen verstecken</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation>Eine Instanz von &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation>Erweiterungsobjekt erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation>Wählen Sie die Klasse des Objektes aus, das Sie erzeugen wollen.</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Kann Kontakt nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Kann Kontakt nicht löschen: Wählen Sie zunächst einen Kontakt aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Kontakt löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Kontakt &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Kontakte löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Sollen %1 Kontakte gelöscht werden?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation>Fügt einen Kontakt zu Liste hinzu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation>M17 Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation>Füg einen M17 Kanal zur Liste hinzu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>DTMF-Kontakt hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation>Fügt einen DTMF (analog) Kontakt zur Liste hinzu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation>DMR Kontakt hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation>Fügt einen DMR Kontakt zur Liste hinzu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation>Dieser Button löscht den ausgewählten Kontakt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\">Kontakt hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Kontakt löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>An</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Direktruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Gruppenruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>Rundruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation>M17</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation>[Rundruf]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation>RX Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Immer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanal frei</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation>Abweichender Farbcode</translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation>Radio Id</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation>TX Voraussetzung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation>Farbcode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation>Zeitschlitz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation>Empfangsgruppe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">TX Kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Roamingzone</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>DMR Kontakt erstellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>DMR Kontakt bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Direktruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Gruppenruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>Rundruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation>Klingelton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Standard]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>DTMF Contakt erstellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>DTMF Kontakt bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation>Klingelton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation>Wählen Sie ein Gerät aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>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.</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation>Fehler: Unbekannt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Fehler: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Fehlerverfolgung:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation>Kann Erweiterung nicht erzeugen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation>Kann Erweiterung nicht erzeugen. Bitte melden Sie diesen Fehler.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation>Kann Listenelement nicht anlegen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation>Kann Listenelement nicht anlegen. Bitte berichten Sie diesen Fehler.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Entfernen</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation>Polizeiwache</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation>Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation>Telefon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation>DX Cluster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation>HF-Gateway</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation>kleines Flugzeug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation>mobile Satellitenstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation>Rollstuhl</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation>Scheemobil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation>Rotes Kreuz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation>Pfadfinder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation>zu Hause</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation>X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation>roter Punkt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation>Kreis 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation>Kreis 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation>Kreis 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation>Kreis 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation>Kreis 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation>Kreis 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation>Kreis 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation>Kreis 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation>Kreis 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation>Kreis 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation>Feuer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation>Campingplatz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation>Motorrad</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation>Lok</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation>Auto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation>Dateiserver</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation>Hurrikan Vorhersage</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation>Erste Hilfe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation>Mailbox</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation>Kanu</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation>Augapfel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation>Traktor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation>Grid Square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation>Hotel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation>TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation>Schule</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation>Logon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation>MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation>NTS-Station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation>Ballon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation>Polizeiauto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation>TBD</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation>RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation>Shuttle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation>SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation>Bus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation>ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation>Wetterdienst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation>Helo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation>Yacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation>MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation>Jogger</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation>Dreieck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation>PBBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation>großes Flugzeug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation>Wetterstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation>Parabolantenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation>Krankenwagen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation>Fahrrad</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation>ICP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation>Feuerwache</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation>Pferd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation>Löschzug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation>Gleiter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation>Krankenhaus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation>IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation>Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation>kleiner Truck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation>Laptop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation>Mic-E</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation>Knoten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation>EOC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation>Rover</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation>Grid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation>Antenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation>Motorboot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation>Rastplatz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation>großer Truck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation>Van</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation>Wasser</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation>XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation>Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation>Schutzhütte</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Immer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanal frei</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation>Abweichender Subton</translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Rauschsperre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation>TX Voraussetzung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">RX Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">TX Subton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Bandbreite</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation>Optionen</translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation>DMR APRS System anlegen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation>DMR APRS System bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Ausgewählt]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation>GPS System bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation>Ziel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation>Aktualisierungsperiode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation>Sendekanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Boot-Einstellungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation>Begrüßungszeile 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation>Erste Begrüßungszeile (insofern das Gerät dies unterstützt).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation>Begrüßungszeile 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation>Begrüßungszeile 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation>Zweite Begrüßungszeile (insofern das Gerät dies unterstützt).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation>Begrüßungszeile 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Audioeinstellungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>MIC Verstärkung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Sprachsynthese</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Kanal-Standardwerte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Leistung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Maximum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Hoch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Mittel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Niedrig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Rauschsperre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Offen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>TX Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation>VOX Empfindlichkeit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Kontakt</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Kann Empfangsgruppe nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Kann Empfangsgruppe nicht löschen: Wählen Sie zunächst eine Gruppe aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Empfangsgruppe löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Empfangsgruppe &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>%1 Empfangsgruppen löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Empfangsgruppe hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Empfangsgruppe löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Empfangsgruppen</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation>Kanalmodus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation>Zugriffsnummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation>TX Kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation>Position senden</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation>Sprache</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation>Daten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation>Sprache &amp; Daten</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation>M17 Kontakt bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation>Der Name des Kontakts.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation>Rufzeichen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation>Das Rufzeichen des Kontakts. Darf nicht länger als 9 Zeichen sein und nur aus A-Z, 0-9, ., / und - bestehen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Klingelton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation>Rundruf</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation>Setz diesen Kontakt als Rundrufkontakt, das gesetzte Rufzeichen wird dann ignoriert.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation>Einen M17 Kontakt hinzufügen</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>Datei</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Gerät</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Hilfe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Datenbanken</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation>Werkzeugleiste</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Neu</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Erzeugt einen neuen Codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Strg+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Öffnen …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Importiert ein Codeplug von &quot;conf&quot; Dateien.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Strg+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Speichern …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Speichert den Codeplug in einer &quot;conf&quot; Datei.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Strg+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Beenden</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Beendet qdmr.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Strg+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Detektieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Detektiert angeschlossene Geräte.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Verifizieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Verifiziert den aktuellen Codeplug für angeschlossene Geräte.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Strg+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Lesen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Liest den Codeplug vom angeschlossenen Gerät.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Schreiben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Schreibt den aktuellen Codeplug auf das angeschlossene Gerät.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>Über qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Lesen Sie das Handbuch.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Einstellungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Zeigt den Einstellungsdialog an.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Rufzeichendatenbank schreiben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Schreibt die Rufzeichendatenbank auf das Gerät.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Rufzeichendatenbank aktualisieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Aktualisiert die lokale Rufzeichendatenbank im Hintergrund aus den Onlinequellen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Sprechgruppendatenbank aktualisieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Aktualisiert die Sprechgruppendatenbank im Hintergrund aus den Onlinequellen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation>Für CHIRP exportieren …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation>Exportiert alle FM Kanäle als CHIRP CSV.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation>Importieren …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation>Importiert einen Codeplug und führt diesen mit dem aktuellen Codeplug zusammen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation>Bahnelement aktualisieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation>Aktualisiert die Bahnelemente.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation>Satelliten bearbeiten …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation>Öffnet einen Editor, um die Satellitendatenbank zu bearbeiten.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation>Satelliten schreiben</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation>Schreibt die Bahnelemente und Transponderinformationen auf das angeschlossene Gerät.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation>Kann Rufzeichendatenbank nicht aktualisieren: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation>Rufzeichendatenbank aktualisiert &amp; geladen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation>Kann Sprechgruppendatenbank nicht aktualisieren: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation>Sprechgruppendatenbank aktualisiert &amp; geladen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation>Kann Orbitalelemente nicht aktualisieren: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation>Orbitalelemente aktualisiert &amp; geladen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation>Radio IDs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation>Kontakte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation>Empfangsgruppen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation>Kanäle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation>Zonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation>Scanlisten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation>Roamingkanäle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation>Roamingzonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Nicht gespeicherte Änderungen am Codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Es gibt nicht gespeicherte Änderungen am Codeplug. Diese gehen verloren, wenn Sie fortfahren.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Auswahl]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Wählen Sie einen Kanal:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation>Direktrufe anzeigen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation>Wählen Sie einen Gruppenruf aus:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation>Roamingkanäle auswählen</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Kann GPS System nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Kann GPS System nicht löschen: Wählen Sie zunächst ein GPS System aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>GPS System löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>GPS System &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>%1 GPS Systeme löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hinweis:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Verstecken&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>GPS System hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>APRS System hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation>GPS/APRS System löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation>[Keine]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation>[Auswahl]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation>Ziel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation>Periode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Nachricht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation>Kein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation>Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>Falsch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>Wahr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[Leer]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation>neues Element</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation>Eigenschaft</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Wert</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Beschreibung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation>wahr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation>falsch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation>Kein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation>Aus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[Leer]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation>Instanz von &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation>Liste von Instanzen von &apos;%1&apos;</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Empfangsgruppe erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Empfangsgruppe bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation>Kann Empfangsgruppe nicht entfernen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation>Kann Empfangsgruppe nicht löschen: Wählen Sie zunächst eine Gruppe aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Kontakt hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation>Kontakt entfernen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Kann Radio ID nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Kann Radio ID nicht löschen: Wählen Sie zunächst eine ID aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Radio ID löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Radio ID &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Scanliste löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>%1 Scanlisten löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>Standard Radio ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Radio ID hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Radio ID löschen</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation>[Keine]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation>Kann Gerät nicht automatisch detektieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation>Wählen Sie Ihr Gerät aus</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation>Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden\n\t%1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation>Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden\n\tInformationen sind kein JSON Objekt!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation>Kann Releaseinformationen nicht von https://github.com/hmatuschek/qdmr laden\n\tRelease enthält keine Informationen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation>Eine neue Version %1 von qdmr wurde freigegeben.</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation>Zeitschlitz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation>Farbcode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation>Auswahl</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation>RX Frequenz [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation>TX Frequenz [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation>TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation>TS 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation>Roamingkanal bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Roamingkanal anlegen</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Roamingkanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Roamingkanal löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Kann Roamingkanal nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Kann Roamingkanal nicht löschen: Sie müssen zunächst mindestens einen Kanal auswählen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation>Roamingkanal löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation>Roamingkanal %1 löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation>%1 Roamingkanäle löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hinweis:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Tip:&lt;/span&gt; Sie müssen die Roamingkanäle nicht explizit anlegen. Sie können diese zusammen mit der Roamingzone automatisch erzeugen lassen. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation>[Auswahl]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation>[Keine]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>RX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation>Zonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>TX Frequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation>Roaming Kanal</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation>%1 (mit %2 Kanälen)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation>Roamingzone</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Roamingzone erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation>Roamingzone bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation>Name:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation>Kanal entfernen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation>Erweiterung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Roamingkanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>DMR Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation>Kann Kanäle nicht entfernen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation>Kann Kanäle nicht entfernen. Wählen sie zunächst mindestens einen Kanal aus.</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation>Roamingzone erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation>Eine Roamingzone aus allen Kanälen mit diesen Gruppenrufen erzeugen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Kann Roamingzone nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Kann Roamingzone nicht löschen: Wählen Sie zunächst eine Zone aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Roamingzone löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Roamingzone &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Roamingzonen löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation>%1 Roamingzonen löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Ausblenden&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Roamingzone hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation>Roamingzone erzeugen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Roamingzone löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Standard]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation>Satellitendatenbank bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation>Hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation>Löschen</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation>Wähle einen Satelliten aus</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation>Satellitentransponder bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation>FM Sprachtransponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation>Uplinkfrequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation>Uplinksubton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation>Downlinkfrequenz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation>Downlinksubton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation>APRS-Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation>Funkbake</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation>Bakenfrequenz</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Scanliste bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation>Primärkanal (50%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation>Sekundärer Kanal (25%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation>Sendekanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Kanal hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation>Kanal entfernen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Scanliste erstellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation>[Kein]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation>[Auswahl]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation>[Letzter]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Kann Scanliste nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Kann Scanliste nicht löschen: Wählen Sie zunächst eine Scanliste aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Scanliste löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Scanliste &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Scanlisten löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>%1 Scanlisten löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Scanliste hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Scanliste löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation>Scanliste</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation>%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation>Kein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation>CTCSS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation>DCS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation>Hz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation>Invertiert</translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Warnung!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Einstellungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Position</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>Systemposition</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Locator</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation>Repeaterinformationsquellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation>aktivieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation>Geräteprogrammierung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Codeplug updaten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>GPS automatisch einschalten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation>Wenn ein GPS/APRS System für irgendeinen Kanal gesetzt wurde, wird das GPS Modul (insofern vorhanden) automatisch eingeschaltet.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation>Wenn eine Roamingzone definiert und mit einem Kanal assoziiert ist, wird automatisches Roaming aktiviert.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Roaming automatisch aktivieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation>Datenquellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>Weltweit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation>Nordamerika</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation>Programmierung</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation>Schnittstellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation>Autoerkennung deaktivieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Verifikationswarnungen ignorieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Frequenzbereiche ignorieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation>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.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation>Geräteuhr stellen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>Rufzeichendatenbank</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation>Die Anzahl der Einträge beschränken</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation>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).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation>Anzahl der Einträge der Rufzeichendatenbank</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation>Legt die Anzahl der Einträge in der Rufzeichendatenbank fest, die auf das Gerät geschrieben werden (wenn oben ausgewählt).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation>Auswahl anhand meiner DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation>Wenn ausgewählt, werden die Einträge der Rufzeichendatenbank anhand der eigenen Standard DMR ID ausgewählt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation>Auswahl anhand der Prefixe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation>Wenn ausgewählt, spezifizieren diese Komma-getrennten DMR ID prefixe jene Einträge der Rufzeichendatenbank, die auf das Gerät geschrieben werden.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Offen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation>Wenn ausgewählt, benutzt die globale Rauschsperreneinstellung.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Standard</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation>Zeitschlitz 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation>Zeitschlitz 2</translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation>Kein</translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Codeplug verifizieren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation>Der Codeplug kann nicht geschrieben werden, solange nicht all kritischen Probleme (rot) aufgelöst wurden.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Zone bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hinweis:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) aufteilen und beide separat auf das Gerät schreiben.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Ausblenden&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Kanäle A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation>hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation>entfernen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Kanäle B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation>Erweiterungen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Zone anlegen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation>Wählen sie zunächst mindestens einen Kanal aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation>Kann Kanal nicht entfernen</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Kann Zone nicht löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Kann Zone nicht löschen: Wählen Sie zunächst eine Zone aus.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Zone löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Zone &apos;%1&apos; löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation>Zonen löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>%1 Zonen löschen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Zone hinzufügen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Zone löschen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zone</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>APRS System bearbeiten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation>Quelle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation>Ziel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation>Pfad</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation>Icon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation>Periode [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation>Nachricht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Erweiterungen</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation>Zu ladende Codeplugdatei.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation>Legt das Log-level der Standardausgabe fest. Muss `debug`, `info`, `warning`, `error` oder `fatal` sein.</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/en_US.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"en\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Create APRS system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Edit APRS system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>About qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Supported Radios</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Unsaved changes to codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Open codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Cannot open file</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Cannot read codeplug from file &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Cannot read codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Save codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Codeplug Files (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Please use new YAML format.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Cannot save codeplug to file &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Cannot save codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Cannot save codeplug to file &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation>Export codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>CHIRP CSV Files (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation>Cannot export codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation>Cannot export codeplug to file &apos;%1&apos;:\n%2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation>Import codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation>Cannot import codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation>Cannot import codeplug from &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation>Do not know, how to handle file &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>No matching devices found.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Cannot connect to radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Cannot connect to radio: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Radio found</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Found device &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>No radio found</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Verification success</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>The codeplug was successfully verified with the radio &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Read …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Read error</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Read complete</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Upload …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Cannot write call-sign DB.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>The detected radio &apos;%1&apos; does not support a call-sign DB.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>No matching device was found.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Write call-sign DB …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation>Cannot write satellite config.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation>The detected radio &apos;%1&apos; does not support satellite tracking.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation>Write satellite config …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Write error</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Write complete</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (alias for %2 %3)</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Narrow (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Wide (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation>Edit Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">RX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">TX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation>Tx Offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Power</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">High</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Mid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Low</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Default</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">TX Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">VOX Level</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">RX Only</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Scan List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\">No offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\">Positive offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\">Negative offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Select a single channel first</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>To clone a channel, please select a single channel to clone.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Cannot delete channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Cannot delete channel: You have to select a channel first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Delete channel?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Delete channel &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>Delete %1 channels?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation>Add Channel …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Clone Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Delete Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation>Add FM Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation>Adds a new FM channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation>Add DMR Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation>Adds a new DMR channel.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation>Add AM Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation>Adds a new AM channel.</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation>AM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>[Default]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>High</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Mid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Low</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>On</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Always</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Free</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Color</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Wide</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Narrow</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>RX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>TX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Power</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>RX Only</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Scanlist</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>RX Group List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>TX Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>RX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>TX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Bandwidth</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Channel</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Select a channel:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation>Merging codeplugs …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, …) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation>Items</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation>Ignore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation>Override</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation>Duplicate</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation>Sets</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation>Merge</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation>Ignores any duplicate item.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation>Replaces any duplicate item with the imported one.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation>Imports any duplicate item with a modified name.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation>Ignores any duplicate set.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation>Replaces any duplicate set with the imported one.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation>Imports any duplicate set with a modified name.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation>Merges duplicate sets.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Cannot move items.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Cannot move items: You have to select at least one item first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Move selected item(s) to the top.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Move selected item(s) ten positions up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Move selected item(s) one position up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Move selected item(s) one position down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Move selected item(s) ten positions down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Move selected item(s) to the bottom.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Cannot move items.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Cannot move items: You have to select at least one item first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation>Cannot move items as long as there is some filter or sorting applied.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Move selected item(s) to the top.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Move selected item(s) ten positions up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Move selected item(s) one position up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Move selected item(s) one position down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Move selected item(s) ten positions down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Move selected item(s) to the bottom.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation>Toggle Filter and Sorting</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation>Close Sort and Filter</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation>An instance of &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation>Create extension object</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation>Select the class of object to create</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Cannot delete contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Cannot delete contact: You have to select a contact first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Delete contact?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Delete contact &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Delete contacts?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Delete %1 contacts?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation>Adds a contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation>Add M17 Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation>Adds an M17 contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>Add DTMF Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation>Adds a DTMF (analog) contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation>Add DMR Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation>Adds a DMR contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation>Delete contact button</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\">Add Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Delete Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>On</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Private Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Group Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>All Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation>M17</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation>[Broadcast]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation>RX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Always</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Channel Free</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation>Other Color-code</translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation>Radio Id</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">TX Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation>Color-code</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation>Time-slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation>Group list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">TX Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Roaming zone</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>Create DMR Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Edit DMR Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Private Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Group Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>All Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation>Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Default]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Create DTMF Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Edit DMR Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation>Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation>Select a device</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation>Error: Unknown.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Error: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Traceback:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation>Cannot create extension.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation>Cannot create extension, consider reporting a bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation>Cannot create list element.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation>Cannot create list element, consider reporting a bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Create</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Remove</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation>Police station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation>Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation>Phone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation>DX cluster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation>HF gateway</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation>Plane small</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation>Mobile satellite station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation>Wheel chair</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation>Snowmobile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation>Red cross</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation>Boy scout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation>Home</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation>X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation>Red dot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation>Circle 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation>Circle 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation>Circle 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation>Circle 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation>Circle 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation>Circle 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation>Circle 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation>Circle 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation>Circle 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation>Circle 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation>Fire</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation>Campground</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation>Motorcycle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation>Rail engine</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation>Car</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation>File server</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation>HC Future</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation>Aid station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation>BBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation>Canoe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation>Eyeball</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation>Tractor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation>Grid Square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation>Hotel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation>TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation>School</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation>Logon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation>MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation>NTS station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation>Balloon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation>Police car</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation>TBD</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation>RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation>Shuttle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation>SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation>Bus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation>ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation>Weather service</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation>Helicopter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation>Yacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation>MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation>Jogger</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation>Triangle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation>PBBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation>Plane large</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation>Weather station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation>Dish antenna</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation>Ambulance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation>Bike</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation>ICP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation>Fire station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation>Horse</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation>Fire truck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation>Glider</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation>Hospital</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation>IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation>Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation>Truck small</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation>Laptop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation>Mic-E</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation>Node</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation>EOC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation>Rover</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation>Grid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation>Antenna</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation>Power boat</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation>Truck stop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation>Truck large</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation>Van</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation>Water</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation>XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation>Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation>Shelter</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Always</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Channel Free</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation>Other Tone</translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">TX Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">RX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">TX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Bandwidth</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation>Select Flags</translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation>Create DMR APRS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation>Edit DMR APRS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation>Edit GPS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation>Update period</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation>Revert Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Boot Settings</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation>Intro Line 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation>First greeting line (if supported by the radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation>Intro line 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation>Intro Line 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation>Second greeting line (if supported by the radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation>Intro line 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Audio Settings</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>MIC Amp.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Speech Synthesis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Channel Default Values</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Power</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>High</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Mid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Low</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>Transmit Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation>VOX Level</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Contact</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Cannot delete group list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Cannot delete group lists: You have to select a group list first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Delete group list?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Delete group list &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>Delete %1 group lists?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Add Group List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Delete Group List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Group Lists</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation>Channel mode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation>Access number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation>Tx contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation>Send position</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation>Voice</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation>Data</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation>Voice + Data</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation>Edit M17 Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation>The name of the contact.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation>Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation>The call-sign of the contact. Must not be longer than 9 chars, A-Z, 0-9, ., /, -.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation>Broadcast</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation>Create M17 Contact</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>File</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Device</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Help</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Databases</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>New</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Creates a new Codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Open …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Save …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Quit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Quits the application.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Detect</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Detect connected radios.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Verify</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Verifies the current codeplug with connected radios.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Read</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Reads a codeplug from connected radios.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Write</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Writes the codeplug to the connected radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>About qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Read the handbook.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Settings</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Shows settings dialog.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Write call-sign DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Writes call-sign DB to radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Refresh call-sign DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Refreshes the downloaded call-sign DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Refresh talk-group DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Refreshes the downloaded talk-group DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation>Export to CHIRP …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation>Exports all FM channels to CHIRP CSV.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation>Import …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation>Imports and merges a codeplug into the current one.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation>Refresh Orbital Elements</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation>Refreshes the orbital elements.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation>Edit Satellites …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation>Opens an editor to edit your satellite database.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation>Write satellites</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation>Writes the orbital elements and transponder information onto the connected device.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation>Cannot update call-sign DB: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation>Call-sign database updated &amp; loaded.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation>Cannot update talk-group DB: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation>Talk-group database updated &amp; loaded.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation>Cannot update orbital elements: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation>Orbital elements updated &amp; loaded.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation>Radio IDs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation>Contacts</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation>Group Lists</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation>Channels</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation>Scan Lists</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation>Roaming Channels</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation>Roaming Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Unsaved changes to codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Select a channel:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation>Show private calls</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation>Select a group call:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation>Select roaming channels</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Cannot delete GPS system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Cannot delete GPS system: You have to select a GPS system first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>Delete positioning system?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>Delete positioning system &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>Delete %1 positioning systems?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>Add GPS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>Add APRS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation>Delete Positioning System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation>Period</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation>None</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation>Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>False</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>True</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation>new element</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation>Property</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Value</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Description</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation>true</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation>false</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation>None</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation>Off</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation>Instance of &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation>List of %1 instances</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Create group list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Edit group list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation>Cannot remove group call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation>Cannot remove group call: You have to select at least one group call first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Add Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation>Remove Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Cannot delete radio IDs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Cannot delete radio IDs: You have to select a radio ID first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Delete radio ID?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Delete radio ID &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Delete scan lists?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Delete %1 scan lists?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>Default Radio ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Add Radio ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Delete Radio ID</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation>Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation>Cannot auto-detect radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation>Select a specific radio</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation>qdmr was updated to version %1</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation>RX Frequency [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation>TX Frequency [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation>Time Slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation>Color Code</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation>Selected</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation>Edit roaming channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Create roaming channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation>TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation>TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Add Roaming Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Delete Roaming Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Cannot delete roaming channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Cannot delete roaming channel: You have to select a channel first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation>Delete roaming channel?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation>Delete roaming channel %1?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation>Delete %1 roaming channel?</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>RX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>TX Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation>Roaming Channel</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation>%1 (containing %2 channels)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation>Roaming zone</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Create roaming zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation>Edit roaming zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation>Cannot remove channels.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation>Cannot remove channels. Select at least one channel first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation>Name:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Add Roaming Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>Add DMR Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation>Remove Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation>Extension</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation>Generate roaming zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation>Create a roaming zone by collecting all channels with these group calls.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Cannot delete roaming zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Cannot delete roaming zone: You have to select a zone first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Delete roaming zone?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Delete roaming zone &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Delete roaming zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation>Delete %1 roaming zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Add Roaming Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation>Generate Roaming Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Delete Roaming Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Default]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation>Edit satellite database</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation>Add</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation>Delete</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation>Select a satellite</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation>Edit Satellite Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation>FM Voice Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation>Uplink Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation>Uplink Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation>Downlink Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation>Downlink Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation>APRS Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation>Beacon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation>Beacon Frequency</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Edit scan list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation>Primary Channel (50%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation>Secondary Channel (25%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation>Transmit Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Add Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation>Remove Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Create scan list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation>[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation>[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation>[Last]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Cannot delete scanlist</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Cannot delete scanlist: You have to select a scanlist first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Delete scan list?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Delete scan list &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Delete scan lists?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Delete %1 scan lists?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Add Scan List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Delete Scan List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation>Scanlist</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation>%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation>None</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation>CTCSS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation>DCS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation>Hz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation>Inverted</translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Warning!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Settings</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Location</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>System location</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Locator</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation>Repeater Info Sources</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation>enable</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation>Radio Programming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Update codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>Auto-enable GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Auto-enable roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation>Data Sources</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>World</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation>North America</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation>Programming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation>Radio Interfaces</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation>disable auto-detect</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Ignore verification warnings</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Ignore frequency limits</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation>Do not set this option unless you know what you are doing.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation>Update Device Clock</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>Call-Sign DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation>Limit number of DB entries</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation>Number of DB entries</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation>Specifies the number of DB entries (if enabled above).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation>Select using my DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation>If enabled, the entries are selected using the users DMR ID.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation>Select using prefixes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation>Uses the global squelch setting if enabled.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Default</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation>Slot 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation>Slot 2</translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation>None</translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Verify Codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Edit zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Channels A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation>add</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation>remove</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Channels B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation>Extension</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Create zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation>Cannot remove channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation>Select at least one channel first.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Cannot delete zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Cannot delete zone: You have to select a zone first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Delete zone?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Delete zone &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation>Delete zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>Delete %1 zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Add Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Delete Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zone</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>Edit APRS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basic</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Name</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation>Source</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation>Path</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation>Icon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation>Update period [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation>Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation>Codeplug file to load.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/fr.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"fr\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Créer un système APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Modifier le système APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>À propos de qdrm</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Radios prises en charge</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Modifications au codeplug non enregistrées.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Il y a des modifications non enregsitrées dans le codeplug. Ces modifications seront perdues si vous continuez.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Ouvrir un codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>Fichiers Codeplug (*.yaml);;Fichiers Codeplug Files, ancien format (*.conf *.csv *.txt);;Tous les fichiers (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Impossible d&apos;ouvrir le fichier</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Impossible de lire le codeplug depuis le fichier &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Impossible de lire le codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Enregistrer le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Fichiers Codeplug (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Merci d&apos;utiliser le nouveau format YAML.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Enregistrer dans l&apos;ancien format de paramétrage tabulaire est désactivé depuis la version 0.9.0. Lire ces fichiers reste possible.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Impossible d&apos;enregistrer le codeplug dans le fichier &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Impossible d&apos;enregistrer le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Impossible d&apos;enregistrer le codeplug dans le fichier &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation>Exporter le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>Fichiers CSV CHIRP (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation>Impossible d&apos;exporter le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation>Impossible d&apos;exporter le codeplug vers le fichier &apos;%1&apos;:\n%2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation>Importer un codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation>Fichiers CSV CHIRP (*.csv);;Fichiers YAML (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation>Impossible d&apos;importer le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation>Impossible d&apos;importer le codeplug depuis &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation>Gestion du fichier &apos;%1&apos; inconnue.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>Aucune radio correspondante trouvé.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Échec de connexion à la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Échec de connexion à la radio: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Radio accessible</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Radio trouvée &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Aucune radio accessible</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>Aucune radio correspondante trouvée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Succès de la validation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>Le codeplug a été validé avec succès pour la radio &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Lecture …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Erreur de lecture</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Lecture terminée</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Téléversement …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Impossible d&apos;écrire la base d&apos;indicatifs.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>La radio &apos;%1&apos; détectée n&apos;accepte pas une base d&apos;indicatifs.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>La radio &apos;%1&apos; détectée ne gère pas de base d&apos;indicatifs. Cependant, cette fonctionnalité n&apos;est pas encore disponible.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>QDMR sélectionne les indicatifs à écrire sur la base du DMR ID par défaut de la radio. Aucun ID par défaut de configuré.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Écriture de la base des indicatifs …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation>Échec de l’écriture du paramétrage des satellites.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation>La radio &apos;%1&apos; détectée ne prend pas en charge le suivi des satellites.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation>La radio &apos;%1&apos; détectée ne prend pas en charge le suivi des satellites. Cependant, cette fonctionnalité n&apos;est pas encore mise en œuvre.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation>Écriture du paramétrage des satellites …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Erreur d&apos;écriture</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Écriture terminée</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (raccourci pour %2 %3)</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Étroite (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Large (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note&#xa0;:&lt;/span&gt; qdmr propose la complétion automatique des canaux. Commencer à saisir l&apos;indicatif d&apos;un relai, à partir de 3 caractères saisis, une requête est envoyée vers repeaterbook.com pour récupérer les répéteurs correspondants. Ces requêtes pouvant prendre du temps, les résultats sont stockés localement dans un cache.&lt;/p&gt;&lt;p&gt;Une liste déroulante apparaît permettant la sélection du relai. Une fois le relai sélectionné, les fréquences RX/TX et les codes couleurs sont complétés.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Masquer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">Fréquence RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">Fréquence TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\">Décalage TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Puissance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Haute</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Moyenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Basse</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Défaut</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">Anti-bavard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">Niveau VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">RX Uniquement</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\">Décalage positif</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\">Décalage négatif</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Commencer par sélectionner un canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>Pour dupliquer un canal, merci de sélectionner un unique canal à dupliquer.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Impossible de supprimer le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Impossible de supprimer un canal: Vous devez d&apos;abord sélectionner le canal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Supprimer le canal?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Supprimer le canal &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>Supprimer %1 canaux?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Dupliquer le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Supprimer le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\">Ajouter un canal DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>[Défaut]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Haute</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Moyenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Basse</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Non</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Oui</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Toujours</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Libre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Couleur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Tonalité</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Ouvert</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Large</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Étroite</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>Fréquence RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>Fréquence TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Puissance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Anti-bavard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>RX Uniquement</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Accepter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>Liste Groupes RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>Contact TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>Tonalité RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>Tonalité TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Largeur de bande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Canal</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Sélectionner un canal:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\">Fusion de codeplugs ...</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Stratégies de résolution de conflits:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Si des objets importés (canaux, contacts, ...) existent déjà, sélectionner comment ces conflits sont à résoudre pour les éléments et les ensembles.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation>Les éléments sont tous des objets unitaires comme IDs de radio, canaux, contacts et canaux de roaming.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation>Éléments</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation>Ignorer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation>Écraser</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation>Dupliquer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation>Les ensembles sont tous des objets contenant d&apos;autres éléments comme les listes de groupes, zones, listes de recherche et zones de roaming.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation>Ensembles</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation>Fusionner</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation>Ignorer les éléments en doublon.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation>Remplacer les éléments en doublons par ceux importés.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation>importer tout élément en doublon avec un nom modifié.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation>Ignorer tout ensemble en doublon.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation>Remplacer tout ensemble en doublon par celui importé.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation>Importer tout ensemble en doublon avec un nom modifier.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation>Fusionner les ensembles en doublon.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Impossible de déplacer les éléments.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Impossible de déplacer les éléments: sélectionner au moins un élément.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Déplacer les éléments sélectionnés tout en haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Déplacer les éléments sélectionnés 10 positions vers le haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Déplacer les éléments sélectionnés une position vers le haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Déplacer les éléments sélectionnés une position vers le bas.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Déplacer les éléments sélectionnés dix positions vers le bas.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Déplacer les éléments sélectionnés tout en bas.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Impossible de déplacer les éléments.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Impossible de déplacer les éléments: sélectionner au moins un élément.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Déplacer les éléments sélectionnés tout en haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Déplacer les éléments sélectionnés dix positions vers le haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Déplacer les éléments sélectionnés une position vers le haut.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Déplacer les éléments sélectionnés une position vers le bas.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Déplacer les éléments sélectionnés dix positions vers le bas.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Déplacer les éléments sélectionnés tout en bas.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation>Une instance de &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation>Créer un objet d&apos;extension</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation>Sélectionner la classe de l&apos;objet à créer</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Impossible de supprimer le contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Impossible de supprimer un contact: commencer par sélectionner un contact.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Supprimer le contact?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Supprimer le contact &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Supprimer les contacts?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Supprimer %1 contacts?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation>Ajouter un contact à la liste.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>Ajouter un contact DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation>Bouton de suppression de contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\">Ajouter un contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Supprimer le contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>Actif</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Inactif</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Appel privé</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Appel de groupe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>Tout appel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Nombre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation>Tonalité RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Toujours</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Canal libre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">Contact TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Zone de roaming</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>Créer un contact DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Éditer contact DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Appel privé</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Appel de groupe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>Appel à tous</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basique</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Nombre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation>Sonnerie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Défaut]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Créer un contact DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Éditer un contact DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Nombre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation>Sonnerie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation>Sélectionner une radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il y a plusieurs radios détectées ou la radio identifiée n&apos;est pas considéré comme sûr d&apos;accès. Dans tous les cas, sélectionner la radio à utiliser.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation>Erreur&#xa0;: Inconnue.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Erreur&#xa0;: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Trace d&apos;erreur&#xa0;:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation>Impossible de créer l&apos;extension.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation>Impossible de créer l&apos;extension, merci de signaler ce bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation>Impossible de créer un élément de liste.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation>Impossible de créer un élément de liste, merci de signaler ce bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Créer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Supprimer</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\">Poste de police</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\">Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Téléphone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\">Cluster DX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\">Passerelle HF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\">Avion (petit)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\">Station satellite mobile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\">Fauteuil roulant</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\">Motoneige</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\">Croix rouge</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\">Scout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\">Maison</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\">X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\">Point rouge</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\">Cercle 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\">Cercle 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\">Cercle 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\">Cercle 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\">Cercle 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\">Cercle 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\">Cercle 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\">Cercle 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\">Cercle 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\">Cercle 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\">Feu</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\">Camping</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\">Moto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\">Locomotive</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\">Voiture</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\">Serveur de fichier</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\">Futur HC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\">Poste de secours</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\">BBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\">Canoë</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\">Œil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\">Tracteur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\">Grid Square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\">Hôtel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\">TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\">École</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\">Connexion</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\">MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\">Station NTS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\">Ballon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\">Voiture de police</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\">À définir</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\">RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\">Navette</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\">SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\">Bus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\">ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\">Service météo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\">HELO</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\">Voilier</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\">MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\">Joggeur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\">Triangle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\">PBBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\">Avion (grand)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\">Sation météo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\">Antenne sattelite</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\">Ambulance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\">Vélo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\">ICP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\">Poste incendie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\">Cheval</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\">Camion de pompiers</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\">Planeur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\">Hôpital</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\">IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\">Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\">Camion (petit)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\">Ordinateur portable</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\">Mic-e</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\">Nœud</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\">EOC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\">Ballade</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\">Grille</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\">Antenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\">Bateau</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\">Aire de repos</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\">Camion (gros)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\">Van</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\">Eau</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\">XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\">Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\">Abri</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Toujours</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Canal libre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">Tonalité RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">Tonalité TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Largeur de bande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation>Édition système GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation>Mettre à jour la période</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation>Inverser le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Paramètres de démarrage</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation>Intro ligne 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation>Première ligne d&apos;accueil (si supporté par la radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation>Intro ligne 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation>Intro ligne 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation>Seconde ligne d&apos;accueil (si supporté par la radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation>Intro ligne 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Paramètres audio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>Gain micro</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Synthèse vocale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Valeurs par défaut des canaux</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Puissance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Haute</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Moyenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Basse</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Ouvrir</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>Anti-bavard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Désactivé</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation>Niveau VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Contact</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Impossible de supprimer la liste de groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Impossible de supprimer des listes de groupes&#xa0;: Sélectionner d&apos;abord une liste de groupes.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Supprimer la liste de groupes&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Supprimer la liste de groupes &apos;%1&apos; &#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>Supprimer %1 listes de groupes&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Ajouter un Groupe RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Supprimer Groupe RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Listes de Groupes</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Sonnerie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>Fichier</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Aide</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Bases</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Nouveau</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Créer un nouveau Codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Ouvrir …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Importer un nouveau codeplug depuis un fichier &amp;quot;conf&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Enregistrer …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enregistrer le codeplug dans un fichier &amp;quot;conf&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Quitter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Quitte l&apos;application.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Déctecter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Détecter les radios connectées.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Vérifier</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Vérifie le codeplug avec les radios connectées.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Lire</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Lire un codeplug depuis les radios connectée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Écrire</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Écrire le codeplug dans la radio connectée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>À propos de qdrm</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Lire le manuel.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Paramètres</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Afficher l&apos;écran de paramétrage.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Écrire la base d&apos;indicatifs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Écrire la base d&apos;indicatifs dans la radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Mettre à jour la base d&apos;indicatifs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Met à jour la base locale d&apos;indicatifs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Mettre à jour la base des groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Mise à jour de la base locale des groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation type=\"unfinished\">Export vers CHIRP ...</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation type=\"unfinished\">Import ...</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation>Importe et fusionne un codeplug dans le codeplug courant.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation>Mettre à jour les éléments orbitaux</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation>Mise à jour des éléments orbitaux.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation type=\"unfinished\">Modifier les satellites ...</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation>Ouvre un éditeur pour modifier la base de satellites.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation>Sauvegarder les satellites</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation>Sauvegarder les éléments orbitaux et les informations de transpondeurs dans la radio connectée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation>Impossible de mettre à jour la base des indicatifs&#xa0;: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation>Base des indicatifs mise à jour et chargée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation>Impossible de mettre à jour la base des groupes&#xa0;: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation>Base des talk-groups mise à jour et chargée.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation>Impossible de mettre à jour les éléments orbitaux&#xa0;: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation>Éléments orbitaux mis à jour et chargés.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation>IDs de la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation>Contacts</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation>Listes de groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation>Canaux</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation>Listes de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation>Canaux de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation>Zones de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Modifications au codeplug non sauvegardées.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Modifications au codeplug non sauvegardées. Ces modifications seront perdues si vous continuez.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Sélectionner un canal&#xa0;:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation>Afficher les appels privés</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation>Sélectionner un appel de groupe&#xa0;:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation>Sélectionner des canaux de roaming</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Impossible de supprimer un système GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Impossible de supprimer un système GPS&#xa0;: commencer par sélectionner un système GPS.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>Supprimer le système de localisation&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>Supprimer le système de localisation &apos;%1&apos;&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>Supprimer %1 systèmes de localisation&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; QDMR est un CPS indépendant des modèles de radios. Cependant, toutes les radios ne disposent pas du GPS ou de l&apos;APRS. Cette partie du paramétrage peut être ignoré lors de l&apos;écriture du code-plug dans la radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Masquer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>Ajouter un système GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>Ajouter un système APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation>Supprimer un système de localisation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation type=\"unfinished\">Aucun</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>Faux</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>Vrai</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation>Nouvel élément</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation>Propriété</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Valeur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Description</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation>vrai</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation>faux</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation type=\"unfinished\">Aucun</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation>Instance de &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation>Liste de %1 instances</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Créer une liste de groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Modifier une liste de groupes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation>Impossible de supprimer l&apos;appel de groupe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation>Impossible de supprimer l&apos;appel de groupe&#xa0;: commencer par sélectionner au moins un appel de groupe.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Ajouter un contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation>Supprimer un contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Impossible de supprimer les IDs radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Impossible de supprimer les IDs radio&#xa0;: commencer par sélectionner un ID radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Supprimer l&apos;ID radio&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Supprimer l&apos;ID radio &apos;%1&apos;&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Supprimer des listes de recherche&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Supprimer %1 listes de recherche&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>ID radio par défaut</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Ajouter un ID radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Supprimer un ID radio</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation>Nombre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation>Impossible de détecter automatiquement la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation>Sélectionner la radio</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation>Impossible de télécharger les notes de version depuis https://github.com/hmatuschek/qdmr\n\t %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation>Impossible de lire les notes de version depuis https&#xa0;://github.com/hmatuschek/qdmr\n\t Les détails de la version ne sont pas au format JSON&#x202f;!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation>Impossible de lire les notes de version depuis https&#xa0;://github.com/hmatuschek/qdmr\n\t La version ne contient pas de notes.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation>qdmr est mis à jour vers la version %1</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation>Fréquence RX [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation>Fréquence TX [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation>Time Slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation>Code Couleur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation>Sélectionné</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation>Édition canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Créer un canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation>TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation>TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; QDMR est un CPS indépendant des modèles de radios. Cependant, toutes les radios ne prennent pas en charge le Roaming. Cette partie du paramétrage sera peut-être ignorée lors de l&apos;écriture du code-plug dans la radio. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conseil:&lt;/span&gt; Vous n&apos;avez pas besoin d&apos;ajouter des canaux de Roaming explicitement, créez simplement des zones de Roaming et ajoutez leurs des canaux. Ces canaux seront alors automatiquement ajoutés à cette liste.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Masquer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Ajouter un canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Supprimer un canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Impossible de supprimer le canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Impossible de supprimer le canal de roaming&#xa0;: commencer par sélectionner un canal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation>Supprimer le canal de roaming&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation>Supprimer le canal de roaming %1&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation>Supprimer %1 canaux de roaming&#x202f;?</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>Fréquence RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>Fréquence TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation>Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation>Canal de roaming</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation>%1 (contenant %2 canaux)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation>Zone de roaming</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Créer une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation>Modifier une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation>Impossible de supprimer des canaux.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation>Impossible de supprimer des canaux. Commencer par sélectionner un canal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation>Nom&#xa0;:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Ajouter un canal de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>Ajouter un canal DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation>Supprimer un canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation>Extension</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation>Générer une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation>Créer une zone de roaming en collectant tous les canaux avec ces appels de groupe.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Impossible de supprimer une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Impossible de supprimer une zone de roaming&#xa0;: commencer par sélectionner une zone.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Supprimer la zone de roaming&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Supprimer la zone de roaming %1&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Supprimer les zones de roaming&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation>Supprimer %1 zones de roaming&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; QDMR est un CPS indépendant des modèles de radios. Cependant, toutes les radios ne prennent pas en charge le Roaming. Cette partie du paramétrage sera peut-être ignorée lors de l&apos;écriture du code-plug dans la radio. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Masquer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Ajouter une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation>Générer une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Supprimer une zone de roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Défaut]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation>Modifier la base de satellites</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation>Ajouter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation>Supprimer</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation>Sélectionner un satellite</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Modifier une liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation>Canal principal (50%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation>Canal secondaire (25%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation>Canal d&apos;appel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Ajouter un canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation>Supprimer le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Créer une liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation>[Aucun]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation>[Sélectionné]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation>[Dernier]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Impossible de supprimer la liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Impossible de supprimer la liste de recherche&#xa0;: commencer par sélectionner une liste.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Supprimer la liste de recherche&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Supprimer la liste de recherche &apos;%1&apos;&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Supprimer les listes de recherche&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Supprimer %1 listes de recherche&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Ajouter une liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Supprimer la liste de recherche</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation>Liste de recherche</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation>%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation>Aucun</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation>CTCSS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation>DCS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation>Hz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation>Inversé</translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Attention&#x202f;!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Paramètres</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Localisation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>Système de localisation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Locator</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation>Sources d&apos;informations des relais</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation>activer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation>Programmation de la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Mise à jour du codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Mettre à jour le codeplug de la radio. Si aucune sélection de paramètres, le codeplug de la radio est écrasé avec potentiellement des valeurs par défaut incomplètes. &lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Si une sélection de paramètres, QDMR télécharge le codeplug actuel de la radio et met à jour uniquement les paramètres sélectionnés. Les paramètres restant de la radio ne sont pas modifiés (recommandé).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>GPS auto-activable</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation>Lorsqu&apos;un système GPS ou APRS est défini et utilisé pour tout les canaux, le module GPS est activé automatiquement.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation>Lorsqu&apos;une zone de roaming est définie et utilisée pour tout les canaux, le roaming automatique est activé.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Activer automatiquement le roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation>Sources de données</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>Monde</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation>Amérique du nord</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation>Programmation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation>Interfaces radios</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation>désactiver la détection automatique</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Ignorer les alertes de vérification</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Comme l&apos;interface de communication avec la radio reste ouverte après la phase de vérification, le délai peut être dépassé et l&apos;écriture du codeplug échouer lorsque le dialogue de vérification s&apos;ouvre. Pour éviter ce comportement, les messages d&apos;information (warnings) de la vérification peuvent être ignorés, supprimant le délai entre la vérification et l&apos;écriture. Les erreurs de vérification continuent à bloquer une écriture de codeplug incorrect.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Ignorer les limites de fréquences</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation>Ne pas activer cette option à moins de savoir ce que vous faites.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation>Mettre à jour l&apos;heure de la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>Base des identifiants</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation>Limiter le nombre d&apos;entrées dans la base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation>Lorsque activé, le nombre d&apos;entrées de la base sera limité. Autrement, le maximum d&apos;entrées sera généré (dépendant de la radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation>Nombre d&apos;entrées dans la base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation>Spécifier le nombre d&apos;entrée de la base (si activé au dessus).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation>Sélectionner en utilisant mon ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation>Si activé, les entrées sont sélectionnées en utilisant les IDs DMR des utilisateurs.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation>Sélectionner en utilisation les préfixes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation>Si activé, ces préfixes d&apos;IDs DMR séparés par des virgules sont utilisés pour sélectionner les entrées dans la base des indicatifs.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Défaut</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation type=\"unfinished\">Aucun</translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Vérifier le codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation>Le codeplug ne peut être envoyé vers la radio tant que les problèmes critiques (rouge) ne sont pas résolus.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Édition de zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; Les zones sont des collections de canaux habituellement valides pour une région particulière.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;QDMR gère les zones en permettant l&apos;utilisation de deux listes indépendantes, une par VFO si la radio dispose de deux VFO. Cependant, certaines radios ne permettent pas d&apos;assigner des zones différentes à chaque VFO. Dans ce cas, QDMR découpe la zone en deux (A &amp;amp; B) en les programmant individuellement dans la radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Masquer&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Canaux A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation>ajouter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation>retirer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Canaux B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation>Extension</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Créer zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation>Impossible de retirer le canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation>Sélectionner au moins un canal.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Impossible de supprimer la zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Impossible de supprimer la zone&#xa0;: commencer par sélectionner une zone.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Supprimer la zone&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Supprimer la zone &apos;%1&apos;&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation>Supprimer les zones&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>Supprimer %1 zones&#x202f;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Ajouter la zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Supprimer une zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zone</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>Édition système APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Basiques</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Nom</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Canal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation>Source</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation>Chemin</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation>Icône</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation>Périodicité de mise à jour [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation>Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/it.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"it_IT\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Crea sistema APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Modifica sistema APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>Informazioni su qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Radio supportate</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Modifiche non salvate al codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Ci sono modifiche non salvate al codeplug corrente. Procedendo, queste modifiche andranno perse.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Apri codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>File codeplug (*.yaml);;File codeplug, vecchio formato (*.conf *.csv *.txt);;Tutti i file (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Impossibile aprire il file</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Impossibile leggere il codeplug dal file &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Impossibile leggere il codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Salva codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>File codeplug (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Utilizzare il nuovo formato YAML.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Il salvataggio nel vecchio formato di configurazione basato su tabelle è stato disabilitato dalla versione 0.9.0. La lettura di questi file funziona ancora.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Impossibile salvare il codeplug nel file &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Impossibile salvare il codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Impossibile salvare il codeplug nel file &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation>Esporta codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>File CSV di CHIRP (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation>Impossibile esportare il codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation>Impossibile esportare il codeplug nel file &apos;%1&apos;:\n%2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation>Importa codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation>File CSV di CHIRP (*.csv);; File YAML (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation>Impossibile importare il codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation>Impossibile importare il codeplug da &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation>Impossibile gestire il file &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>Nessun dispositivo corrispondente trovato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Impossibile connettersi alla radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Impossibile connettersi alla radio: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Radio rilevata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Rilevato dispositivo &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Nessuna radio rilevata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>Non è stato trovato nessun dispositivo corrispondente.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Verifica riuscita</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>Il codeplug è stato verificato con la radio &apos;%1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Lettura…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Errore di lettura</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Lettura completata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Caricamento…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Impossibile scrivere il DB dei nominativi.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>La radio rilevata &apos;%1&apos; non supporta un DB dei nominativi.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>La radio rilevata &apos;%1&apos; supporta un DB dei nominativi. Tuttavia, questa funzione non è ancora stata implementata.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>QDMR seleziona i nominativi da scrivere in base all&apos;ID DMR predefinito della radio. Non è stato impostato alcun ID predefinito.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Scrittura del DB dei nominativi…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation>Impossibile scrivere la configurazione del satellite.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation>La radio rilevata &apos;%1&apos; non supporta il tracciamento satellitare.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation>La radio rilevata &apos;%1&apos; non supporta il tracciamento satellitare. Questa funzione, tuttavia, non è ancora implementata.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation>Salvataggio della configurazione del satellite…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Errore di scrittura</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Scrittura completata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (alias di %2 %3)</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">stretta (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">larga (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Nota:&lt;/span&gt; qdmr fornisce un completamento automatico per i canali. In pratica, inizia a digitare il nominativo di un ripetitore. Dopo aver inserito tre caratteri, viene inviata una richiesta a repeaterbook.com per recuperare i ripetitori corrispondenti. Queste operazioni possono richiedere qualche istante. I risultati vengono memorizzati localmente in una cache.&lt;/p&gt;&lt;p&gt;Apparirà un menu a discesa che permette di selezionare un ripetitore. Una volta selezionato un ripetitore, le frequenze RX/TX e i toni CTCSS vengono compilati automaticamente (se applicabili).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;nascondi&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">Frequenza RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">Frequenza TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Potenza</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Massima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Alta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Media</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Bassa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Minima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Predefinito/a</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">Timeout TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">Livello VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Solo RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Estensioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\">Nessun offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\">Offset positivo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\">Offset negativo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Selezionare prima un singolo canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>Per clonare un canale, selezionare un canale singolo da clonare.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Impossibile eliminare il canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Impossibile eliminare il canale: devi prima selezionarne uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Eliminare il canale?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Eliminare il canale &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>Eliminare %1 canali?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation>Aggiungi canale…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Clona canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Elimina canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation>Aggiungi canale FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation>Aggiunge un nuovo canale FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation>Aggiungi canale DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation>Aggiunge un nuovo canale DMR.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation>Aggiungi canale AM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation>Aggiunge un nuovo canale AM.</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation>AM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>[Predefinito/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Massima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Alta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Media</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Bassa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Minima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Attivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Sempre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Libero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Colore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Tono</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Aperto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Larga</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Stretta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Tipo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>Frequenza RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>Frequenza TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Potenza</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>Solo RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Consenti</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>Lista gruppi RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>Contatto TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>Tono RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>Tono TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Larghezza di banda</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Canale</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Seleziona un canale:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation>Unione dei codeplug…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Strategie di risoluzione dei conflitti:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;se alcuni degli oggetti importati (canali, contatti, …) esistono già, seleziona come risolvere questi conflitti per singoli elementi e insiemi.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation>Gli elementi sono tutti oggetti atomici come ID radio, canali, contatti e canali roaming.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation>Elementi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation>Ignora</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation>Sostituisci</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation>Duplica</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation>Gli insiemi sono tutti oggetti che contengono altri elementi, come liste gruppi, zone, liste di scansione e zone roaming.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation>Insiemi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation>Unisci</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation>Ignora qualsiasi elemento duplicato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation>Sostituisci qualsiasi elemento duplicato con quello importato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation>Importa qualsiasi elemento duplicato con un nome modificato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation>Ignora qualsiasi insieme duplicato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation>Sostituisci qualsiasi insieme duplicato con quello importato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation>Importa qualsiasi insieme duplicato con un nome modificato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation>Unisci gli insiemi duplicati.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Impossibile spostare gli elementi.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Impossibile spostare gli elementi: devi prima selezionarne almeno uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Sposta gli elementi selezionati in cima.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Sposta gli elementi selezionati di dieci posizioni verso l’alto.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Sposta gli elementi selezionati di una posizione verso l’alto.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Sposta gli elementi selezionati di una posizione verso il basso.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Sposta gli elementi selezionati di dieci posizioni verso il basso.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Sposta gli elementi selezionati in fondo.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Impossibile spostare gli elementi.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Impossibile spostare gli elementi: devi prima selezionarne almeno uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation>Impossibile spostare gli elementi finché è applicato un filtro o un ordinamento.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation>Sposta gli elementi selezionati in cima.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation>Sposta gli elementi selezionati di dieci posizioni verso l’alto.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation>Sposta gli elementi selezionati di una posizione verso l’alto.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation>Sposta gli elementi selezionati di una posizione verso il basso.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation>Sposta gli elementi selezionati di dieci posizioni verso il basso.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation>Sposta gli elementi selezionati in fondo.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation>Attiva/Disattiva filtro e ordinamento</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation>Chiudi ordinamento e filtro</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation>Un’istanza di &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation>Crea oggetto di estensione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation>Selezionare la classe dell&apos;oggetto da creare</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Impossibile eliminare il contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Impossibile eliminare il contatto: devi prima selezionarne uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Eliminare il contatto?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Eliminare il contatto &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Eliminare i contatti?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Eliminare %1 contatti?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation>Aggiunge un contatto alla lista.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>Aggiungi contatto DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation>Pulsante elimina contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\">Aggiungi contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Elimina contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>Attivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Chiamata privata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Chiamata di gruppo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>Chiamata generale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Tipo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Numero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation>Tono RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Sempre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Canale libero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">Consenti TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">Contatto TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Zona roaming</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>Crea contatto DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Modifica contatto DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Chiamata privata</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Chiamata di gruppo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>Chiamata generale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Tipo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Numero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation>Suoneria</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Predefinito/a]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Crea contatto DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Modifica contatto DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Numero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation>Suoneria</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation>Seleziona un dispositivo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;È stato rilevato più di un dispositivo oppure quello trovato non è considerato sicuro da usare. In ogni caso, seleziona il dispositivo da utilizzare.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation>Errore: sconosciuto.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Errore: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Traccia dello stack:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation>Impossibile creare l&apos;estensione.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation>Impossibile creare l’estensione, considera di segnalare un bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation>Impossibile creare l&apos;elemento della lista.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation>Impossibile creare l’elemento della lista, considera di segnalare un bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Crea</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Rimuovere</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation>Stazione di polizia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation>Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation>Telefono</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation>Cluster DX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation>Gateway HF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation>Aereo piccolo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation>Stazione satellitare mobile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation>Sedia a rotelle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation>Motoslitta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation>Croce rossa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation>Boy scout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation>Casa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation>X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation>Punto rosso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation>Cerchio 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation>Cerchio 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation>Cerchio 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation>Cerchio 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation>Cerchio 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation>Cerchio 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation>Cerchio 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation>Cerchio 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation>Cerchio 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation>Cerchio 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation>Fuoco</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation>Campeggio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation>Motocicletta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation>Locomotiva</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation>Automobile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation>File server</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation>Futuro HC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation>Pronto soccorso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation>BBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation>Canoa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation>Occhio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation>Trattore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation>Grid square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation>Hotel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation>TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation>Scuola</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation>Accesso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation>MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation>Stazione NTS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation>Mongolfiera</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation>Auto della polizia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation>TBD</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation>RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation>Navetta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation>SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation>Autobus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation>ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation>Servizio meteo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation>Elicottero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation>Yacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation>MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation>Corridore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation>Triangolo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation>PBBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation>Aereo grande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation>Stazione meteo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation>Antenna parabolica</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation>Ambulanza</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation>Bicicletta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation>ICP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation>Caserma dei vigili del fuoco</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation>Cavallo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation>Automezzo dei vigili del fuoco</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation>Aliante</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation>Ospedale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation>IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation>Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation>Camion piccolo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation>Computer portatile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation>Mic-E</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation>Nodo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation>EOC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation>Rover</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation>Griglia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation>Antenna</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation>Motoscafo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation>Area di sosta per camion</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation>Camion grande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation>Furgone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation>Acqua</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation>XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation>Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation>Rifugio</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Sempre</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Canale libero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">Consenti TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">Tono RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">Tono TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Larghezza di banda</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation>Seleziona flag</translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation>Crea sistema DMR APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation>Modifica sistema DMR APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation>Modifica sistema GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation>Destinazione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation>Intervallo di aggiornamento</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation>Ripristina canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Impostazioni di avvio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation>Riga introduttiva 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation>Prima riga di benvenuto (se supportata dalla radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation>Riga introduttiva 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation>Riga introduttiva 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation>Seconda riga di benvenuto (se supportata dalla radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation>Riga introduttiva 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Impostazioni audio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>Amp. MIC.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Sintesi vocale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Valori predefiniti dei canali</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Potenza</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Massima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Alta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Media</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Bassa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Minima</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Aperto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>Timeout di trasmissione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation>Livello VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Contatto</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Impossibile eliminare la lista gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Impossibile eliminare le liste gruppi: devi prima selezionarne una.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Eliminare la lista gruppi?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Eliminare la lista gruppi &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>Eliminare %1 liste gruppi?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Aggiungi lista gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Elimina lista gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Liste gruppi</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Suoneria</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Estensioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>File</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Dispositivo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Aiuto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Database</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation>Barra degli strumenti</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Nuovo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Crea un nuovo codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Apri…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Importa un codeplug dai file &amp;quot;conf&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Salva…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Salva il codeplug in un file &amp;quot;conf&amp;quot;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Esci</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Esci dall&apos;applicazione.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Rileva</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Rileva le radio collegate.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Verifica</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifica il codeplug corrente con le radio collegate.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Leggi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Legge il codeplug dalle radio collegate.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Scrivi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Scrive il codeplug sulla radio collegata.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>Informazioni su qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Leggi il manuale.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Impostazioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Mostra la finestra delle impostazioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Scrivi il DB dei nominativi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Scrive il DB dei nominativi sulla radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Aggiorna il DB dei nominativi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Aggiorna il DB dei nominativi scaricati</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Aggiorna il DB dei talk-group</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Aggiorna il DB dei talk-group scaricati</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation>Esporta in CHIRP…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation>Esporta tutti i canali FM in un file CSV per CHIRP.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation>Importa…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation>Importa e unisce un codeplug in quello corrente.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation>Aggiorna gli elementi orbitali</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation>Aggiorna gli elementi orbitali.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation>Modifica i satelliti…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation>Apre un editor per modificare il database dei satelliti.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation>Salva i satelliti</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation>Scrive gli elementi orbitali e le informazioni del transponder sul dispositivo connesso.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation>Impossibile aggiornare il database dei nominativi: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation>Database dei nominativi aggiornato e caricato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation>Impossibile aggiornare il database dei talk-group: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation>Database dei talk-group aggiornato e caricato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation>Impossibile aggiornare gli elementi orbitali: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation>Elementi orbitali aggiornati e caricati.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation>ID Radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation>Contatti</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation>Liste gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation>Canali</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation>Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation>Liste di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation>Canali roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation>Zone roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Modifiche non salvate al codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Ci sono modifiche non salvate al codeplug corrente. Procedendo queste modifiche andranno perse.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Seleziona un canale:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation>Mostra chiamate private</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation>Seleziona una chiamata di gruppo:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation>Seleziona canali roaming</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Impossibile eliminare sistema GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Impossibile eliminare il sistema GPS: devi prima selezionarne uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>Eliminare sistema di posizionamento?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>Eliminare sistema di posizionamento &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>Eliminare %1 sistemi di posizionamento?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Nota:&lt;/span&gt; QDMR è un CPS indipendente dal dispositivo. Tuttavia, non tutte le radio supportano GPS o APRS. Pertanto, queste impostazioni potrebbero essere ignorate durante la programmazione del codeplug sul dispositivo.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Nascondi&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>Aggiungi sistema GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>Aggiungi sistema APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation>Elimina sistema di posizionamento</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Tipo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation>Destinazione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation>Intervallo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Messaggio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation>Nessuno/a</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation>Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>Falso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>Vero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation>nuovo elemento</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation>Proprietà</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Valore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Descrizione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation>vero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation>falso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation>Nessuno/a</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation>Disattivato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation>Istanza di &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation>Elenco di %1 istanze</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Crea lista gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Modifica lista gruppi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation>Impossibile rimuovere la chiamata di gruppo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation>Impossibile rimuovere la chiamata di gruppo: devi prima selezionarne almeno una.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Aggiungi contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation>Rimuovi contatto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Impossibile eliminare gli ID radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Impossibile eliminare gli ID radio: devi prima selezionarne uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Eliminare l’ID radio?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Eliminare l’ID radio &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Eliminare le liste di scansione?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Eliminare %1 liste di scansione?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>ID radio predefinito</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Aggiungi ID radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Elimina ID radio</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Tipo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation>Numero</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation>Impossibile rilevare automaticamente la radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation>Seleziona una radio specifica</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation>Impossibile scaricare le note di rilascio da https://github.com/hmatuschek/qdmr\n\t %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation>Impossibile leggere le note di rilascio da https://github.com/hmatuschek/qdmr\n\tIl rilascio non è un oggetto JSON!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation>Impossibile leggere le note di rilascio da https://github.com/hmatuschek/qdmr\n\tLa release non contiene note di rilascio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation>qdmr è stato aggiornato alla versione %1</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation>Frequenza RX [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation>Frequenza TX [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation>Time slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation>Color code</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation>Selezionato</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation>Modifica canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Crea canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation>TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation>TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Nota:&lt;/span&gt; QDMR è un CPS indipendente dal dispositivo. Tuttavia, non tutte le radio supportano il roaming. Pertanto, queste impostazioni potrebbero essere ignorate durante la programmazione del codeplug sul dispositivo. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Suggerimento:&lt;/span&gt; non è necessario aggiungere esplicitamente un canale roaming, basta creare le zone roaming e aggiungere i canali lì. Questi canali verranno poi aggiunti automaticamente a questo elenco.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Nascondi&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Aggiungi canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Elimina canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Impossibile eliminare il canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Impossibile eliminare il canale roaming: devi prima selezionarne uno.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation>Eliminare il canale roaming?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation>Eliminare il canale roaming %1?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation>Eliminare %1 canali roaming?</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>Frequenza RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>Frequenza TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation>Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation>Canale roaming</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation>%1 (contenente %2 canali)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation>Zona roaming</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Crea zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation>Modifica zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation>Impossibile rimuovere i canali.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation>Impossibile rimuovere i canali. Seleziona prima almeno un canale.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation>Nome:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Aggiungi canale roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>Aggiungi canale DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation>Rimuovi canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation>Estensione</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation>Genera zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation>Crea una zona roaming raccogliendo tutti i canali con queste chiamate di gruppo.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Impossibile eliminare la zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Impossibile eliminare la zona roaming: devi prima selezionarne una.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Eliminare la zona roaming?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Eliminare la zona roaming &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Eliminare le zone roaming?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation>Eliminare %1 zone roaming?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Nota:&lt;/span&gt; QDMR è un CPS indipendente dal dispositivo. Tuttavia, non tutte le radio supportano il roaming. Pertanto, queste impostazioni potrebbero essere ignorate durante la programmazione del codeplug sul dispositivo. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Nascondi&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Aggiungi zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation>Genera zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Elimina zona roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Predefinito/a]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation>Modifica il database dei satelliti</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation>Aggiungi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation>Elimina</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation>Seleziona un satellite</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation>Modifica transponder satellitare</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation>Transponder voce FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation>Frequenza di uplink</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation>Tono di uplink</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation>Frequenza di downlink</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation>Tono di downlink</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation>Transponder APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation>Beacon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation>Frequenza del beacon</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Modifica lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation>Canale primario (50%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation>Canale secondario (25%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation>Canale di trasmissione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Aggiungi canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation>Rimuovi canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Crea lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation>[Nessuno/a]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation>[Selezionato]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation>[Ultimo]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Impossibile eliminare la lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Impossibile eliminare la lista di scansione: devi prima selezionarne una.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Eliminare la lista di scansione?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Eliminare la lista di scansione &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Eliminare le liste di scansione?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Eliminare %1 liste di scansione?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Aggiungi lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Elimina lista di scansione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation>Lista di scansione</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation>%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation>Nessuno/a</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation>CTCSS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation>DCS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation>Hz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation>Invertito</translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Attenzione!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Impostazioni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Posizione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>Posizione del sistema</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Localizzatore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation>Fonti informazioni ripetitore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation>abilita</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation>Programmazione radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Aggiorna codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Aggiorna il codeplug sulla radio. Se non selezionato, il codeplug sulla radio verrà sovrascritto con valori predefiniti eventualmente incompleti.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;Se selezionato, QDMR scarica il codeplug dalla radio e aggiorna solo le impostazioni specificate. Le restanti impostazioni della radio non vengono modificate (consigliato).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>Abilita automaticamente il GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation>Quando un sistema GPS o APRS è definito e utilizzato per un canale, il modulo GPS viene abilitato automaticamente.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation>Quando una zona roaming è definita e utilizzata da un canale, il roaming automatico viene abilitato.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Abilita automaticamente il roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation>Fonti dati</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>Mondo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation>America del Nord</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation>Programmazione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation>Interfacce radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation>disabilita il rilevamento automatico</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Ignora gli avvisi di verifica</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Poiché l’interfaccia di comunicazione con la radio rimane aperta dopo la verifica, potrebbero verificarsi time-out e il caricamento del codeplug potrebbe fallire quando compare la finestra di dialogo di verifica. Per evitare questo, gli avvisi di verifica possono essere ignorati, eliminando il gap temporale tra verifica e caricamento. Gli errori di verifica continuano comunque a impedire il caricamento.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Ignora i limiti di frequenza</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation>Non impostare questa opzione, a meno che tu non sappia cosa stai facendo.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation>Aggiorna l’orologio del dispositivo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>Database dei nominativi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation>Limita il numero di voci del database</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation>Se abilitato, il numero di voci del database sarà limitato. Altrimenti, verrà generato il numero massimo di voci (dipendente dal dispositivo).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation>Numero di voci del database</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation>Specifica il numero di voci del database (se abilitato sopra).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation>Seleziona usando il mio ID DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation>Se abilitato, le voci vengono selezionate usando l’ID DMR dell’utente.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation>Seleziona usando i prefissi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation>Se abilitati, questi prefissi DMR separati da virgola vengono utilizzati per selezionare le voci del database dei nominativi.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Aperto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Predefinito/a</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation>Nessuno/a</translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Verifica codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation>Il codeplug non può essere caricato, fin quando tutti i problemi critici (rossi) non saranno risolti.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Modifica zona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Nota:&lt;/span&gt; le zone sono raccolte di canali generalmente valide per una specifica regione. Cioè, una raccolta di canali per ripetitori all’interno di una certa area.&lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;QDMR gestisce le zone permettendo due elenchi di canali indipendenti per ciascun VFO della radio (se ne ha due). Tuttavia, molte radio permettono di assegnare le zone a ciascun VFO individualmente. In questi casi, QDMR dividerà la zona in due (A e B) e le programmerà singolarmente sulla radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Nascondi&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Canali A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation>aggiungi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation>elimina</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Canali B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation>Estensione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Crea zona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation>Impossibile eliminare il canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation>Seleziona prima almeno un canale.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Impossibile eliminare la zona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Impossibile eliminare la zona: devi prima selezionarne una.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Eliminare la zona?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Eliminare la zona &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation>Eliminare le zone?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>Eliminare %1 zone?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Aggiungi zona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Elimina zona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zona</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>Modifica sistema APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Base</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Nome</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Canale</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation>Fonte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation>Destinazione</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation>Percorso</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation>Icona</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation>Intervallo di aggiornamento [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation>Messaggio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Estensioni</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation>File codeplug da caricare.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation>Specifica il livello di log dell&apos;applicazione da inviare a stdout. Deve essere uno tra `debug`, `info`, `warning`, `error` o `fatal`.</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/nl.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"nl\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Ondersteunde Radios</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Open codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Kan bestand niet openen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Kan codeplug niet lezen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Codeplug opslaan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Codeplug Bestanden (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Kan codeplug niet opslaan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>CHIRP CSV Bestanden (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Kan niet verbinden met radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Radio gevonden</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Geen radio gevonden</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Lees …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Leesfout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Upload …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Schrijffout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Smal (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Breed (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">RX Frequentie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">TX Frequentie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Vermogen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Hoog</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Middel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Laag</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">TX Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Scan Lijst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Kanaal verwijderen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>%1 kanalen verwijderen?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Kanaal Klonen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Kanaal Verwijderen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Hoog</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Middel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Laag</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Aan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Altijd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Vrij</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Kleur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Breed</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Smal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>RX Frequentie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>TX Frequentie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Vermogen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Scanlijst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>TX Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">RX Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">TX Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Bandbreedte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\">Kanaal</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>Aan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation type=\"unfinished\">RX Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Altijd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanaal Vrij</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Fout: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Traceback:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\">Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Telefoon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\">DX cluster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\">Vliegtuig klein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\">Mobiel satelliet station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\">Rolstoel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\">Sneeuwscooter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\">Rode kruis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\">Thuis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\">X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\">Cirkel 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\">Cirkel 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\">Cirkel 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\">Cirkel 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\">Cirkel 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\">Cirkel 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\">Cirkel 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\">Cirkel 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\">Cirkel 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\">Cirkel 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\">Vuur</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\">Kampeerterrein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\">Motorfiets</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\">Auto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\">Kano</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\">Trekker</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\">Hotel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\">TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\">School</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\">MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\">Ballon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\">Politie auto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\">SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\">Bus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\">ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\">Jacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\">MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\">Jogger</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\">Driehoek</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\">Vliegtuig groot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\">Weerstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\">Schotelantenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\">Ambulance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\">Fiets</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\">Brandweerkazerne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\">Paard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\">Brandweerauto</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\">Ziekenhuis</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\">IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\">Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\">Vrachtwagen klein</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\">Laptop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\">Mic-E</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\">Antenne</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\">Water</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\">XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\">Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Altijd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanaal Vrij</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">RX Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">TX Toon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Bandbreedte</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Vermogen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Max</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Hoog</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Middel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Laag</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Min</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Contact</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>Bestand</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Nieuw</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Opslaan …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Detecteren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Verifiëren</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Instellingen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation type=\"unfinished\">Radio IDs</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation type=\"unfinished\">Contacten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation type=\"unfinished\">Groepenlijst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation type=\"unfinished\">Kanalen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation type=\"unfinished\">Scan Lijsten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\">GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation type=\"unfinished\">Roaming Kanalen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation type=\"unfinished\">Roaming Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\">Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation type=\"unfinished\">DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\">Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Kanaal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Bericht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Waarde</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Beschrijving</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Uit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[Geen]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\">Type</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation type=\"unfinished\">TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation type=\"unfinished\">TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Zones</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensies</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Geen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Waarschuwing!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Instellingen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Locatie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>Systeem locatie</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Locator</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Update codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>Wereld</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Negeer frequentie limieten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Open</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zone</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Naam</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Kanaal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation type=\"unfinished\">Updateperiode [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\">Bericht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Extensies</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/pl.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"pl\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Utwórz system APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Edytuj system APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>O qdrm</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Wspierane radia</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">Częstotliwość RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">Częstotliwość TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Moc</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Maks</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Wysoki</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Średni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Niski</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Domyślny</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">Limit czasu TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">Poziom VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Tylko RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Skanuj listę</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Usuń kanał</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Maks</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Wysoki</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Średni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Niski</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Włącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Zawsze</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Wolny</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Kolor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Otwórz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Szeroki</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Wąski</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>Częstotliwość RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>Częstotliwość TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Moc</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Koniec czasu połączenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>Tylko RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Zezwól</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>Lista skanowania</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Strefy</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>Lista grupy</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>Kontakt TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>Identyfikator DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\">GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>Ton RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>Ton TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Szerokość pasma</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Kanał</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Wybierz kanał:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Nie można przenieść elementów.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Nie można przenieść elementów: Musisz wybrać conajmniej jeden z nich.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Nie można przenieść elementów</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Nie można przenieść elementów: Musisz wybrać conajmniej jeden z nich.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Nie można usunąć kontaktu</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation type=\"unfinished\">Włącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\">Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Numer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Zawsze</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Wolny kanał</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Edytuj kontakt DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Numer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>Identyfikator DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Utwórz kontakt DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Edytuj kontakt DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Numer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Zawsze</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Wolny kanał</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">Ton RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">Ton TX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Szerokość pasma</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Moc</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">Maks</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Wysoki</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Średni</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Niski</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Minimum</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Otwórz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">Poziom VOX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation type=\"unfinished\">O qdrm</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation type=\"unfinished\">Ustawienia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation type=\"unfinished\">Identyfikatory radia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation type=\"unfinished\">Kontakty</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation type=\"unfinished\">Kanały</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Strefy</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation type=\"unfinished\">Skanuj listy</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\">GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation type=\"unfinished\">Wybierz kanał:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\">Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\">Kanał</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\">Wyłącz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\">Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Numer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Strefy</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">Znaczenie mocno zależy od kontekstu. Sprawdź kontekst źródłowy.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation type=\"unfinished\">Ustawienia</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Otwórz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Domyślny</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\">Podstawowe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Nazwa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\">Kanał</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Rozszerzenia</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/pt_BR.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"pt_BR\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/ru.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"ru\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation>Шумоподавитель</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Создать систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Изменить систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>О программе qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Поддерживаемые радиостанции</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Несохранённые изменения кодплага.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>В текущем кодплаге есть несохранённые изменения. Они будут потеряны, если продолжить.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Открыть кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>Файлы кодплага (*.yaml);;Файлы кодплага, старый формат (*.conf *.csv *.txt);;Все файлы (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Не удалось открыть файл</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Не удалось прочитать кодплаг из файла «%1»: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Не удалось прочитать кодплаг.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Сохранить кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Файлы кодплага (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Используйте новый формат YAML.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Сохранение в старом табличном формате conf отключено с версии 0.9.0. Чтение таких файлов по-прежнему поддерживается.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Не удалось сохранить кодплаг в файл «%1»: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Не удалось сохранить кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Не удалось сохранить кодплаг в файл «%1».</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation>Экспорт кодплага</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation>Файлы CHIRP CSV (*.csv)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation>Не удалось экспортировать кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation>Не удалось экспортировать кодплаг в файл «%1»:\n%2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation>Импорт кодплага</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation>Файлы CHIRP CSV (*.csv);;Файлы YAML (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation>Не удалось импортировать кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation>Не удалось импортировать кодплаг из «%1»: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation>Неизвестно, как обработать файл «%1».</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>Подходящих устройств не найдено.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Не удалось подключиться к радиостанции</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Не удалось подключиться к радиостанции: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Радиостанция найдена</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Найдено устройство «%1».</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Радиостанция не найдена</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Проверка пройдена</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>Кодплаг совпадает с данными в радиостанции «%1»</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Считать…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Ошибка чтения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Чтение завершено</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Запись…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Не удалось записать базу позывных.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>Радиостанция «%1» не поддерживает базу позывных.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>Подходящее устройство не найдено.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>Радиостанция «%1» поддерживает базу позывных, но эта функция пока не реализована.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>QDMR подбирает позывные для записи по DMR ID радиостанции. DMR ID по умолчанию не задан.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Запись базы позывных…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation>Не удалось записать конфигурацию спутников.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation>Радиостанция «%1» не поддерживает слежение за спутниками.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation>Радиостанция «%1» поддерживает слежение за спутниками, но эта функция пока не реализована.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation>Запись конфигурации спутников…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Ошибка записи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Запись завершена</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (псевдоним для %2 %3)</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Narrow (12.5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Wide (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\">Изменить канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation>Частота приёма</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation>Частота передачи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\">Tx Offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation>Мощность</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation>Макс.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation>Высокая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation>Средняя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation>Низкая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation>Мин.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation>По умолчанию</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\">Tx Timeout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">VOX Level</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Rx Only</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation>Список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\">No offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\">Positive offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\">Negative offset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Сначала выберите один канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>Чтобы клонировать канал, выберите один канал для клонирования.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Не удалось удалить канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Не удалось удалить канал: сначала выберите канал.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Удалить канал?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Удалить канал «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>Удалить %1 каналов?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\">Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation>Добавить канал…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Клонировать канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation type=\"unfinished\">Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Удалить канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation>Добавить FM-канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation>Добавляет новый FM-канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation>Добавить DMR-канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation>Добавляет новый DMR-канал.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation>Добавить AM-канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation>Добавляет новый AM-канал.</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation>AM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Default]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>Макс.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Высокая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Средняя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Низкая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Мин.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Вкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Всегда</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Свободно</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Код цвета</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Тон</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Открыть</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>Широкая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>Узкая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>Тип</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>Частота приёма</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>Частота передачи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Мощность</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>Таймаут</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Rx Only</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Условие передачи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation type=\"unfinished\">Scanlist</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation type=\"unfinished\">RX Group List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation type=\"unfinished\">TX Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation type=\"unfinished\">Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Шумоподавитель</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">Rx Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">Tx Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Зоны</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Полоса</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Канал</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Выберите канал:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\">Merging codeplugs …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation type=\"unfinished\">Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation type=\"unfinished\">Items</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation type=\"unfinished\">Ignore</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation type=\"unfinished\">Override</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation type=\"unfinished\">Duplicate</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation type=\"unfinished\">Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation type=\"unfinished\">Sets</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation type=\"unfinished\">Merge</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation type=\"unfinished\">Ignores any duplicate item.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation type=\"unfinished\">Replaces any duplicate item with the imported one.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation type=\"unfinished\">Imports any duplicate item with a modified name.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation type=\"unfinished\">Ignores any duplicate set.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation type=\"unfinished\">Replaces any duplicate set with the imported one.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation type=\"unfinished\">Imports any duplicate set with a modified name.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation type=\"unfinished\">Merges duplicate sets.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\">Cannot move items.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\">Cannot move items: You have to select at least one item first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\">Move selected item(s) to the top.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\">Move selected item(s) ten positions up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\">Move selected item(s) one position up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\">Move selected item(s) one position down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\">Move selected item(s) ten positions down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\">Move selected item(s) to the bottom.</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation type=\"unfinished\">Cannot move items.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation type=\"unfinished\">Cannot move items: You have to select at least one item first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\">Cannot move items as long as there is some filter or sorting applied.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\">Move selected item(s) to the top.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\">Move selected item(s) ten positions up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\">Move selected item(s) one position up.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\">Move selected item(s) one position down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\">Move selected item(s) ten positions down.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\">Move selected item(s) to the bottom.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\">Toggle Filter and Sorting</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\">Close Sort and Filter</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation type=\"unfinished\">An instance of %1.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation type=\"unfinished\">&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation type=\"unfinished\">Create extension object</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation type=\"unfinished\">Select the class of object to create</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Не удалось удалить контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Не удалось удалить контакт: сначала выберите контакт.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Удалить контакт?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Удалить контакт «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Удалить контакты?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Удалить %1 контактов?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation type=\"unfinished\">Adds a contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>Добавить DTMF-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation type=\"unfinished\">Delete contact button</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Удалить контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\">Добавить контакт M17</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\">Adds an M17 contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\">Adds an DTMF (analog) contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\">Добавить DMR-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\">Adds an DMR contact to the list.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation>Добавить контакт</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation type=\"unfinished\">DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>Вкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Индивидуальный вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Групповой вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>Общий вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Тип</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation type=\"unfinished\">RX Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\">M17</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\">[Broadcast]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation>Всегда</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation>Канал свободен</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\">Чужой цветовой код</translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\">Radio Id</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">Tx Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\">Color-code</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\">Time-slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\">Group list</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">Tx Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Roaming zone</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>Создать DMR-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Изменить DMR-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Индивидуальный вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Групповой вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>Общий вызов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Тип</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Default]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Создать DTMF-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Изменить DMR-контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation type=\"unfinished\">Select a device</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation type=\"unfinished\">Error: Unknown.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation type=\"unfinished\">Error: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation type=\"unfinished\">Traceback:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation type=\"unfinished\">Cannot create extension.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation type=\"unfinished\">Cannot create extension, consider reporting a bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation type=\"unfinished\">Cannot create list element.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation type=\"unfinished\">Cannot create list element, consider reporting a bug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Создать</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Удалить</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\">Police station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\">Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Phone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\">DX cluster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\">HF gateway</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\">Plane small</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\">Mobile Satellite station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\">Wheel Chair</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\">Snowmobile</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\">Red cross</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\">Boy scout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\">Home</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\">X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\">Red dot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\">Circle 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\">Circle 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\">Circle 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\">Circle 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\">Circle 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\">Circle 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\">Circle 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\">Circle 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\">Circle 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\">Circle 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\">Fire</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\">Campground</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\">Motorcycle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\">Rail engine</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\">Car</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\">File server</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\">HC Future</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\">Aid station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\">BBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\">Canoe</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\">Eyeball</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\">Tractor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\">Grid Square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\">Hotel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\">TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\">School</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\">Logon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\">MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\">NTS station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\">Balloon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\">Police car</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\">TBD</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\">RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\">Shuttle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\">SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\">Bus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\">ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\">Weather service</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\">Helo</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\">Yacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\">MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\">Jogger</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\">Triangle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\">PBBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\">Plane large</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\">Weather station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\">Dish antenna</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\">Ambulance</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\">Bike</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\">ICP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\">Fire station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\">Horse</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\">Fire truck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\">Glider</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\">Hospital</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\">IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\">Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\">Truck small</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\">Laptop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\">Mic-E</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\">Node</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\">EOC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\">Rover</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\">Grid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\">Antenna</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\">Power boat</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\">Truck stop</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\">Truck large</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\">Van</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\">Water</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\">XAPRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\">Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\">Shelter</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation>Всегда</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation>Канал свободен</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\">Чужой тон</translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation>Шумоподавитель</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">Tx Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">Rx Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">Tx Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation>Полоса</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\">Select Flags</translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation>Создать DMR-систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation>Изменить DMR-систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation type=\"unfinished\">Edit GPS System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\">Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation type=\"unfinished\">Update period</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation type=\"unfinished\">Revert Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Параметры загрузки</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation type=\"unfinished\">Intro Line 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\">First greeting line (if supported by the radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation type=\"unfinished\">Intro line 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation type=\"unfinished\">Intro Line 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation type=\"unfinished\">Second greeting line (if supported by the radio).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation type=\"unfinished\">Intro line 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Аудио</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>Усиление МК</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Синтез речи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Значения канала по умолчанию</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Мощность</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>Макс.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Высокая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Средняя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Низкая</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Мин.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Шумоподавитель</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Открыть</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>Таймаут передачи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">VOX Level</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Контакт</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Не удалось удалить групповой список</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Не удалось удалить групповые списки: сначала выберите список.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Удалить групповой список?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Удалить групповой список «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>Удалить %1 групповых списков?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Добавить групповой список</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Удалить групповой список</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Групповые списки</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\">Channel mode</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\">Access number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\">Tx contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\">Send position</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\">Речь</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\">Данные</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\">Voice + Data</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\">Изменить контакт M17</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\">The name of the contact.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\">Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\">The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Ring</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\">Broadcast</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\">Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\">Создать контакт M17</translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>Файл</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Устройство</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Справка</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Базы данных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation>Панель инструментов</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Создать</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Создаёт новый кодплаг.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation type=\"unfinished\">Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Открыть…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation type=\"unfinished\">Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Сохранить…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation type=\"unfinished\">Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Выход</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Завершает работу приложения.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation type=\"unfinished\">Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Найти устройство</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Найти подключённые радиостанции.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Проверить</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation type=\"unfinished\">Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Считать</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Считывает кодплаг из подключённой радиостанции.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Записать</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Записывает кодплаг в подключённую радиостанцию.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>О программе qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Открыть руководство.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation type=\"unfinished\">F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Настройки</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Показать окно настроек</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Записать базу позывных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Записывает базу позывных в радиостанцию.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Обновить базу позывных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Обновляет загруженную базу позывных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Обновить БД токгрупп</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Обновляет загруженную базу токгрупп</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation>Экспорт в CHIRP…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation>Экспортирует все FM-каналы в CSV для CHIRP.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation>Импорт…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation>Импортирует и объединяет кодплаг с текущим.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation>Обновить орбитальные элементы</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation>Обновляет орбитальные элементы.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation>Редактировать спутники…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation>Открывает редактор базы спутников.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation>Записать спутники</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation>Записывает орбитальные элементы и данные транспондера на подключённое устройство.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation>Не удалось обновить базу позывных: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation>База позывных обновлена и загружена.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation>Не удалось обновить БД токгрупп: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation>База токгрупп обновлена и загружена.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation>Не удалось обновить орбитальные элементы: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation>Орбитальные элементы обновлены и загружены.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation>Контакты</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation>Групповые списки</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation>Каналы</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation>Зоны</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation>Списки сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation>Каналы роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation>Зоны роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Несохранённые изменения кодплага.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>В текущем кодплаге есть несохранённые изменения. Они будут потеряны, если продолжить.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Выберите канал:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation type=\"unfinished\">Show private calls</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation type=\"unfinished\">Select a group call:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation type=\"unfinished\">Select roaming channels</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Не удалось удалить систему GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Не удалось удалить систему GPS: сначала выберите систему.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>Удалить систему позиционирования?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>Удалить систему позиционирования «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>Удалить %1 систем позиционирования?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>Добавить систему GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation type=\"unfinished\">Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>Добавить систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation type=\"unfinished\">Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation type=\"unfinished\">Delete Position System</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Тип</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\">Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\">Period</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\">Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation>Нет</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>Нет</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>Да</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation type=\"unfinished\">new element</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation type=\"unfinished\">Property</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Значение</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Описание</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation type=\"unfinished\">true</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation type=\"unfinished\">false</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation>Нет</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation>Выкл.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation type=\"unfinished\">Instance of %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation type=\"unfinished\">List of %1 instances</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Создать групповой список</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Изменить групповой список</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation type=\"unfinished\">Cannot remove group call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation type=\"unfinished\">Cannot remove group call: You have to select at least one group call first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Добавить контакт</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation type=\"unfinished\">Remove Contact</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Не удалось удалить DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Не удалось удалить DMR ID: сначала выберите запись.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Удалить DMR ID?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Удалить DMR ID «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Удалить выбранные DMR ID?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Удалить %1 записей DMR ID?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>Основной DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Добавить DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Удалить DMR ID</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Тип</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation type=\"unfinished\">Number</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation type=\"unfinished\">Cannot auto-detect radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation type=\"unfinished\">Select a specific radio</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation type=\"unfinished\">Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation type=\"unfinished\">Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation type=\"unfinished\">Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation type=\"unfinished\">qDMR was updated to version %1</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation type=\"unfinished\">RX Frequency [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation type=\"unfinished\">TX Frequency [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation type=\"unfinished\">Time Slot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation type=\"unfinished\">Color Code</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation type=\"unfinished\">Selected</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation type=\"unfinished\">Edit roaming channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Создать канал роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation type=\"unfinished\">TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation type=\"unfinished\">TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Добавить канал роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Удалить канал роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Не удалось удалить канал роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Не удалось удалить канал роуминга: сначала выберите канал.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation>Удалить канал роуминга?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation>Удалить канал роуминга «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation>Удалить %1 канал(ов) роуминга?</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>Частота приёма</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>Частота передачи</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation>Зоны</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation type=\"unfinished\">Roaming Channel</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation type=\"unfinished\">%1 (containing %2 channels)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Roaming zone</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Создать зону роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation type=\"unfinished\">Set Roaming Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation type=\"unfinished\">Cannot remove channels.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation type=\"unfinished\">Cannot remove channels. Select at least one channel first.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation type=\"unfinished\">Name:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Добавить канал роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>Добавить DMR-канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\">Remove Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\">Extension</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation type=\"unfinished\">Generate roaming zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation type=\"unfinished\">Create a roaming zone by collecting all channels with these group calls.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Не удалось удалить зону роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Не удалось удалить зону роуминга: сначала выберите зону.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Удалить зону роуминга?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Удалить зону роуминга «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Удалить зоны роуминга?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation type=\"unfinished\">Delete %1 roaming zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Добавить зону роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation type=\"unfinished\">Generate Roaming Zone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Удалить зону роуминга</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Default]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation type=\"unfinished\">Edit satellite database</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation>Добавить</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation>Удалить</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation type=\"unfinished\">Select a satellite</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\">Edit Satellite Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\">FM Voice Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\">Uplink Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\">Uplink Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\">Downlink Frequency</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\">Downlink Tone</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\">APRS Transponder</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\">Beacon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\">Beacon Frequency</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Изменить список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation type=\"unfinished\">Primary Channel (50%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation type=\"unfinished\">Secondary Channel (25%)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation type=\"unfinished\">Transmit Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Добавить канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation type=\"unfinished\">Remove Channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Создать список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[None]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Selected]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation type=\"unfinished\">[Last]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Не удалось удалить список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Не удалось удалить список сканирования: сначала выберите список.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Удалить список сканирования?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Удалить список сканирования «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Удалить списки сканирования?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Удалить %1 списков сканирования?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Добавить список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Удалить список сканирования</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation type=\"unfinished\">Scan-List</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation type=\"unfinished\">Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation type=\"unfinished\">%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation>Нет</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation>CTCSS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation>DCS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation type=\"unfinished\">Hz</translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation type=\"unfinished\">Inverted</translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Внимание!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Настройки</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation>Местоположение</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation>Системное местоположение</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation>Локатор</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation>Источники данных о ретрансляторах</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation>включить</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation>Программирование радиостанции</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Обновлять кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>Включать GPS автоматически</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation type=\"unfinished\">When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation type=\"unfinished\">When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Включать роуминг автоматически</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation>Источники данных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation>Мир</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation>Северная Америка</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation>Программирование</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation>Интерфейсы радиостанции</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation>отключить автоопределение</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Игнорировать предупреждения проверки</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Игнорировать ограничения по частоте</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation type=\"unfinished\">Do not set this option unless you know what you are doing.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation>Обновлять часы устройства</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>База позывных</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation type=\"unfinished\">Limit number of DB entries</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation type=\"unfinished\">When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation type=\"unfinished\">Number of DB entries</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation type=\"unfinished\">Specifies the number of DB entries (if enabled above).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation type=\"unfinished\">Select using my DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation type=\"unfinished\">If enabled, the entries are selected using the users DMR ID.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation type=\"unfinished\">Select using prefixes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation type=\"unfinished\">If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation>Открыть</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\">Uses the global squelch setting if enabled.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation>По умолчанию</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\">Слот 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\">Слот 2</translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation>Нет</translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Проверить кодплаг</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation>Запись кодплага невозможна, пока не устранены все критические проблемы (красные).</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Изменить зону</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Каналы A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation type=\"unfinished\">add</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation type=\"unfinished\">remove</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Каналы B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\">Extension</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Создать зону</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation type=\"unfinished\">Cannot remove channel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation type=\"unfinished\">Select at least one channel first.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Не удалось удалить зону</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Не удалось удалить зону: сначала выберите зону.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Удалить зону?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Удалить зону «%1»?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation type=\"unfinished\">Delete zones?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>Удалить %1 зон?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Добавить зону</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation type=\"unfinished\">Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Удалить зону</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation type=\"unfinished\">Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Зона</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>Изменить систему APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Основное</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Имя</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Канал</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation type=\"unfinished\">Source</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation type=\"unfinished\">Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation type=\"unfinished\">Path</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation type=\"unfinished\">Icon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation type=\"unfinished\">Update period [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation type=\"unfinished\">Message</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation>Расширения</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation>Файл кодплага для загрузки.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation>Уровень журнала в stdout: `debug`, `info`, `warning`, `error` или `fatal`.</translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "i18n/sv.ts",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!DOCTYPE TS>\n<TS version=\"2.1\" language=\"sv\">\n<context>\n    <name>AMChannelDialog</name>\n    <message>\n        <location filename=\"../src/amchanneldialog.cc\" line=\"11\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"44\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>APRSSystemDialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"102\"/>\n        <source>Create APRS system</source>\n        <translation>Skapa APRS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"110\"/>\n        <source>Edit APRS system</source>\n        <translation>Redigera APRS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"133\"/>\n        <source>[Selected]</source>\n        <translation type=\"unfinished\">[Vald]</translation>\n    </message>\n</context>\n<context>\n    <name>AboutDialog</name>\n    <message>\n        <location filename=\"../src/aboutdialog.ui\" line=\"14\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"20\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"37\"/>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"55\"/>\n        <source>About qdmr</source>\n        <translation>Om qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../shared/ui/aboutdialog.ui\" line=\"88\"/>\n        <source>Supported Radios</source>\n        <translation>Radio som stöds</translation>\n    </message>\n</context>\n<context>\n    <name>Application</name>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"180\"/>\n        <location filename=\"../src/application.cc\" line=\"198\"/>\n        <location filename=\"../src/application.cc\" line=\"520\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation>Ej sparade ändringar i codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"181\"/>\n        <location filename=\"../src/application.cc\" line=\"199\"/>\n        <location filename=\"../src/application.cc\" line=\"521\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation>Det finns osparade ändringar av den aktuella kodpluggen. Dessa ändringar går förlorade om du fortsätter.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"207\"/>\n        <source>Open codeplug</source>\n        <translation>Öppna kodpluggen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"209\"/>\n        <source>Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)</source>\n        <translation>Codeplug-filer (*.yaml);;Codeplug-filer, gammalt format (*.conf *.csv *.txt);;Alla filer (*)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"286\"/>\n        <location filename=\"../src/application.cc\" line=\"323\"/>\n        <location filename=\"../src/application.cc\" line=\"362\"/>\n        <source>Cannot open file</source>\n        <translation>Kan inte öppna filen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"217\"/>\n        <location filename=\"../src/application.cc\" line=\"231\"/>\n        <location filename=\"../src/application.cc\" line=\"250\"/>\n        <location filename=\"../src/application.cc\" line=\"363\"/>\n        <source>Cannot read codeplug from file &apos;%1&apos;: %2</source>\n        <translation>Kan inte läsa codeplug från filen &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"216\"/>\n        <source>Cannot read codeplug.</source>\n        <translation>Kan inte läsa kodpluggen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"267\"/>\n        <source>Save codeplug</source>\n        <translation>Spara kodpluggen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"268\"/>\n        <source>Codeplug Files (*.yaml *.yml)</source>\n        <translation>Codeplug-filer (*.yaml *.yml)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"274\"/>\n        <source>Please use new YAML format.</source>\n        <translation>Använd det nya YAML-formatet.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"275\"/>\n        <source>Saving in the old table-based conf format was disabled with 0.9.0. Reading these files still works.</source>\n        <translation>Spara i det gamla tabellbaserade conf-formatet inaktiverades med 0.9.0. Att läsa dessa filer fungerar fortfarande.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"287\"/>\n        <location filename=\"../src/application.cc\" line=\"324\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;: %2</source>\n        <translation>Det går inte att spara codeplug till filen &apos;%1&apos;: %2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"296\"/>\n        <source>Cannot save codeplug</source>\n        <translation>Det går inte att spara codepluggen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"297\"/>\n        <source>Cannot save codeplug to file &apos;%1&apos;.</source>\n        <translation>Det går inte att spara codeplug till filen &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"314\"/>\n        <source>Export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"315\"/>\n        <source>CHIRP CSV Files (*.csv)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"332\"/>\n        <source>Cannot export codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"333\"/>\n        <source>Cannot export codeplug to file &apos;%1&apos;:\n%2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"349\"/>\n        <source>Import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"350\"/>\n        <source>CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"369\"/>\n        <location filename=\"../src/application.cc\" line=\"377\"/>\n        <location filename=\"../src/application.cc\" line=\"383\"/>\n        <location filename=\"../src/application.cc\" line=\"396\"/>\n        <source>Cannot import codeplug</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"370\"/>\n        <location filename=\"../src/application.cc\" line=\"378\"/>\n        <location filename=\"../src/application.cc\" line=\"397\"/>\n        <source>Cannot import codeplug from &apos;%1&apos;: %2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"384\"/>\n        <source>Do not know, how to handle file &apos;%1&apos;.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"418\"/>\n        <source>No matching devices found.</source>\n        <translation>Inga matchande enheter hittades.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"444\"/>\n        <source>Cannot connect to radio</source>\n        <translation>Kan inte ansluta till radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"445\"/>\n        <source>Cannot connect to radio: %1</source>\n        <translation>Kan inte ansluta till radio: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Radio found</source>\n        <translation>Radio hittad</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"455\"/>\n        <source>Found device &apos;%1&apos;.</source>\n        <translation>Hittade enheten &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"458\"/>\n        <location filename=\"../src/application.cc\" line=\"474\"/>\n        <location filename=\"../src/application.cc\" line=\"529\"/>\n        <location filename=\"../src/application.cc\" line=\"600\"/>\n        <location filename=\"../src/application.cc\" line=\"643\"/>\n        <location filename=\"../src/application.cc\" line=\"724\"/>\n        <source>No radio found</source>\n        <translation>Ingen radio hittades</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"459\"/>\n        <location filename=\"../src/application.cc\" line=\"475\"/>\n        <location filename=\"../src/application.cc\" line=\"530\"/>\n        <location filename=\"../src/application.cc\" line=\"601\"/>\n        <location filename=\"../src/application.cc\" line=\"644\"/>\n        <location filename=\"../src/application.cc\" line=\"725\"/>\n        <source>No matching device was found.</source>\n        <translation>Ingen matchande enhet hittades.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"499\"/>\n        <source>Verification success</source>\n        <translation>Verifieringen lyckades</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"500\"/>\n        <source>The codeplug was successfully verified with the radio &apos;%1&apos;</source>\n        <translation>Kodpluggen har verifierats med radion &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"544\"/>\n        <source>Read ...</source>\n        <translation>Läs…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"554\"/>\n        <source>Read error</source>\n        <translation>Läsfel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"573\"/>\n        <source>Read complete</source>\n        <translation>Läs komplett</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"629\"/>\n        <source>Upload ...</source>\n        <translation>Skriv…</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"650\"/>\n        <location filename=\"../src/application.cc\" line=\"660\"/>\n        <location filename=\"../src/application.cc\" line=\"672\"/>\n        <source>Cannot write call-sign DB.</source>\n        <translation>Kan inte skriva anropssignal DB.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"651\"/>\n        <source>The detected radio &apos;%1&apos; does not support a call-sign DB.</source>\n        <translation>Den upptäckta radion &apos;%1&apos; stöder inte en anropssignal-DB.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"661\"/>\n        <source>The detected radio &apos;%1&apos; does support a call-sign DB. This feature, however, is not implemented yet.</source>\n        <translation>Den upptäckta radion &apos;%1&apos; stöder en anropssignal-DB. Denna funktion är dock inte implementerad ännu.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"673\"/>\n        <source>QDMR selects the call-signs to be written based on the default DMR ID of the radio. No default ID set.</source>\n        <translation>QDMR väljer de anropssignaler som ska skrivas baserat på radions standard DMR-ID. Inget standard-ID inställt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"710\"/>\n        <source>Write call-sign DB ...</source>\n        <translation>Skriv anropssignal DB …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"731\"/>\n        <location filename=\"../src/application.cc\" line=\"741\"/>\n        <source>Cannot write satellite config.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"732\"/>\n        <source>The detected radio &apos;%1&apos; does not support satellite tracking.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"742\"/>\n        <source>The detected radio &apos;%1&apos; does support satellite tracking. This feature, however, is not implemented yet.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"764\"/>\n        <source>Write satellite config ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"783\"/>\n        <source>Write error</source>\n        <translation>Skrivfel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"795\"/>\n        <source>Write complete</source>\n        <translation>Skriv komplett</translation>\n    </message>\n    <message>\n        <location filename=\"../src/application.cc\" line=\"855\"/>\n        <source>%1 (alias for %2 %3)</source>\n        <translation>%1 (alias för %2 %3)</translation>\n    </message>\n</context>\n<context>\n    <name>BandwidthSelect</name>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"8\"/>\n        <source>Narrow (12.5 kHz)</source>\n        <translation type=\"unfinished\">Smal (12,5 kHz)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/bandwidthselect.cc\" line=\"9\"/>\n        <source>Wide (25 kHz)</source>\n        <translation type=\"unfinished\">Bred (25 kHz)</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelDialog</name>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"14\"/>\n        <source>Edit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Obs!&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"40\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"48\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"65\"/>\n        <source>Rx Frequency</source>\n        <translation type=\"unfinished\">RX-frekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"75\"/>\n        <source>Tx Frequency</source>\n        <translation type=\"unfinished\">TX-frekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"85\"/>\n        <source>Tx Offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"137\"/>\n        <source>Power</source>\n        <translation type=\"unfinished\">Effekt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"153\"/>\n        <source>Max</source>\n        <translation type=\"unfinished\">max.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"158\"/>\n        <source>High</source>\n        <translation type=\"unfinished\">Hög</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"163\"/>\n        <source>Mid</source>\n        <translation type=\"unfinished\">Mitten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"168\"/>\n        <source>Low</source>\n        <translation type=\"unfinished\">Lågt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"173\"/>\n        <source>Min</source>\n        <translation type=\"unfinished\">Minimalt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"181\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"218\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"252\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Standard</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"190\"/>\n        <source>Tx Timeout</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"205\"/>\n        <location filename=\"../src/channeldialog.ui\" line=\"242\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"227\"/>\n        <source>VOX Level</source>\n        <translation type=\"unfinished\">VOX-nivå</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"261\"/>\n        <source>Rx Only</source>\n        <translation type=\"unfinished\">Bara RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"271\"/>\n        <source>Scan List</source>\n        <translation type=\"unfinished\">Scan List</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.ui\" line=\"301\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"25\"/>\n        <source>No offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"27\"/>\n        <source>Positive offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"29\"/>\n        <source>Negative offset</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channeldialog.cc\" line=\"50\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListView</name>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"102\"/>\n        <source>Select a single channel first</source>\n        <translation>Välj en enskild kanal först</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"103\"/>\n        <source>To clone a channel, please select a single channel to clone.</source>\n        <translation>För att klona en kanal, välj en enskild kanal att klona.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"159\"/>\n        <source>Cannot delete channel</source>\n        <translation>Kan inte ta bort kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"160\"/>\n        <source>Cannot delete channel: You have to select a channel first.</source>\n        <translation>Kan inte ta bort kanal: Du måste först välja en kanal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete channel?</source>\n        <translation>Ta bort kanal?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"170\"/>\n        <source>Delete channel %1?</source>\n        <translation>Ta bort kanalen &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.cc\" line=\"174\"/>\n        <source>Delete %1 channels?</source>\n        <translation>Ta bort %1 kanaler?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"35\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"32\"/>\n        <source>Add Channel ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"42\"/>\n        <source>Clone Channel</source>\n        <translation>Klona kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"45\"/>\n        <source>Alt+C</source>\n        <translation>Alt+C</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"52\"/>\n        <source>Delete Channel</source>\n        <translation>Ta bort kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"55\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"64\"/>\n        <source>Add FM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"67\"/>\n        <source>Adds a new FM channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"75\"/>\n        <source>Add DMR Channel</source>\n        <translation type=\"unfinished\">Lägg till DMR-kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"78\"/>\n        <source>Adds a new DMR channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"86\"/>\n        <source>Add AM Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/channellistview.ui\" line=\"89\"/>\n        <source>Adds a new AM channel.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"279\"/>\n        <source>FM</source>\n        <translation>FM</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"277\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"281\"/>\n        <source>AM</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"292\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"303\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"381\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"407\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"418\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"425\"/>\n        <source>[Default]</source>\n        <translation>[Standard]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"294\"/>\n        <source>Max</source>\n        <translation>max.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"295\"/>\n        <source>High</source>\n        <translation>Hög</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"296\"/>\n        <source>Mid</source>\n        <translation>Mitten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"297\"/>\n        <source>Low</source>\n        <translation>Lågt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"298\"/>\n        <source>Min</source>\n        <translation>Minimalt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"305\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>Off</source>\n        <translation>Av</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"308\"/>\n        <source>On</source>\n        <translation>Aktiverat</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"312\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"318\"/>\n        <source>Always</source>\n        <translation>Alltid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"313\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"319\"/>\n        <source>Free</source>\n        <translation>Fri</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"314\"/>\n        <source>Color</source>\n        <translation>Färg</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"320\"/>\n        <source>Tone</source>\n        <translation>Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"323\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"347\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"354\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"365\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"375\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"384\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"410\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"415\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"434\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"441\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"448\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"459\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"420\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"427\"/>\n        <source>Open</source>\n        <translation>Öppet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"451\"/>\n        <source>Wide</source>\n        <translation>bred</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"453\"/>\n        <source>Narrow</source>\n        <translation>smal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"474\"/>\n        <source>Type</source>\n        <translation>typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"475\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"476\"/>\n        <source>Rx Frequency</source>\n        <translation>RX-frekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"477\"/>\n        <source>Tx Frequency</source>\n        <translation>TX-frekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"478\"/>\n        <source>Power</source>\n        <translation>Effekt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"479\"/>\n        <source>Timeout</source>\n        <translation>timeouttid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"480\"/>\n        <source>Rx Only</source>\n        <translation>Bara RX</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"481\"/>\n        <source>Admit</source>\n        <translation>Tillåta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"482\"/>\n        <source>Scanlist</source>\n        <translation>skanningslistan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"483\"/>\n        <source>Zones</source>\n        <translation>Zoner</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"484\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"485\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"486\"/>\n        <source>RX Group List</source>\n        <translation>Grupplistan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"487\"/>\n        <source>TX Contact</source>\n        <translation>TX kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"488\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"489\"/>\n        <source>GPS/APRS</source>\n        <translation>GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"490\"/>\n        <source>Roaming</source>\n        <translation>Roaming</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"491\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"492\"/>\n        <source>Rx Tone</source>\n        <translation>RX Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"493\"/>\n        <source>Tx Tone</source>\n        <translation>TX Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"494\"/>\n        <source>Bandwidth</source>\n        <translation>Bandbredd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"495\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"523\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n</context>\n<context>\n    <name>ChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"24\"/>\n        <source>Select a channel:</source>\n        <translation>Välj kanal:</translation>\n    </message>\n</context>\n<context>\n    <name>ConfigMergeDialog</name>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"17\"/>\n        <source>Merging codeplugs ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"32\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"48\"/>\n        <source>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"51\"/>\n        <source>Items</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"58\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"111\"/>\n        <source>Ignore</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"63\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"116\"/>\n        <source>Override</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"68\"/>\n        <location filename=\"../src/configmergedialog.ui\" line=\"121\"/>\n        <source>Duplicate</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"101\"/>\n        <source>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"104\"/>\n        <source>Sets</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.ui\" line=\"126\"/>\n        <source>Merge</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"71\"/>\n        <source>Ignores any duplicate item.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"74\"/>\n        <source>Replaces any duplicate item with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"77\"/>\n        <source>Imports any duplicate item with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"89\"/>\n        <source>Ignores any duplicate set.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"92\"/>\n        <source>Replaces any duplicate set with the imported one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"95\"/>\n        <source>Imports any duplicate set with a modified name.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configmergedialog.cc\" line=\"98\"/>\n        <source>Merges duplicate sets.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectListView</name>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"72\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"94\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"118\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"141\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"165\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"188\"/>\n        <source>Cannot move items.</source>\n        <translation>Kan inte flytta objekt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"73\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"95\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"119\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"142\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"166\"/>\n        <location filename=\"../src/configobjectlistview.cc\" line=\"189\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Kan inte flytta objekt: Du måste välja minst ett objekt först.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"25\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"45\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"65\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"85\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"105\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjectlistview.ui\" line=\"125\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTableView</name>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"253\"/>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"260\"/>\n        <source>Cannot move items.</source>\n        <translation>Kan inte flytta objekt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"254\"/>\n        <source>Cannot move items: You have to select at least one item first.</source>\n        <translation>Kan inte flytta objekt: Du måste välja minst ett objekt först.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.cc\" line=\"261\"/>\n        <source>Cannot move items as long as there is some filter or sorting applied.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"50\"/>\n        <source>Move selected item(s) to the top.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"64\"/>\n        <source>Move selected item(s) ten positions up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"78\"/>\n        <source>Move selected item(s) one position up.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"92\"/>\n        <source>Move selected item(s) one position down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"106\"/>\n        <source>Move selected item(s) ten positions down.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"120\"/>\n        <source>Move selected item(s) to the bottom.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"188\"/>\n        <source>Toggle Filter and Sorting</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttableview.ui\" line=\"199\"/>\n        <source>Close Sort and Filter</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ConfigObjectTypeSelectionDialog</name>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"44\"/>\n        <source>An instance of %1.</source>\n        <translation>En förekomst av &apos;%1&apos;.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.cc\" line=\"55\"/>\n        <source>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</source>\n        <translation>&lt;p&gt;%1&lt;p&gt;&lt;p style=&quot;margin-left:10px;&quot;&gt;%2&lt;/p&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"20\"/>\n        <source>Create extension object</source>\n        <translation>Skapa extension object</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configobjecttypeselectiondialog.ui\" line=\"26\"/>\n        <source>Select the class of object to create</source>\n        <translation>Välj objektklassen som ska skapas</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListView</name>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"83\"/>\n        <source>Cannot delete contact</source>\n        <translation>Kan inte ta bort kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"84\"/>\n        <source>Cannot delete contact: You have to select a contact first.</source>\n        <translation>Kan inte ta bort kontakt: Du måste först välja en kontakt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact?</source>\n        <translation>Ta bort kontakt?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"94\"/>\n        <source>Delete contact %1?</source>\n        <translation>Ta bort kontakten &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete contacts?</source>\n        <translation>Ta bort kontakter?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.cc\" line=\"98\"/>\n        <source>Delete %1 contacts?</source>\n        <translation>Ta bort %1 kontakter?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"32\"/>\n        <source>Adds a contact to the list.</source>\n        <translation>Lägger till en kontakt i listan.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"60\"/>\n        <source>Add M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"63\"/>\n        <source>Adds an M17 contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"68\"/>\n        <source>Add DTMF Contact</source>\n        <translation>Lägg till DTMF-kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"71\"/>\n        <source>Adds an DTMF (analog) contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"76\"/>\n        <source>Add DMR Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"79\"/>\n        <source>Adds an DMR contact to the list.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"45\"/>\n        <source>Delete contact button</source>\n        <translation>Ta bort kontakt-knappen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"35\"/>\n        <source>Add Contact</source>\n        <translation type=\"unfinished\">Lägg till kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"48\"/>\n        <source>Delete Contact</source>\n        <translation>Ta bort kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactlistview.ui\" line=\"51\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ContactListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"657\"/>\n        <source>DTMF</source>\n        <translation>DTMF</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>On</source>\n        <translation>Påslagen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"663\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"684\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"706\"/>\n        <source>Off</source>\n        <translation>Avstängd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"674\"/>\n        <source>Private Call</source>\n        <translation>Privat samtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"675\"/>\n        <source>Group Call</source>\n        <translation>Gruppsamtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"676\"/>\n        <source>All Call</source>\n        <translation>All Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"688\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"698\"/>\n        <source>M17</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"703\"/>\n        <source>[Broadcast]</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"723\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"725\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"727\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"729\"/>\n        <source>RX Tone</source>\n        <translation>RX-ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"731\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Alltid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanal Tillgänglig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"35\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Alltid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"36\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanal Tillgänglig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"37\"/>\n        <source>Other Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>DMRChannelDialog</name>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"36\"/>\n        <source>Radio Id</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">TX Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"40\"/>\n        <source>Color-code</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"43\"/>\n        <source>Time-slot</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"45\"/>\n        <source>Group list</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"47\"/>\n        <source>Tx Contact</source>\n        <translation type=\"unfinished\">TX kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"49\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrchanneldialog.cc\" line=\"51\"/>\n        <source>Roaming zone</source>\n        <translation type=\"unfinished\">Roamingzon</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactDialog</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"27\"/>\n        <source>Create DMR Contact</source>\n        <translation>Skapa DMR-kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"52\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Redigera DMR-kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"83\"/>\n        <source>Private Call</source>\n        <translation>Privat samtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"84\"/>\n        <source>Group Call</source>\n        <translation>Gruppsamtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"85\"/>\n        <source>All Call</source>\n        <translation>All Call</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grund inställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"36\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"53\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"63\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"73\"/>\n        <source>Ring</source>\n        <translation>Ringa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.ui\" line=\"84\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRContactSelect</name>\n    <message>\n        <location filename=\"../src/dmrcontactdialog.cc\" line=\"181\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIDDialog</name>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grund inställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"46\"/>\n        <source>DMR ID</source>\n        <translation>DMR ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dmriddialog.ui\" line=\"57\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DMRIdSelect</name>\n    <message>\n        <location filename=\"../src/idselect.cc\" line=\"9\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Standard]</translation>\n    </message>\n</context>\n<context>\n    <name>DTMFContactDialog</name>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"12\"/>\n        <source>Create DTMF Contact</source>\n        <translation>Skapa DTMF-kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.cc\" line=\"20\"/>\n        <source>Edit DMR Contact</source>\n        <translation>Redigera DMR-kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grund inställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"46\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"56\"/>\n        <source>Ring</source>\n        <translation>Ringa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/dtmfcontactdialog.ui\" line=\"67\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>DeviceSelectionDialog</name>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"14\"/>\n        <source>Select a device</source>\n        <translation>Välj en enhet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/deviceselectiondialog.ui\" line=\"20\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n</context>\n<context>\n    <name>ErrorMessageView</name>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"12\"/>\n        <source>Error: Unknown.</source>\n        <translation>Fel: Okänt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.cc\" line=\"18\"/>\n        <source>Error: %1</source>\n        <translation>Fel: %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/errormessageview.ui\" line=\"30\"/>\n        <source>Traceback:</source>\n        <translation>Traceback:</translation>\n    </message>\n</context>\n<context>\n    <name>ExtensionView</name>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"110\"/>\n        <source>Cannot create extension.</source>\n        <translation>Det går inte att skapa tillägg.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"111\"/>\n        <source>Cannot create extension, consider reporting a bug.</source>\n        <translation>Kan inte skapa tillägg, överväg att rapportera ett fel.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"114\"/>\n        <source>Cannot create list element.</source>\n        <translation>Kan inte skapa listelement.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.cc\" line=\"115\"/>\n        <source>Cannot create list element, consider reporting a bug.</source>\n        <translation>Kan inte skapa listelement, överväg att rapportera ett fel.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"25\"/>\n        <source>Create</source>\n        <translation>Skapa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionview.ui\" line=\"32\"/>\n        <source>Remove</source>\n        <translation>Ta bort</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSelect</name>\n    <message>\n        <location filename=\"../src/aprsselect.cc\" line=\"11\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>FMAPRSSystem</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"7\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"8\"/>\n        <source>Police station</source>\n        <translation type=\"unfinished\">Polisstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"9\"/>\n        <source>Digipeater</source>\n        <translation type=\"unfinished\">Digipeater</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"10\"/>\n        <source>Phone</source>\n        <translation type=\"unfinished\">Telefon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"11\"/>\n        <source>DX cluster</source>\n        <translation type=\"unfinished\">DX-kluster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"12\"/>\n        <source>HF gateway</source>\n        <translation type=\"unfinished\">HF gateway</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"13\"/>\n        <source>Plane small</source>\n        <translation type=\"unfinished\">Litet flygplan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"14\"/>\n        <source>Mobile Satellite station</source>\n        <translation type=\"unfinished\">Mobil satellitstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"15\"/>\n        <source>Wheel Chair</source>\n        <translation type=\"unfinished\">Rullstol</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"16\"/>\n        <source>Snowmobile</source>\n        <translation type=\"unfinished\">Snöskoter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"17\"/>\n        <source>Red cross</source>\n        <translation type=\"unfinished\">Röda Korset</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"18\"/>\n        <source>Boy scout</source>\n        <translation type=\"unfinished\">Pojk Scout</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"19\"/>\n        <source>Home</source>\n        <translation type=\"unfinished\">Hem</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"20\"/>\n        <source>X</source>\n        <translation type=\"unfinished\">X</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"21\"/>\n        <source>Red dot</source>\n        <translation type=\"unfinished\">Röd prick</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"22\"/>\n        <source>Circle 0</source>\n        <translation type=\"unfinished\">Cirkel 0</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"23\"/>\n        <source>Circle 1</source>\n        <translation type=\"unfinished\">Cirkel 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"24\"/>\n        <source>Circle 2</source>\n        <translation type=\"unfinished\">Cirkel 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"25\"/>\n        <source>Circle 3</source>\n        <translation type=\"unfinished\">Cirkel 3</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"26\"/>\n        <source>Circle 4</source>\n        <translation type=\"unfinished\">Cirkel 4</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"27\"/>\n        <source>Circle 5</source>\n        <translation type=\"unfinished\">Cirkel 5</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"28\"/>\n        <source>Circle 6</source>\n        <translation type=\"unfinished\">Cirkel 6</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"29\"/>\n        <source>Circle 7</source>\n        <translation type=\"unfinished\">Cirkel 7</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"30\"/>\n        <source>Circle 8</source>\n        <translation type=\"unfinished\">Cirkel 8</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"31\"/>\n        <source>Circle 9</source>\n        <translation type=\"unfinished\">Cirkel 9</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"32\"/>\n        <source>Fire</source>\n        <translation type=\"unfinished\">Brand</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"33\"/>\n        <source>Campground</source>\n        <translation type=\"unfinished\">Camping</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"34\"/>\n        <source>Motorcycle</source>\n        <translation type=\"unfinished\">Motorcykel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"35\"/>\n        <source>Rail engine</source>\n        <translation type=\"unfinished\">Spårmotor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"36\"/>\n        <source>Car</source>\n        <translation type=\"unfinished\">Bil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"37\"/>\n        <source>File server</source>\n        <translation type=\"unfinished\">Fil server</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"38\"/>\n        <source>HC Future</source>\n        <translation type=\"unfinished\">HC Future</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"39\"/>\n        <source>Aid station</source>\n        <translation type=\"unfinished\">Biståndsstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"40\"/>\n        <source>BBS</source>\n        <translation type=\"unfinished\">BBS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"41\"/>\n        <source>Canoe</source>\n        <translation type=\"unfinished\">Kanot</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"42\"/>\n        <source>Eyeball</source>\n        <translation type=\"unfinished\">Ögonglob</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"43\"/>\n        <source>Tractor</source>\n        <translation type=\"unfinished\">Traktor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"44\"/>\n        <source>Grid Square</source>\n        <translation type=\"unfinished\">Grid Square</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"45\"/>\n        <source>Hotel</source>\n        <translation type=\"unfinished\">Hotell</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"46\"/>\n        <source>TCP/IP</source>\n        <translation type=\"unfinished\">TCP/IP</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"47\"/>\n        <source>School</source>\n        <translation type=\"unfinished\">Skola</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"48\"/>\n        <source>Logon</source>\n        <translation type=\"unfinished\">Logga in</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"49\"/>\n        <source>MacOS</source>\n        <translation type=\"unfinished\">MacOS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"50\"/>\n        <source>NTS station</source>\n        <translation type=\"unfinished\">NTS station</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"51\"/>\n        <source>Balloon</source>\n        <translation type=\"unfinished\">Ballong</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"52\"/>\n        <source>Police car</source>\n        <translation type=\"unfinished\">Polis bil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"53\"/>\n        <source>TBD</source>\n        <translation type=\"unfinished\">TBD</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"54\"/>\n        <source>RV</source>\n        <translation type=\"unfinished\">RV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"55\"/>\n        <source>Shuttle</source>\n        <translation type=\"unfinished\">Shuttle</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"56\"/>\n        <source>SSTV</source>\n        <translation type=\"unfinished\">SSTV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"57\"/>\n        <source>Bus</source>\n        <translation type=\"unfinished\">Buss</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"58\"/>\n        <source>ATV</source>\n        <translation type=\"unfinished\">ATV</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"59\"/>\n        <source>Weather service</source>\n        <translation type=\"unfinished\">Vädertjänst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"60\"/>\n        <source>Helo</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"61\"/>\n        <source>Yacht</source>\n        <translation type=\"unfinished\">Yacht</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"62\"/>\n        <source>MS Windows</source>\n        <translation type=\"unfinished\">MS Windows</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"63\"/>\n        <source>Jogger</source>\n        <translation type=\"unfinished\">Joggare</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"64\"/>\n        <source>Triangle</source>\n        <translation type=\"unfinished\">Triangel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"65\"/>\n        <source>PBBS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"66\"/>\n        <source>Plane large</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"67\"/>\n        <source>Weather station</source>\n        <translation type=\"unfinished\">Väderstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"68\"/>\n        <source>Dish antenna</source>\n        <translation type=\"unfinished\">Diskantenn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"69\"/>\n        <source>Ambulance</source>\n        <translation type=\"unfinished\">Ambulans</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"70\"/>\n        <source>Bike</source>\n        <translation type=\"unfinished\">Cykel</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"71\"/>\n        <source>ICP</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"72\"/>\n        <source>Fire station</source>\n        <translation type=\"unfinished\">Brandstation</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"73\"/>\n        <source>Horse</source>\n        <translation type=\"unfinished\">Häst</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"74\"/>\n        <source>Fire truck</source>\n        <translation type=\"unfinished\">Brandbil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"75\"/>\n        <source>Glider</source>\n        <translation type=\"unfinished\">Segelflygplan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"76\"/>\n        <source>Hospital</source>\n        <translation type=\"unfinished\">Sjukhus</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"77\"/>\n        <source>IOTA</source>\n        <translation type=\"unfinished\">IOTA</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"78\"/>\n        <source>Jeep</source>\n        <translation type=\"unfinished\">Jeep</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"79\"/>\n        <source>Truck small</source>\n        <translation type=\"unfinished\">Lilla lastbil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"80\"/>\n        <source>Laptop</source>\n        <translation type=\"unfinished\">Bärbar dator</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"81\"/>\n        <source>Mic-E</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"82\"/>\n        <source>Node</source>\n        <translation type=\"unfinished\">Nod</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"83\"/>\n        <source>EOC</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"84\"/>\n        <source>Rover</source>\n        <translation type=\"unfinished\">Rover</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"85\"/>\n        <source>Grid</source>\n        <translation type=\"unfinished\">Rutnät</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"86\"/>\n        <source>Antenna</source>\n        <translation type=\"unfinished\">Antenn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"87\"/>\n        <source>Power boat</source>\n        <translation type=\"unfinished\">Motorbåt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"88\"/>\n        <source>Truck stop</source>\n        <translation type=\"unfinished\">Lastbilsstopp</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"89\"/>\n        <source>Truck large</source>\n        <translation type=\"unfinished\">Stor lastbil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"90\"/>\n        <source>Van</source>\n        <translation type=\"unfinished\">Skåpbil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"91\"/>\n        <source>Water</source>\n        <translation type=\"unfinished\">Vatten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"92\"/>\n        <source>XAPRS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"93\"/>\n        <source>Yagi</source>\n        <translation type=\"unfinished\">Yagi</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.cc\" line=\"94\"/>\n        <source>Shelter</source>\n        <translation type=\"unfinished\">Skydd</translation>\n    </message>\n</context>\n<context>\n    <name>FMAdmitSelect</name>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"9\"/>\n        <source>Always</source>\n        <translation type=\"unfinished\">Alltid</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"10\"/>\n        <source>Channel Free</source>\n        <translation type=\"unfinished\">Kanal Tillgänglig</translation>\n    </message>\n    <message>\n        <location filename=\"../src/admitselect.cc\" line=\"11\"/>\n        <source>Other Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>FMChannelDialog</name>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"32\"/>\n        <source>Squelch</source>\n        <translation type=\"unfinished\">Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"34\"/>\n        <source>Tx Admit</source>\n        <translation type=\"unfinished\">TX Admit</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"36\"/>\n        <source>Rx Tone</source>\n        <translation type=\"unfinished\">RX Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"38\"/>\n        <source>Tx Tone</source>\n        <translation type=\"unfinished\">TX Ton</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"40\"/>\n        <source>Bandwidth</source>\n        <translation type=\"unfinished\">Bandbredd</translation>\n    </message>\n    <message>\n        <location filename=\"../src/fmchanneldialog.cc\" line=\"42\"/>\n        <source>APRS</source>\n        <translation type=\"unfinished\">APRS</translation>\n    </message>\n</context>\n<context>\n    <name>FlagEditDialog</name>\n    <message>\n        <location filename=\"../src/flageditdialog.ui\" line=\"14\"/>\n        <source>Select Flags</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>GPSSystemDialog</name>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"8\"/>\n        <source>Create DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"15\"/>\n        <source>Edit DMR APRS System</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Vald]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"20\"/>\n        <source>Edit GPS System</source>\n        <translation>Redigera GPS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"36\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"56\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"63\"/>\n        <source>Update period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"89\"/>\n        <source>Revert Channel</source>\n        <translation>Återställ kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/gpssystemdialog.ui\" line=\"107\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GeneralSettingsView</name>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"22\"/>\n        <source>Boot Settings</source>\n        <translation>Startinställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"28\"/>\n        <source>Intro Line 1</source>\n        <translation>Intro rad 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"35\"/>\n        <source>First greeting line (if supported by the radio).</source>\n        <translation>Första hälsningsraden (om det stöds av radion).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"38\"/>\n        <source>Intro line 1</source>\n        <translation>Intro rad 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"45\"/>\n        <source>Intro Line 2</source>\n        <translation>Intro rad 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"52\"/>\n        <source>Second greeting line (if supported by the radio).</source>\n        <translation>Andra hälsningsraden (om den stöds av radion).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"58\"/>\n        <source>Intro line 2</source>\n        <translation>Intro rad 2</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"68\"/>\n        <source>Audio Settings</source>\n        <translation>Ljudinställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"74\"/>\n        <source>MIC Amp.</source>\n        <translation>MIC-förstärkare</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"91\"/>\n        <source>Speech Synthesis</source>\n        <translation>Talsyntes</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"108\"/>\n        <source>Channel Default Values</source>\n        <translation>Kanalens standardvärden</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"114\"/>\n        <source>Power</source>\n        <translation>Effekt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"122\"/>\n        <source>Max</source>\n        <translation>max.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"127\"/>\n        <source>High</source>\n        <translation>Hög</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"132\"/>\n        <source>Mid</source>\n        <translation>Mitten</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"137\"/>\n        <source>Low</source>\n        <translation>Lågt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"142\"/>\n        <source>Min</source>\n        <translation>Minimalt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"150\"/>\n        <source>Squelch</source>\n        <translation>Squelch</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"157\"/>\n        <source>Open</source>\n        <translation>Öppet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"167\"/>\n        <source>Transmit Timeout</source>\n        <translation>Timeout för sändning</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"174\"/>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"194\"/>\n        <source>Off</source>\n        <translation>Av</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"187\"/>\n        <source>VOX Level</source>\n        <translation>VOX-nivå</translation>\n    </message>\n    <message>\n        <location filename=\"../src/generalsettingsview.ui\" line=\"211\"/>\n        <source>Extensions</source>\n        <translation>Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"935\"/>\n        <source>Contact</source>\n        <translation>Kontakt</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsView</name>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"46\"/>\n        <source>Cannot delete RX group list</source>\n        <translation>Kan inte radera grupplistan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"47\"/>\n        <source>Cannot delete RX group lists: You have to select a group list first.</source>\n        <translation>Kan inte ta bort grupplistor: Du måste först välja en grupplista.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete RX group list?</source>\n        <translation>Ta bort grupplistan?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"56\"/>\n        <source>Delete RX group list %1?</source>\n        <translation>Ta bort grupplistan &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.cc\" line=\"60\"/>\n        <source>Delete %1 RX group lists?</source>\n        <translation>Ta bort %1 grupplistor?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"32\"/>\n        <source>Add RX Group</source>\n        <translation>Lägg till grupplista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"42\"/>\n        <source>Delete RX Group</source>\n        <translation>Ta bort grupplista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/grouplistsview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>GroupListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"911\"/>\n        <source>RX Group Lists</source>\n        <translation>Grupplistor</translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelDialog</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"47\"/>\n        <source>Channel mode</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"49\"/>\n        <source>Access number</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"52\"/>\n        <source>Tx contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"54\"/>\n        <source>Send position</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ChannelModeSelect</name>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"15\"/>\n        <source>Voice</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"16\"/>\n        <source>Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17channeldialog.cc\" line=\"17\"/>\n        <source>Voice + Data</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactDialog</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"14\"/>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"43\"/>\n        <source>Edit M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"24\"/>\n        <source>Basic</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"30\"/>\n        <source>Name</source>\n        <translation type=\"unfinished\">Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"37\"/>\n        <source>The name of the contact.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"44\"/>\n        <source>Call</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"51\"/>\n        <source>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"58\"/>\n        <source>Ring</source>\n        <translation type=\"unfinished\">Ringa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"68\"/>\n        <source>Broadcast</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"75\"/>\n        <source>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.ui\" line=\"86\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"49\"/>\n        <source>Create M17 Contact</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>M17ContactSelect</name>\n    <message>\n        <location filename=\"../src/m17contactdialog.cc\" line=\"94\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>MainWindow</name>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"74\"/>\n        <source>File</source>\n        <translation>Fil</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"86\"/>\n        <source>Device</source>\n        <translation>Enhet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"98\"/>\n        <location filename=\"../src/mainwindow.ui\" line=\"266\"/>\n        <source>Help</source>\n        <translation>Hjälp</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"105\"/>\n        <source>Databases</source>\n        <translation>Databaser</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"127\"/>\n        <source>Toolbar</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"155\"/>\n        <source>New</source>\n        <translation>Ny</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"158\"/>\n        <source>Creates a new Codeplug.</source>\n        <translation>Skapar en ny Codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"161\"/>\n        <source>Ctrl+N</source>\n        <translation>Ctrl+N</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"169\"/>\n        <source>Open ...</source>\n        <translation>Öppna …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"172\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Importerar en codeplug från &amp;quot;conf&amp;quot; filer.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"175\"/>\n        <source>Ctrl+O</source>\n        <translation>Ctrl+O</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"183\"/>\n        <source>Save ...</source>\n        <translation>Spara …</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"186\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sparar codepluggen i en &amp;quot;conf&amp;quot; fil.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"189\"/>\n        <source>Ctrl+S</source>\n        <translation>Ctrl+S</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"197\"/>\n        <source>Quit</source>\n        <translation>Sluta</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"200\"/>\n        <source>Quits the application.</source>\n        <translation>Avsluter applikationen.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"203\"/>\n        <source>Ctrl+Q</source>\n        <translation>Ctrl+Q</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"211\"/>\n        <source>Detect</source>\n        <translation>Upptäck</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"214\"/>\n        <source>Detect connected radios.</source>\n        <translation>Upptäck anslutna radioapparater.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"222\"/>\n        <source>Verify</source>\n        <translation>Verifiera</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"225\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>Verifierar den aktuella kodpluggen med anslutna radioapparater.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"228\"/>\n        <source>Ctrl+R</source>\n        <translation>Ctrl+R</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"236\"/>\n        <source>Read</source>\n        <translation>Läsa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"239\"/>\n        <source>Reads a codeplug from connected radios.</source>\n        <translation>Läser en kodplugg från anslutna radioapparater.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"247\"/>\n        <source>Write</source>\n        <translation>Skriva</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"250\"/>\n        <source>Writes the codeplug to the connected radio.</source>\n        <translation>Skriver kodpluggen till den anslutna radion.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"258\"/>\n        <source>About qdmr</source>\n        <translation>Om qdmr</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"269\"/>\n        <source>Read the handbook.</source>\n        <translation>Läs handboken.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"272\"/>\n        <source>F1</source>\n        <translation>F1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"280\"/>\n        <location filename=\"../src/mainwindow.cc\" line=\"107\"/>\n        <source>Settings</source>\n        <translation>Inställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"283\"/>\n        <source>Shows settings dialog</source>\n        <translation>Visar inställningsdialog.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"291\"/>\n        <source>Write Callsign DB</source>\n        <translation>Skriv anropssignal DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"294\"/>\n        <source>Writes call-sign DB to radio.</source>\n        <translation>Skriver anropssignal DB till radio.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"299\"/>\n        <source>Refresh Callsign DB</source>\n        <translation>Uppdatera anropssignal DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"302\"/>\n        <source>Refreshes the downloaded callsign DB</source>\n        <translation>Uppdaterar den nedladdade anropssignal-DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"310\"/>\n        <source>Refresh Talkgroup DB</source>\n        <translation>Uppdatera samtalsgrupp DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"313\"/>\n        <source>Refreshes the downloaded talkgroup DB</source>\n        <translation>Uppdaterar den nedladdade samtalsgruppens DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"318\"/>\n        <source>Export to CHIRP ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"321\"/>\n        <source>Exports all FM channels to CHIRP CSV.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"326\"/>\n        <source>Import ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"329\"/>\n        <source>Imports and merges a codeplug into the current one.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"334\"/>\n        <source>Refresh Orbital Elements</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"337\"/>\n        <source>Refreshes the orbital elements.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"345\"/>\n        <source>Edit Satellites ...</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"348\"/>\n        <source>Opens an editor to edit your satellite database.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"356\"/>\n        <source>Write satellites</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.ui\" line=\"359\"/>\n        <source>Writes the orbital elements and transponder information onto the connected device.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"70\"/>\n        <source>Cannot update callsign DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"73\"/>\n        <source>Callsign database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"78\"/>\n        <source>Cannot update talkgroup DB: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"81\"/>\n        <source>Talkgroup database updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"86\"/>\n        <source>Cannot update orbital elements: %1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"89\"/>\n        <source>Orbital elements updated &amp; loaded.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"111\"/>\n        <source>Radio IDs</source>\n        <translation type=\"unfinished\">Radio ID-nummrar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"113\"/>\n        <source>Contacts</source>\n        <translation type=\"unfinished\">Kontakter</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"115\"/>\n        <source>Group Lists</source>\n        <translation type=\"unfinished\">Grupplistor</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"117\"/>\n        <source>Channels</source>\n        <translation type=\"unfinished\">Kanaler</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"119\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Zoner</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"121\"/>\n        <source>Scan Lists</source>\n        <translation type=\"unfinished\">Scan Lists</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"123\"/>\n        <source>GPS/APRS</source>\n        <translation type=\"unfinished\">GPS/APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"125\"/>\n        <source>Roaming Channels</source>\n        <translation type=\"unfinished\">Roaming-kanaler</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"128\"/>\n        <source>Roaming Zones</source>\n        <translation type=\"unfinished\">Roamingzoner</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"132\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"145\"/>\n        <source>Unsaved changes to codeplug.</source>\n        <translation type=\"unfinished\">Ej sparade ändringar i codeplug.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/mainwindow.cc\" line=\"146\"/>\n        <source>There are unsaved changes to the current codeplug. These changes are lost if you proceed.</source>\n        <translation type=\"unfinished\">Det finns osparade ändringar av den aktuella kodpluggen. Dessa ändringar går förlorade om du fortsätter.</translation>\n    </message>\n</context>\n<context>\n    <name>MultiChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"46\"/>\n        <source>[Selected]</source>\n        <translation>[Vald]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/channelselectiondialog.cc\" line=\"69\"/>\n        <source>Select a channel:</source>\n        <translation>Välj kanal:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiGroupCallSelectionDialog</name>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"18\"/>\n        <source>Show private calls</source>\n        <translation>Visa privata samtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/contactselectiondialog.cc\" line=\"42\"/>\n        <source>Select a group call:</source>\n        <translation>Välj ett gruppsamtal:</translation>\n    </message>\n</context>\n<context>\n    <name>MultiRoamingChannelSelectionDialog</name>\n    <message>\n        <location filename=\"../src/roamingchannelselectiondialog.cc\" line=\"12\"/>\n        <source>Select roaming channels</source>\n        <translation>Välj roamingkanaler</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListView</name>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"73\"/>\n        <source>Cannot delete GPS system</source>\n        <translation>Kan inte radera GPS-systemet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"74\"/>\n        <source>Cannot delete GPS system: You have to select a GPS system first.</source>\n        <translation>Kan inte ta bort GPS-system: Du måste först välja ett GPS-system.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete positioning system?</source>\n        <translation>Ta bort positioneringssystemet?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"83\"/>\n        <source>Delete positioning system %1?</source>\n        <translation>Ta bort positioneringssystemet &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.cc\" line=\"87\"/>\n        <source>Delete %1 positioning systems?</source>\n        <translation>Ta bort %1 positioneringssystem?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"35\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Obs!&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;\n&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Göm&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"72\"/>\n        <source>Add GPS System</source>\n        <translation>Lägg till GPS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"75\"/>\n        <source>Alt+G</source>\n        <translation>Alt+G</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"85\"/>\n        <source>Add APRS System</source>\n        <translation>Lägg till APRS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"88\"/>\n        <source>Alt+A</source>\n        <translation>Alt+A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"95\"/>\n        <source>Delete Position System</source>\n        <translation>Ta bort positioneringssystem</translation>\n    </message>\n    <message>\n        <location filename=\"../src/positioningsystemlistview.ui\" line=\"98\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>PositioningSystemListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"789\"/>\n        <source>DMR</source>\n        <translation>DMR</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"791\"/>\n        <source>APRS</source>\n        <translation>APRS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"801\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"829\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"837\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"818\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"822\"/>\n        <source>[Selected]</source>\n        <translation>[Vald]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"853\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"854\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"855\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"856\"/>\n        <source>Period</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"857\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"858\"/>\n        <source>Message</source>\n        <translation>Meddelande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"859\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyDelegate</name>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"74\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"75\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"112\"/>\n        <source>False</source>\n        <translation>Falsk</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"113\"/>\n        <source>True</source>\n        <translation>Sann</translation>\n    </message>\n    <message>\n        <location filename=\"../src/propertydelegate.cc\" line=\"142\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>PropertyWrapper</name>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"352\"/>\n        <source>new element</source>\n        <translation>nytt element</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"528\"/>\n        <source>Property</source>\n        <translation>Egenskap</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"529\"/>\n        <source>Value</source>\n        <translation>Värde</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"530\"/>\n        <source>Description</source>\n        <translation>Beskrivning</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"592\"/>\n        <source>true</source>\n        <translation>sann</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"593\"/>\n        <source>false</source>\n        <translation>falsk</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"614\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"616\"/>\n        <source>Off</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"627\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"635\"/>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"643\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"645\"/>\n        <source>Instance of %1</source>\n        <translation>Förekomst av &apos;%1&apos;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/extensionwrapper.cc\" line=\"650\"/>\n        <source>List of %1 instances</source>\n        <translation>Lista över %1 instanser</translation>\n    </message>\n</context>\n<context>\n    <name>QObject</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"126\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n</context>\n<context>\n    <name>RXGroupListDialog</name>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"17\"/>\n        <source>Create Group List</source>\n        <translation>Skapa grupplista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"24\"/>\n        <source>Edit Group List</source>\n        <translation>Redigera grupplista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"82\"/>\n        <source>Cannot remove group call</source>\n        <translation>Kan inte ta bort gruppsamtal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.cc\" line=\"83\"/>\n        <source>Cannot remove group call: You have to select at least one group call first.</source>\n        <translation>Kan inte ta bort gruppsamtal: Du måste först välja minst ett gruppsamtal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"65\"/>\n        <source>Add Contact</source>\n        <translation>Lägg till kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"68\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"75\"/>\n        <source>Remove Contact</source>\n        <translation>Ta bort kontakt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"78\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/rxgrouplistdialog.ui\" line=\"88\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIDListView</name>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"61\"/>\n        <source>Cannot delete radio IDs</source>\n        <translation>Kan inte radera radio-ID:n</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"62\"/>\n        <source>Cannot delete radio IDs: You have to select a radio ID first.</source>\n        <translation>Kan inte radera radio-ID:n: Du måste först välja ett radio-ID.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID?</source>\n        <translation>Ta bort radio-ID?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"71\"/>\n        <source>Delete radio ID %1?</source>\n        <translation>Ta bort radio-ID &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete scan lists?</source>\n        <translation>Ta bort skanningslistor?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.cc\" line=\"75\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Ta bort %1 skanningslistor?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"22\"/>\n        <source>Default Radio ID</source>\n        <translation>Standard radio-ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"46\"/>\n        <source>Add Radio ID</source>\n        <translation>Lägg till radio-ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioidlistview.ui\" line=\"53\"/>\n        <source>Delete Radio ID</source>\n        <translation>Ta bort radio-ID</translation>\n    </message>\n</context>\n<context>\n    <name>RadioIdListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"998\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1013\"/>\n        <source>Type</source>\n        <translation>Typ</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1014\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1015\"/>\n        <source>Number</source>\n        <translation>Nummer</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"1016\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>RadioSelectionDialog</name>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"14\"/>\n        <source>Cannot auto-detect radio</source>\n        <translation>Kan inte automatiskt upptäcka radio</translation>\n    </message>\n    <message>\n        <location filename=\"../src/radioselectiondialog.ui\" line=\"20\"/>\n        <source>Select a specific radio</source>\n        <translation>Välj en specifik radio</translation>\n    </message>\n</context>\n<context>\n    <name>ReleaseNotes</name>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"31\"/>\n        <source>Cannot download release notes from https://github.com/hmatuschek/qdmr\n\t %1</source>\n        <translation>Det går inte att ladda ned release notes från https://github.com/hmatuschek/qdmr\n\t %1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"43\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease is not a JSON object!</source>\n        <translation>Kan inte läsa release notes från https://github.com/hmatuschek/qdmr\n\tRelease är inte ett JSON-objekt!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"48\"/>\n        <source>Cannot read release notes from https://github.com/hmatuschek/qdmr\n\tRelease does not contain a release note.</source>\n        <translation>Kan inte läsa release notes från https://github.com/hmatuschek/qdmr\n\tReleasen innehåller ingen release note.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/releasenotes.cc\" line=\"59\"/>\n        <source>qDMR was updated to version %1</source>\n        <translation>qdmr uppdaterades till version %1</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelDialog</name>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"28\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"35\"/>\n        <source>RX Frequency [MHz]</source>\n        <translation>RX-frekvens [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"42\"/>\n        <source>TX Frequency [MHz]</source>\n        <translation>Sändningsfrekvens [MHz]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"49\"/>\n        <source>Time Slot</source>\n        <translation>Tidslucka</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"56\"/>\n        <source>Color Code</source>\n        <translation>Färgkod</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"74\"/>\n        <location filename=\"../src/roamingchanneldialog.ui\" line=\"95\"/>\n        <source>Selected</source>\n        <translation>Vald</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"37\"/>\n        <source>Edit roaming channel</source>\n        <translation>Redigera roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"39\"/>\n        <source>Create roaming channel</source>\n        <translation>Skapa roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"46\"/>\n        <source>TS 1</source>\n        <translation>TS 1</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchanneldialog.cc\" line=\"47\"/>\n        <source>TS 2</source>\n        <translation>TS 2</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListView</name>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"26\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Obs!&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Tips:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Göm&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"51\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Lägg till roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.ui\" line=\"58\"/>\n        <source>Delete Roaming Channel</source>\n        <translation>Ta bort roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"63\"/>\n        <source>Cannot delete roaming channel</source>\n        <translation>Det går inte att ta bort roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"64\"/>\n        <source>Cannot delete roaming channel: You have to select a channel first.</source>\n        <translation>Kan inte ta bort roamingkanal: Du måste först välja en kanal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"74\"/>\n        <source>Delete roaming channel %1?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingchannellistview.cc\" line=\"78\"/>\n        <source>Delete %1 roaming channel?</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"557\"/>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"565\"/>\n        <source>[Selected]</source>\n        <translation>[Vald]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"579\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"594\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"595\"/>\n        <source>RX Frequency</source>\n        <translation>RX-frekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"596\"/>\n        <source>TX Frequency</source>\n        <translation>Sändningsfrekvens</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"598\"/>\n        <source>TS</source>\n        <translation>TS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"599\"/>\n        <source>Zones</source>\n        <translation type=\"unfinished\">Zoner</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"600\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"597\"/>\n        <source>CC</source>\n        <translation>CC</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingChannelRefListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"627\"/>\n        <source>Roaming Channel</source>\n        <translation>Roaming kanal</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"953\"/>\n        <source>%1 (containing %2 channels)</source>\n        <translation>%1 (innehåller %2 kanaler)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"960\"/>\n        <source>Roaming zone</source>\n        <translation>Roamingzon</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneDialog</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"15\"/>\n        <source>Create Roaming Zone</source>\n        <translation>Skapa roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"22\"/>\n        <source>Set Roaming Zone</source>\n        <translation>Redigera roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"83\"/>\n        <source>Cannot remove channels.</source>\n        <translation>Kan inte ta bort kanaler.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"84\"/>\n        <source>Cannot remove channels. Select at least one channel first.</source>\n        <translation>Kan inte ta bort kanaler. Välj minst en kanal först.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"38\"/>\n        <source>Name:</source>\n        <translation>Namn:</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"62\"/>\n        <source>Add Roaming Channel</source>\n        <translation>Lägg till roamingkanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"69\"/>\n        <source>Add DMR Channel</source>\n        <translation>Lägg till DMR-kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"72\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"79\"/>\n        <source>Remove Channel</source>\n        <translation>Ta bort kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"82\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.ui\" line=\"92\"/>\n        <source>Extension</source>\n        <translation>Förlängning</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneListView</name>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"52\"/>\n        <source>Generate roaming zone</source>\n        <translation>Skapa roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"53\"/>\n        <source>Create a roaming zone by collecting all channels with these group calls.</source>\n        <translation>Skapa en roamingzon genom att samla alla kanaler med dessa gruppsamtal.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"87\"/>\n        <source>Cannot delete roaming zone</source>\n        <translation>Det går inte att ta bort roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"88\"/>\n        <source>Cannot delete roaming zone: You have to select a zone first.</source>\n        <translation>Det går inte att ta bort roamingzon: Du måste först välja en zon.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone?</source>\n        <translation>Ta bort roamingzon?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"98\"/>\n        <source>Delete roaming zone %1?</source>\n        <translation>Ta bort roamingzon &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete roaming zones?</source>\n        <translation>Ta bort roamingzoner?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.cc\" line=\"102\"/>\n        <source>Delete %1 roaming zones?</source>\n        <translation>Ta bort %1 roamingzoner?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"23\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Obs!&lt;/span&gt; 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. &lt;/p&gt;\n&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;\n&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Göm&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"48\"/>\n        <source>Add Roaming Zone</source>\n        <translation>Lägg till roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"51\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"58\"/>\n        <source>Generate Roaming Zone</source>\n        <translation>Skapa roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"65\"/>\n        <source>Delete Roaming Zone</source>\n        <translation>Ta bort roamingzon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonelistview.ui\" line=\"68\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>RoamingZoneSelect</name>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"121\"/>\n        <source>[None]</source>\n        <translation type=\"unfinished\">[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/roamingzonedialog.cc\" line=\"122\"/>\n        <source>[Default]</source>\n        <translation type=\"unfinished\">[Standard]</translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteDatabaseDialog</name>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"20\"/>\n        <source>Edit satellite database</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"48\"/>\n        <source>Add</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitedatabasedialog.ui\" line=\"55\"/>\n        <source>Delete</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteSelectionDialog</name>\n    <message>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"20\"/>\n        <location filename=\"../src/satelliteselectiondialog.ui\" line=\"26\"/>\n        <source>Select a satellite</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SatelliteTransponderDialog</name>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"14\"/>\n        <source>Edit Satellite Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"26\"/>\n        <source>FM Voice Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"32\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"105\"/>\n        <source>Uplink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"42\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"121\"/>\n        <source>Uplink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"58\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"143\"/>\n        <source>Downlink Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"68\"/>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"159\"/>\n        <source>Downlink Tone</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"93\"/>\n        <source>APRS Transponder</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"184\"/>\n        <source>Beacon</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/satellitetransponderdialog.ui\" line=\"196\"/>\n        <source>Beacon Frequency</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ScanListDialog</name>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"20\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"13\"/>\n        <source>Edit Scan List</source>\n        <translation>Redigera skanningslista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"38\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"48\"/>\n        <source>Primary Channel (50%)</source>\n        <translation>Primär kanal (50 %)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"65\"/>\n        <source>Secondary Channel (25%)</source>\n        <translation>Sekundär kanal (25 %)</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"82\"/>\n        <source>Transmit Channel</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"113\"/>\n        <source>Add Channel</source>\n        <translation>Lägg till kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"116\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"123\"/>\n        <source>Remove Channel</source>\n        <translation>Ta bort kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"126\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.ui\" line=\"136\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"24\"/>\n        <source>Create Scan List</source>\n        <translation>Skapa skanningslista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"34\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"36\"/>\n        <source>[None]</source>\n        <translation>[Ingen]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"35\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"37\"/>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"39\"/>\n        <source>[Selected]</source>\n        <translation>[Vald]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistdialog.cc\" line=\"38\"/>\n        <source>[Last]</source>\n        <translation>[Sista]</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsView</name>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"42\"/>\n        <source>Cannot delete scanlist</source>\n        <translation>Kan inte radera skanningslistan</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"43\"/>\n        <source>Cannot delete scanlist: You have to select a scanlist first.</source>\n        <translation>Kan inte ta bort skanningslista: Du måste först välja en skanningslista.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list?</source>\n        <translation>Ta bort skanningslistan?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"53\"/>\n        <source>Delete scan list %1?</source>\n        <translation>Ta bort skanningslistan &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete scan lists?</source>\n        <translation>Ta bort skanningslistor?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.cc\" line=\"57\"/>\n        <source>Delete %1 scan lists?</source>\n        <translation>Ta bort %1 skanningslistor?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"35\"/>\n        <source>Add Scan List</source>\n        <translation>Lägg till skanningslista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"38\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"45\"/>\n        <source>Delete Scan List</source>\n        <translation>Ta bort skanningslista</translation>\n    </message>\n    <message>\n        <location filename=\"../src/scanlistsview.ui\" line=\"48\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ScanListsWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"887\"/>\n        <source>Scan-List</source>\n        <translation>Scanlista</translation>\n    </message>\n</context>\n<context>\n    <name>SearchPopup</name>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"18\"/>\n        <source>Ctrl+F</source>\n        <translation>Ctrl+F</translation>\n    </message>\n    <message>\n        <location filename=\"../src/searchpopup.cc\" line=\"92\"/>\n        <location filename=\"../src/searchpopup.cc\" line=\"107\"/>\n        <source>%1/%2</source>\n        <translation>%1/%2</translation>\n    </message>\n</context>\n<context>\n    <name>SelectiveCallBox</name>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"16\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"17\"/>\n        <source>CTCSS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"18\"/>\n        <source>DCS</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"40\"/>\n        <source>Hz</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/selectivecallbox.cc\" line=\"50\"/>\n        <source>Inverted</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>SettingsDialog</name>\n    <message>\n        <location filename=\"../src/settings.cc\" line=\"461\"/>\n        <source>Warning!</source>\n        <translation>Varning!</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"20\"/>\n        <source>Settings</source>\n        <translation>Inställningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"42\"/>\n        <source>Location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"48\"/>\n        <source>System location</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"62\"/>\n        <source>Locator</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"81\"/>\n        <source>Repeater Info Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"96\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"132\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"146\"/>\n        <location filename=\"../src/settingsdialog.ui\" line=\"160\"/>\n        <source>enable</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"212\"/>\n        <source>Radio Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"221\"/>\n        <source>Update codeplug</source>\n        <translation>Uppdatera codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"228\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Uppdatera kodpluggen på radion. Om det inte väljs, åsidosätts kodpluggen på radion med möjligen ofullständiga standardvärden.&lt;/p&gt;\n&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"238\"/>\n        <source>Auto-enable GPS</source>\n        <translation>Autoaktivera GPS</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"245\"/>\n        <source>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</source>\n        <translation>När ett GPS- eller APRS-system definieras och används för valfri kanal, aktiveras GPS-modulen automatiskt.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"262\"/>\n        <source>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</source>\n        <translation>När en roamingzon definieras och används av en kanal, aktiveras automatisk roaming.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"255\"/>\n        <source>Auto-enable roaming</source>\n        <translation>Aktivera roaming automatiskt</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"30\"/>\n        <source>Data Sources</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"110\"/>\n        <source>World</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"115\"/>\n        <source>North America</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"171\"/>\n        <source>Programming</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"183\"/>\n        <source>Radio Interfaces</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"189\"/>\n        <source>disable auto-detect</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"272\"/>\n        <source>Ignore verification warnings</source>\n        <translation>Ignorera verifieringsvarningar</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"279\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"289\"/>\n        <source>Ignore frequency limits</source>\n        <translation>Ignorera frekvensgränser</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"296\"/>\n        <source>Do not set this option unless you know what you are doing.</source>\n        <translation>Ställ inte in det här alternativet om du inte vet vad du gör.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"306\"/>\n        <source>Update Device Clock</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"329\"/>\n        <source>Call-Sign DB</source>\n        <translation>Anropssignal DB</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"335\"/>\n        <source>Limit number of DB entries</source>\n        <translation>Begränsa antalet DB-poster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"342\"/>\n        <source>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</source>\n        <translation>När det är aktiverat kommer antalet DB-poster att vara begränsat. Annars genereras det maximala antalet poster (enhetsberoende).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"352\"/>\n        <source>Number of DB entries</source>\n        <translation>Antal DB-poster</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"365\"/>\n        <source>Specifies the number of DB entries (if enabled above).</source>\n        <translation>Anger antalet DB-poster (om aktiverat ovan).</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"378\"/>\n        <source>Select using my DMR ID</source>\n        <translation>Välj med mitt DMR-ID</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"385\"/>\n        <source>If enabled, the entries are selected using the users DMR ID.</source>\n        <translation>Om det är aktiverat väljs posterna med användarens DMR-ID.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"395\"/>\n        <source>Select using prefixes</source>\n        <translation>Välj med prefix</translation>\n    </message>\n    <message>\n        <location filename=\"../src/settingsdialog.ui\" line=\"402\"/>\n        <source>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</source>\n        <translation>Om aktiverat används dessa kommaseparerade DMR ID-prefix för att välja anropssignal DB-posterna.</translation>\n    </message>\n</context>\n<context>\n    <name>SquelchEdit</name>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"32\"/>\n        <source>Open</source>\n        <translation type=\"unfinished\">Öppet</translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"45\"/>\n        <source>Uses the global squelch setting if enabled.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/squelchedit.ui\" line=\"48\"/>\n        <source>Default</source>\n        <translation type=\"unfinished\">Standard</translation>\n    </message>\n</context>\n<context>\n    <name>TimeslotSelect</name>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"6\"/>\n        <source>Slot 1</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/timeslotselect.cc\" line=\"7\"/>\n        <source>Slot 2</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>TransponderFrequencyEditor</name>\n    <message>\n        <location filename=\"../src/transponderfrequencydelegate.cc\" line=\"28\"/>\n        <source>None</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>VerifyDialog</name>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"14\"/>\n        <source>Verify Codeplug</source>\n        <translation>Verifiera Codeplug</translation>\n    </message>\n    <message>\n        <location filename=\"../src/verifydialog.ui\" line=\"20\"/>\n        <source>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n<context>\n    <name>ZoneDialog</name>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"20\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"18\"/>\n        <source>Edit Zone</source>\n        <translation>Redigera zon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"39\"/>\n        <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>\n        <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Obs!\n&lt;/span&gt; Zoner är samlingar av kanaler som vanligtvis är giltiga för en specifik region.\nDvs en samling kanaler för repeatrar inom en viss region. &lt;/p&gt;\n&lt;p align=&quot;justify&quot;&gt;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;amp; B) och programmera in dem individuellt i radion.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;\n&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Göm\n&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"54\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"77\"/>\n        <source>Channels A</source>\n        <translation>Kanaler A</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"95\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"138\"/>\n        <source>add</source>\n        <translation>Lägg till</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"102\"/>\n        <location filename=\"../src/zonedialog.ui\" line=\"145\"/>\n        <source>remove</source>\n        <translation>ta bort</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"120\"/>\n        <source>Channels B</source>\n        <translation>Kanaler B</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.ui\" line=\"160\"/>\n        <source>Extension</source>\n        <translation type=\"unfinished\">Förlängning</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"28\"/>\n        <source>Create Zone</source>\n        <translation>Skapa zon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"73\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"104\"/>\n        <source>Cannot remove channel</source>\n        <translation>Kan inte ta bort kanalen</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonedialog.cc\" line=\"74\"/>\n        <location filename=\"../src/zonedialog.cc\" line=\"105\"/>\n        <source>Select at least one channel first.</source>\n        <translation>Välj minst en kanal först.</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListView</name>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"41\"/>\n        <source>Cannot delete zone</source>\n        <translation>Kan inte ta bort zon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"42\"/>\n        <source>Cannot delete zone: You have to select a zone first.</source>\n        <translation>Kan inte ta bort zon: Du måste först välja en zon.</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone?</source>\n        <translation>Ta bort zon?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"52\"/>\n        <source>Delete zone %1?</source>\n        <translation>Ta bort zon &apos;%1&apos;?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete zones?</source>\n        <translation>Ta bort zoner?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.cc\" line=\"56\"/>\n        <source>Delete %1 zones?</source>\n        <translation>Ta bort %1 zoner?</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"32\"/>\n        <source>Add Zone</source>\n        <translation>Lägg till zon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"35\"/>\n        <source>Alt++</source>\n        <translation>Alt++</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"42\"/>\n        <source>Delete Zone</source>\n        <translation>Ta bort zon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/zonelistview.ui\" line=\"45\"/>\n        <source>Alt+-</source>\n        <translation>Alt+-</translation>\n    </message>\n</context>\n<context>\n    <name>ZoneListWrapper</name>\n    <message>\n        <location filename=\"../src/configitemwrapper.cc\" line=\"758\"/>\n        <source>Zone</source>\n        <translation>Zon</translation>\n    </message>\n</context>\n<context>\n    <name>aprssystemdialog</name>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"20\"/>\n        <source>Edit APRS System</source>\n        <translation>Redigera APRS-system</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"30\"/>\n        <source>Basic</source>\n        <translation>Grundläggande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"42\"/>\n        <source>Name</source>\n        <translation>Namn</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"52\"/>\n        <source>Channel</source>\n        <translation>Kanal</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"62\"/>\n        <source>Source</source>\n        <translation>Källa</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"100\"/>\n        <source>Destination</source>\n        <translation>Destination</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"138\"/>\n        <source>Path</source>\n        <translation>Väg</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"145\"/>\n        <source>Icon</source>\n        <translation>Ikon</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"152\"/>\n        <source>Update period [s]</source>\n        <translation>Uppdateringsperiod [s]</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"159\"/>\n        <source>Message</source>\n        <translation>Meddelande</translation>\n    </message>\n    <message>\n        <location filename=\"../src/aprssystemdialog.ui\" line=\"192\"/>\n        <source>Extensions</source>\n        <translation type=\"unfinished\">Extensions</translation>\n    </message>\n</context>\n<context>\n    <name>main</name>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"25\"/>\n        <source>Codeplug file to load.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n    <message>\n        <location filename=\"../src/main.cc\" line=\"26\"/>\n        <source>Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.</source>\n        <translation type=\"unfinished\"></translation>\n    </message>\n</context>\n</TS>\n"
  },
  {
    "path": "lib/CMakeLists.txt",
    "content": "IF (APPLE)\n SET(hid_SOURCES hid_macos.cc)\n SET(hid_HEADERS hid_macos.hh)\nELSE (APPLE)\n SET(hid_SOURCES hid_libusb.cc)\n SET(hid_HEADERS hid_libusb.hh)\nENDIF(APPLE)\n\nconfigure_file(config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)\n\nqt_add_library(libdmrconf SHARED\n  ${hid_SOURCES}\n  ${CMAKE_CURRENT_BINARY_DIR}/config.h\n  utils.cc crc32.cc addressmap.cc radiointerface.cc errorstack.cc frequency.cc interval.cc\n  ranges.cc dummyfilereader.cc chirpformat.cc signaling.cc radio.cc dfu_libusb.cc usbserial.cc\n  radioinfo.cc usbdevice.cc radiolimits.cc csvreader.cc dfufile.cc userdatabase.cc logger.cc level.cc\n  melody.cc melody_stream.cc visitor.cc configlabelingvisitor.cc configcopyvisitor.cc\n  intermediaterepresentation.cc configmergevisitor.cc configobject.cc configreference.cc config.cc\n  radiosettings.cc contact.cc rxgrouplist.cc channel.cc zone.cc scanlist.cc gpssystem.cc codeplug.cc\n  roamingzone.cc roamingchannel.cc callsigndb.cc talkgroupdatabase.cc radioid.cc transferflags.cc\n  encryptionextension.cc commercial_extension.cc smsextension.cc gnsssettings.cc dmrsettings.cc\n  channel_extension.cc bootsettings.cc audiosettings.cc tonesettings.cc packetstream.cc\n  satellitedatabase.cc orbitalelementsdatabase.cc transponderdatabase.cc satelliteconfig.cc\n  tyt_radio.cc tyt_interface.cc tyt_codeplug.cc tyt_callsigndb.cc tyt_extensions.cc\n  md2017.cc md2017_codeplug.cc md2017_callsigndb.cc md2017_filereader.cc md2017_limits.cc\n  md390.cc md390_codeplug.cc md390_filereader.cc md390_limits.cc\n  uv390.cc uv390_codeplug.cc uv390_callsigndb.cc uv390_filereader.cc uv390_limits.cc\n  dm1701.cc dm1701_codeplug.cc dm1701_callsigndb.cc dm1701_filereader.cc dm1701_limits.cc\n  radioddity_radio.cc radioddity_codeplug.cc radioddity_extensions.cc radioddity_interface.cc\n  rd5r.cc rd5r_codeplug.cc rd5r_filereader.cc rd5r_limits.cc\n  gd77.cc gd77_codeplug.cc gd77_callsigndb.cc gd77_filereader.cc gd77_limits.cc\n  opengd77_interface.cc opengd77base.cc opengd77base_codeplug.cc opengd77base_callsigndb.cc\n  opengd77base_satelliteconfig.cc opengd77_extension.cc opengd77_limits.cc\n  opengd77.cc opengd77_codeplug.cc opengd77_callsigndb.cc opengd77_satelliteconfig.cc\n  openuv380.cc openuv380_codeplug.cc openuv380_callsigndb.cc openuv380_satelliteconfig.cc\n  openrtx.cc openrtx_link.cc openrtx_interface.cc openrtx_codeplug.cc\n  c7000device.cc\n  gd73.cc gd73_interface.cc gd73_codeplug.cc gd73_filereader.cc gd73_limits.cc\n  anytone_interface.cc anytone_radio.cc anytone_codeplug.cc anytone_extension.cc anytone_limits.cc\n  anytone_satelliteconfig.cc anytone_settingsextension.cc\n  d868uv.cc d868uv_codeplug.cc d868uv_callsigndb.cc d868uv_limits.cc\n  d878uv.cc d878uv_codeplug.cc d878uv_limits.cc\n  d578uv.cc d578uv_codeplug.cc d578uv_limits.cc\n  d878uv2.cc d878uv2_codeplug.cc d878uv2_limits.cc d878uv2_callsigndb.cc\n  d168uv.cc d168uv_codeplug.cc d168uv_limits.cc d168uv_satelliteconfig.cc\n  dmr6x2uv.cc dmr6x2uv_codeplug.cc dmr6x2uv_limits.cc\n  dmr6x2uv2.cc dmr6x2uv2_codeplug.cc dmr6x2uv2_limits.cc\n  auctus_a6_interface.cc\n  dr1801uv.cc dr1801uv_interface.cc dr1801uv_codeplug.cc dr1801uv_filereader.cc dr1801uv_limits.cc\n  dm32uv.cc dm32uv_interface.cc dm32uv_limits.cc dm32uv_codeplug.cc dm32uv_callsigndb.cc)\n\ntarget_sources(libdmrconf PUBLIC FILE_SET HEADERS\n  BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}\n  FILES\n  ${CMAKE_CURRENT_BINARY_DIR}/config.h\n  libdmrconf.hh utils.hh ${hid_HEADERS} crc32.hh addressmap.hh radiointerface.hh errorstack.hh\n  frequency.hh interval.hh ranges.hh dummyfilereader.hh chirpformat.hh signaling.hh radio.hh level.hh\n  dfu_libusb.hh usbserial.hh radioinfo.hh usbdevice.hh radiolimits.hh csvreader.hh dfufile.hh\n  userdatabase.hh logger.hh melody.hh melody_stream.hh visitor.hh configlabelingvisitor.hh configcopyvisitor.hh\n  intermediaterepresentation.hh configmergevisitor.hh configobject.hh configreference.hh config.hh\n  radiosettings.hh contact.hh rxgrouplist.hh channel.hh zone.hh scanlist.hh gpssystem.hh codeplug.hh\n  roamingzone.hh roamingchannel.hh callsigndb.hh talkgroupdatabase.hh radioid.hh transferflags.hh\n  encryptionextension.hh commercial_extension.hh smsextension.hh gnsssettings.hh dmrsettings.hh\n  channel_extension.hh bootsettings.hh audiosettings.hh tonesettings.hh packetstream.hh\n  satellitedatabase.hh orbitalelementsdatabase.hh transponderdatabase.hh satelliteconfig.hh\n  tyt_radio.hh tyt_interface.hh tyt_codeplug.hh tyt_callsigndb.hh tyt_extensions.hh\n  md2017.hh md2017_codeplug.hh md2017_callsigndb.hh md2017_filereader.hh md2017_limits.hh\n  md390.hh md390_codeplug.hh md390_filereader.hh md390_limits.hh\n  uv390.hh uv390_codeplug.hh uv390_callsigndb.hh uv390_filereader.hh uv390_limits.hh\n  dm1701.hh dm1701_codeplug.hh dm1701_callsigndb.hh dm1701_filereader.hh dm1701_limits.hh\n  radioddity_radio.hh radioddity_codeplug.hh radioddity_extensions.hh radioddity_interface.hh\n  rd5r.hh rd5r_codeplug.hh rd5r_filereader.hh rd5r_limits.hh\n  gd77.hh gd77_codeplug.hh gd77_callsigndb.hh gd77_filereader.hh gd77_limits.hh\n  opengd77_interface.hh opengd77base.hh opengd77base_codeplug.hh opengd77base_callsigndb.hh\n  opengd77base_satelliteconfig.hh opengd77_extension.hh opengd77_limits.hh\n  opengd77.hh opengd77_codeplug.hh opengd77_callsigndb.hh opengd77_satelliteconfig.hh\n  openuv380.hh openuv380_codeplug.hh openuv380_callsigndb.hh openuv380_satelliteconfig.hh\n  openrtx.hh openrtx_link.hh openrtx_interface.hh openrtx_codeplug.hh\n  c7000device.hh\n  gd73.hh gd73_interface.hh gd73_codeplug.hh gd73_filereader.hh gd73_limits.hh\n  anytone_interface.hh anytone_radio.hh anytone_codeplug.hh anytone_extension.hh anytone_limits.hh\n  anytone_satelliteconfig.hh anytone_settingsextension.hh\n  d868uv.hh d868uv_codeplug.hh d868uv_callsigndb.hh d868uv_limits.hh\n  d878uv.hh d878uv_codeplug.hh d878uv_limits.hh\n  d578uv.hh d578uv_codeplug.hh d578uv_limits.hh\n  d878uv2.hh d878uv2_codeplug.hh d878uv2_limits.hh d878uv2_callsigndb.hh\n  d168uv.hh d168uv_codeplug.hh d168uv_limits.hh d168uv_satelliteconfig.hh\n  dmr6x2uv.hh dmr6x2uv_codeplug.hh dmr6x2uv_limits.hh\n  dmr6x2uv2.hh dmr6x2uv2_codeplug.hh dmr6x2uv2_limits.hh\n  auctus_a6_interface.hh\n  dr1801uv.hh dr1801uv_interface.hh dr1801uv_codeplug.hh dr1801uv_filereader.hh dr1801uv_limits.hh\n  dm32uv.hh dm32uv_interface.hh dm32uv_limits.hh dm32uv_codeplug.hh dm32uv_callsigndb.hh)\n\n\nset_target_properties(libdmrconf PROPERTIES\n  MACOSX_RPATH TRUE\n  OUTPUT_NAME dmrconf\n  VERSION \"${PROJECT_VERSION}\"\n  SOVERSION \"${PROJECT_VERSION_MAJOR}\")\n\ntarget_link_libraries(libdmrconf PRIVATE Qt6::Core Qt6::SerialPort Qt6::Positioning Qt6::Network\n  Qt6::Concurrent Qt6::Multimedia\n  ${LIBUSB_1_LIBRARIES} ${YAMLCPP_LIBRARIES} ${ADDITIONAL_LIBS})\n\ninstall(TARGETS libdmrconf DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}\n        FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libdmrconf/)\n\nif (UNIX AND NOT APPLE AND ${INSTALL_UDEV_RULES})\n  install(FILES \"${CMAKE_SOURCE_DIR}/dist/99-qdmr.rules\" DESTINATION ${INSTALL_UDEV_PATH})\nendif(UNIX AND NOT APPLE AND ${INSTALL_UDEV_RULES})\n\n"
  },
  {
    "path": "lib/addressmap.cc",
    "content": "#include \"addressmap.hh\"\n#include <algorithm>\n\nAddressMap::AddressMap()\n  : _items()\n{\n  // pass...\n}\n\nAddressMap::AddressMap(const AddressMap &other)\n  : _items(other._items)\n{\n  // pass...\n}\n\nAddressMap &\nAddressMap::operator =(const AddressMap &other) {\n  _items = other._items;\n  return *this;\n}\n\n\nvoid\nAddressMap::clear() {\n  _items.clear();\n}\n\nbool\nAddressMap::add(uint32_t addr, uint32_t len, int idx) {\n  if (0 > idx)\n    idx = _items.size();\n  AddrMapItem item(addr, len, idx);\n\n  std::vector<AddrMapItem>::iterator at = std::lower_bound(_items.begin(), _items.end(), item);\n  if (_items.end() == at)\n    _items.push_back(item);\n  else\n    _items.insert(at, item);\n  return true;\n}\n\nbool\nAddressMap::rem(uint32_t idx) {\n  std::vector<AddrMapItem>::iterator at = _items.begin();\n  for (; at!=_items.end(); at++) {\n    if (at->index == idx)\n      break;\n  }\n  if (_items.end() == at)\n    return false;\n  _items.erase(at);\n  return true;\n}\n\nbool\nAddressMap::contains(uint32_t addr) const {\n  return 0 <= find(addr);\n}\n\nint\nAddressMap::find(uint32_t addr) const {\n  std::vector<AddrMapItem>::const_iterator at = std::lower_bound(_items.begin(), _items.end(), addr);\n  if (_items.end() == at)\n    return _items.back().contains(addr) ? _items.back().index : -1;\n  if (at->contains(addr))\n    return at->index;\n  if (_items.begin() == at)\n    return -1;\n  --at;\n  return at->contains(addr) ? at->index : -1;\n}\n"
  },
  {
    "path": "lib/addressmap.hh",
    "content": "#ifndef ADDRESSMAP_HH\n#define ADDRESSMAP_HH\n\n#include <cinttypes>\n#include <vector>\n\n/** This class represents a memory map.\n * That is, it maintains a vector of memory regions (address and length) that can be searched\n * efficiently. This should speedup the generation of codeplugs consisting of many small memory\n * sections.\n *\n * @ingroup util */\nclass AddressMap\n{\npublic:\n  /** Empty constructor. */\n  AddressMap();\n  /** Copy constructor. */\n  AddressMap(const AddressMap &other);\n\n  /** Copy assignment. */\n  AddressMap &operator=(const AddressMap &other);\n\n  /** Clears the address map. */\n  void clear();\n  /** Adds an item to the address map. */\n  bool add(uint32_t addr, uint32_t len, int idx=-1);\n  /** Removes an item from the address map associated with the given index. */\n  bool rem(uint32_t idx);\n  /** Returns @c true if the given address is contained in any of the memory regions. */\n  bool contains(uint32_t addr) const;\n  /** Finds the index of the memory region containing the given address. If no such region is found,\n   * -1 is returned. */\n  int find(uint32_t addr) const;\n\nprotected:\n  /** Memory map item.\n   * That is, a collection of address, length and associated index. */\n  struct AddrMapItem {\n    uint32_t address;   ///< The start address of the item.\n    uint32_t length;    ///< The size/length of the memory item.\n    uint32_t index;     ///< The associated (element) index.\n\n    /** Constructor. */\n    inline AddrMapItem(uint32_t addr, uint32_t len, uint32_t idx)\n      : address(addr), length(len), index(idx) {\n      // pass...\n    }\n    /** Comparison operator. */\n    inline bool operator<(const AddrMapItem &other) const {\n      return address < other.address;\n    }\n    /** Comparison operator. */\n    inline bool operator<(uint32_t addr) const {\n      return address < addr;\n    }\n    /** Returns @c true if the given address is contained within this memory region. */\n    inline bool contains(uint32_t addr) const {\n      return (address <= addr) && ((address+length) > addr);\n    }\n  };\n\nprotected:\n  /** Holds the vector of memory items, the order of these items is maintained. */\n  std::vector<AddrMapItem> _items;\n};\n\n#endif // ADDRESSMAP_HH\n"
  },
  {
    "path": "lib/anytone_codeplug.cc",
    "content": "#include \"anytone_codeplug.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include \"anytone_extension.hh\"\n#include \"config.hh\"\n#include \"intermediaterepresentation.hh\"\n#include <QTimeZone>\n#include <QRegularExpression>\n\n#define CUSTOM_CTCSS_TONE 0x33\n\n\nQVector<char> _anytone_bin_dtmf_tab = {\n  '0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'\n};\n\n\n/* ******************************************************************************************** *\n * Implementation of AnytoneCodeplug::CTCSS\n * ******************************************************************************************** */\nSelectiveCall\nAnytoneCodeplug::CTCSS::_codeTable[] = {\n  SelectiveCall(62.5),  SelectiveCall(67.0),  SelectiveCall(69.3),  SelectiveCall(71.9),\n  SelectiveCall(74.4),  SelectiveCall(77.0),  SelectiveCall(79.7),  SelectiveCall(82.5),\n  SelectiveCall(85.4),  SelectiveCall(88.5),  SelectiveCall(91.5),  SelectiveCall(94.8),\n  SelectiveCall(97.4), SelectiveCall(100.0), SelectiveCall(103.5), SelectiveCall(107.2),\n SelectiveCall(110.9), SelectiveCall(114.8), SelectiveCall(118.8), SelectiveCall(123.0),\n SelectiveCall(127.3), SelectiveCall(131.8), SelectiveCall(136.5), SelectiveCall(141.3),\n SelectiveCall(146.2), SelectiveCall(151.4), SelectiveCall(156.7), SelectiveCall(159.8),\n SelectiveCall(162.2), SelectiveCall(165.5), SelectiveCall(167.9), SelectiveCall(171.3),\n SelectiveCall(173.8), SelectiveCall(177.3), SelectiveCall(179.9), SelectiveCall(183.5),\n SelectiveCall(186.2), SelectiveCall(189.9), SelectiveCall(192.8), SelectiveCall(196.6),\n SelectiveCall(199.5), SelectiveCall(203.5), SelectiveCall(206.5), SelectiveCall(210.7),\n SelectiveCall(218.1), SelectiveCall(225.7), SelectiveCall(229.1), SelectiveCall(233.6),\n SelectiveCall(241.8), SelectiveCall(250.3), SelectiveCall(254.1), SelectiveCall()\n};\n\nuint8_t\nAnytoneCodeplug::CTCSS::encode(const SelectiveCall &code) {\n  for (uint8_t i=0; i<52; i++) {\n    if (code == _codeTable[i])\n      return i;\n  }\n  return 0;\n}\n\nSelectiveCall\nAnytoneCodeplug::CTCSS::decode(uint8_t num) {\n  if (num >= 52)\n    return SelectiveCall();\n  return _codeTable[num];\n}\n\n\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::InvertedBytemapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::InvertedBytemapElement::InvertedBytemapElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::InvertedBytemapElement::clear() {\n  memset(_data, 0xff, _size);\n}\n\nbool\nAnytoneCodeplug::InvertedBytemapElement::isEncoded(unsigned int idx) const {\n  if (idx >= _size)\n    return false;\n  return 0 == _data[idx];\n}\n\nvoid\nAnytoneCodeplug::InvertedBytemapElement::setEncoded(unsigned int idx, bool enable) {\n  if (idx >= _size)\n    return;\n  _data[idx] = enable ? 0x00 : 0xff;\n}\n\nvoid\nAnytoneCodeplug::InvertedBytemapElement::enableFirst(unsigned int n) {\n  memset(_data, 0x00, n);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ChannelElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, ChannelElement::size())\n{\n  // pass...\n}\n\nAnytoneCodeplug::ChannelElement::~ChannelElement() {\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::ChannelElement::clear() {\n  setRXFrequency(0);\n  setTXOffset(0);\n  setMode(Mode::Analog);\n  setPower(Channel::Power::Low);\n  setBandwidth(FMChannel::Bandwidth::Narrow);\n  setRXTone(SelectiveCall());\n  setTXTone(SelectiveCall());\n  setBit(0x0008, 5, false); // Unused set to 0\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::rxFrequency() const {\n  return ((unsigned)getBCD8_be(Offset::rxFrequency()))*10;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRXFrequency(unsigned hz) {\n  setBCD8_be(Offset::rxFrequency(), hz/10);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::txOffset() const {\n  return ((unsigned)getBCD8_be(Offset::txFrequencyOffset()))*10;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXOffset(unsigned hz) {\n  setBCD8_be(Offset::txFrequencyOffset(), hz/10);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::txFrequency() const {\n  unsigned rx = rxFrequency(), off = txOffset();\n  if (RepeaterMode::Simplex == repeaterMode())\n    return rx;\n  else if (RepeaterMode::Positive == repeaterMode())\n    return rx+off;\n  return rx-off;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXFrequency(unsigned hz) {\n  unsigned rx = rxFrequency();\n  if (rx == hz) {\n    setTXOffset(0); setRepeaterMode(RepeaterMode::Simplex);\n  } else if (rx < hz) {\n    setTXOffset(hz-rx); setRepeaterMode(RepeaterMode::Positive);\n  } else {\n    setTXOffset(rx-hz); setRepeaterMode(RepeaterMode::Negative);\n  }\n}\n\nAnytoneCodeplug::ChannelElement::Mode\nAnytoneCodeplug::ChannelElement::mode() const {\n  return (Mode) getUInt2(Offset::channelMode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setMode(Mode mode) {\n  setUInt2(Offset::channelMode(), (unsigned)mode);\n}\n\nChannel::Power\nAnytoneCodeplug::ChannelElement::power() const {\n  switch ((Power)getUInt2(Offset::power())) {\n  case POWER_LOW: return Channel::Power::Low;\n  case POWER_MIDDLE: return Channel::Power::Mid;\n  case POWER_HIGH: return Channel::Power::High;\n  case POWER_TURBO: return Channel::Power::Max;\n  }\n  return Channel::Power::Low;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt2(Offset::power(), (unsigned)POWER_LOW);\n    break;\n  case Channel::Power::Mid:\n    setUInt2(Offset::power(), (unsigned)POWER_MIDDLE);\n    break;\n  case Channel::Power::High:\n    setUInt2(Offset::power(), (unsigned)POWER_HIGH);\n    break;\n  case Channel::Power::Max:\n    setUInt2(Offset::power(), (unsigned)POWER_TURBO);\n    break;\n  }\n}\n\nFMChannel::Bandwidth\nAnytoneCodeplug::ChannelElement::bandwidth() const {\n  if (getBit(Offset::bandwidth()))\n    return FMChannel::Bandwidth::Wide;\n  return FMChannel::Bandwidth::Narrow;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  switch (bw) {\n  case FMChannel::Bandwidth::Narrow: setBit(Offset::bandwidth(), false); break;\n  case FMChannel::Bandwidth::Wide: setBit(Offset::bandwidth(), true); break;\n  }\n}\n\nAnytoneCodeplug::ChannelElement::RepeaterMode\nAnytoneCodeplug::ChannelElement::repeaterMode() const {\n  return (RepeaterMode)getUInt2(Offset::repeaterMode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRepeaterMode(RepeaterMode mode) {\n  setUInt2(Offset::repeaterMode(), (unsigned)mode);\n}\n\nAnytoneCodeplug::ChannelElement::SignalingMode\nAnytoneCodeplug::ChannelElement::rxSignalingMode() const {\n  return (SignalingMode)getUInt2(Offset::rxSignalingMode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRXSignalingMode(SignalingMode mode) {\n  setUInt2(Offset::rxSignalingMode(), (unsigned)mode);\n}\n\nSelectiveCall\nAnytoneCodeplug::ChannelElement::rxTone() const {\n  if (SignalingMode::None == rxSignalingMode())\n    return SelectiveCall();\n  else if (SignalingMode::CTCSS == rxSignalingMode())\n    return rxCTCSS();\n  else if (SignalingMode::DCS == rxSignalingMode())\n    return rxDCS();\n  return SelectiveCall();\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) {\n  if (code.isInvalid()) {\n    setRXSignalingMode(SignalingMode::None);\n  } else if (code.isCTCSS()) {\n    setRXSignalingMode(SignalingMode::CTCSS);\n    setRXCTCSS(code);\n  } else if (code.isDCS()) {\n    setRXSignalingMode(SignalingMode::DCS);\n    setRXDCS(code);\n  }\n}\n\nAnytoneCodeplug::ChannelElement::SignalingMode\nAnytoneCodeplug::ChannelElement::txSignalingMode() const {\n  return (SignalingMode)getUInt2(Offset::txSignalingMode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXSignalingMode(SignalingMode mode) {\n  setUInt2(Offset::txSignalingMode(), (unsigned)mode);\n}\n\nSelectiveCall\nAnytoneCodeplug::ChannelElement::txTone() const {\n  if (SignalingMode::None == txSignalingMode())\n    return SelectiveCall();\n  else if (SignalingMode::CTCSS == txSignalingMode())\n    return txCTCSS();\n  else if (SignalingMode::DCS == txSignalingMode())\n    return txDCS();\n  return SelectiveCall();\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) {\n  if (code.isInvalid()) {\n    setTXSignalingMode(SignalingMode::None);\n  } else if (code.isCTCSS()) {\n    setTXSignalingMode(SignalingMode::CTCSS);\n    setTXCTCSS(code);\n  } else if (code.isDCS()) {\n    setTXSignalingMode(SignalingMode::DCS);\n    setTXDCS(code);\n  }\n}\n\nbool\nAnytoneCodeplug::ChannelElement::rxTxSwapped() const {\n  return getBit(Offset::swapRXTX());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableSwapRxTx(bool enable) {\n  setBit(Offset::swapRXTX(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::rxOnly() const {\n  return getBit(Offset::rxOnly());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableRXOnly(bool enable) {\n  setBit(Offset::rxOnly(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::callConfirm() const {\n  return getBit(Offset::callConfirm());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableCallConfirm(bool enable) {\n  setBit(Offset::callConfirm(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::talkaround() const {\n  return getBit(Offset::talkaround());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableTalkaround(bool enable) {\n  setBit(Offset::talkaround(), enable);\n}\n\nbool\nAnytoneCodeplug::ChannelElement::txCTCSSIsCustom() const {\n  return CUSTOM_CTCSS_TONE == getUInt8(Offset::txCTCSS());\n}\nSelectiveCall\nAnytoneCodeplug::ChannelElement::txCTCSS() const {\n  return CTCSS::decode(getUInt8(Offset::txCTCSS()));\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXCTCSS(const SelectiveCall &tone) {\n  setUInt8(Offset::txCTCSS(), CTCSS::encode(tone));\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableTXCustomCTCSS() {\n  setUInt8(Offset::txCTCSS(), CUSTOM_CTCSS_TONE);\n}\nbool\nAnytoneCodeplug::ChannelElement::rxCTCSSIsCustom() const {\n  return CUSTOM_CTCSS_TONE == getUInt8(Offset::rxCTCSS());\n}\nSelectiveCall\nAnytoneCodeplug::ChannelElement::rxCTCSS() const {\n  return CTCSS::decode(getUInt8(Offset::rxCTCSS()));\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRXCTCSS(const SelectiveCall &tone) {\n  setUInt8(Offset::rxCTCSS(), CTCSS::encode(tone));\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableRXCustomCTCSS() {\n  setUInt8(Offset::rxCTCSS(), CUSTOM_CTCSS_TONE);\n}\n\nSelectiveCall\nAnytoneCodeplug::ChannelElement::txDCS() const {\n  uint16_t code = getUInt16_le(Offset::txDCS());\n  if (512 > code)\n    return SelectiveCall::fromBinaryDCS(code, false);\n  return SelectiveCall::fromBinaryDCS(code-512, true);\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTXDCS(const SelectiveCall &code) {\n  if (code.isDCS())\n    setUInt16_le(Offset::txDCS(), code.binCode() + (code.isInverted() ? 512 : 0));\n  else\n    setUInt16_le(Offset::txDCS(), 0);\n}\n\nSelectiveCall\nAnytoneCodeplug::ChannelElement::rxDCS() const {\n  uint16_t code = getUInt16_le(Offset::rxDCS());\n  if (512 > code)\n    return SelectiveCall::fromBinaryDCS(code, false);\n  return SelectiveCall::fromBinaryDCS(code-512, true);\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRXDCS(const SelectiveCall &code) {\n  if (code.isDCS())\n    setUInt16_le(Offset::rxDCS(), code.binCode() + (code.isInverted() ? 512 : 0));\n  else\n    setUInt16_le(Offset::rxDCS(), 0);\n}\n\ndouble\nAnytoneCodeplug::ChannelElement::customCTCSSFrequency() const {\n  return ((double) getUInt16_le(Offset::customCTCSS()))/10;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setCustomCTCSSFrequency(double hz) {\n  setUInt16_le(Offset::customCTCSS(), hz*10);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::twoToneDecodeIndex() const {\n  return getUInt16_le(Offset::twoFunctionIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTwoToneDecodeIndex(unsigned idx) {\n  setUInt16_le(Offset::twoFunctionIndex(), idx);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::contactIndex() const {\n  return getUInt32_le(Offset::contactIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setContactIndex(unsigned idx) {\n  return setUInt32_le(Offset::contactIndex(), idx);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::radioIDIndex() const {\n  return getUInt8(Offset::radioIdIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setRadioIDIndex(unsigned idx) {\n  return setUInt8(Offset::radioIdIndex(), idx);\n}\n\nAnytoneFMChannelExtension::SquelchMode\nAnytoneCodeplug::ChannelElement::squelchMode() const {\n  return (AnytoneFMChannelExtension::SquelchMode)getUInt3(Offset::squelchMode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setSquelchMode(AnytoneFMChannelExtension::SquelchMode mode) {\n  setUInt3(Offset::squelchMode(), (unsigned)mode);\n}\n\nAnytoneCodeplug::ChannelElement::Admit\nAnytoneCodeplug::ChannelElement::admit() const {\n  return (Admit)getUInt2(Offset::admitCriterion());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setAdmit(Admit admit) {\n  setUInt2(Offset::admitCriterion(), (unsigned)admit);\n}\n\nAnytoneCodeplug::ChannelElement::OptSignaling\nAnytoneCodeplug::ChannelElement::optionalSignaling() const {\n  return (OptSignaling)getUInt2(Offset::optionalSingnaling());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setOptionalSignaling(OptSignaling sig) {\n  setUInt2(Offset::optionalSingnaling(), (unsigned)sig);\n}\n\nbool\nAnytoneCodeplug::ChannelElement::hasScanListIndex() const {\n  return 0xff != scanListIndex();\n}\nunsigned\nAnytoneCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(Offset::scanListIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setScanListIndex(unsigned idx) {\n  setUInt8(Offset::scanListIndex(), idx);\n}\nvoid\nAnytoneCodeplug::ChannelElement::clearScanListIndex() {\n  setScanListIndex(0xff);\n}\n\nbool\nAnytoneCodeplug::ChannelElement::hasGroupListIndex() const {\n  return 0xff != groupListIndex();\n}\nunsigned\nAnytoneCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupListIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setGroupListIndex(unsigned idx) {\n  setUInt8(Offset::groupListIndex(), idx);\n}\nvoid\nAnytoneCodeplug::ChannelElement::clearGroupListIndex() {\n  setGroupListIndex(0xff);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::twoToneIDIndex() const {\n  return getUInt8(Offset::twoToneIDIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTwoToneIDIndex(unsigned idx) {\n  setUInt8(Offset::twoToneIDIndex(), idx);\n}\nunsigned\nAnytoneCodeplug::ChannelElement::fiveToneIDIndex() const {\n  return getUInt8(Offset::fiveToneIDIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setFiveToneIDIndex(unsigned idx) {\n  setUInt8(Offset::fiveToneIDIndex(), idx);\n}\nunsigned\nAnytoneCodeplug::ChannelElement::dtmfIDIndex() const {\n  return getUInt8(Offset::dtmfIDIndex());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setDTMFIDIndex(unsigned idx) {\n  setUInt8(Offset::dtmfIDIndex(), idx);\n}\n\nunsigned\nAnytoneCodeplug::ChannelElement::colorCode() const {\n  return getUInt8(Offset::colorCode());\n}\nvoid\nAnytoneCodeplug::ChannelElement::setColorCode(unsigned code) {\n  setUInt8(Offset::colorCode(), code);\n}\n\nDMRChannel::TimeSlot\nAnytoneCodeplug::ChannelElement::timeSlot() const {\n  if (false == getBit(Offset::timeSlot()))\n    return DMRChannel::TimeSlot::TS1;\n  return DMRChannel::TimeSlot::TS2;\n}\nvoid\nAnytoneCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  if (DMRChannel::TimeSlot::TS1 == ts)\n    setBit(Offset::timeSlot(), false);\n  else\n    setBit(Offset::timeSlot(), true);\n}\n\nbool\nAnytoneCodeplug::ChannelElement::smsConfirm() const {\n  return getBit(Offset::smsConfirm());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableSMSConfirm(bool enable) {\n  setBit(Offset::smsConfirm(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::simplexTDMA() const {\n  return getBit(Offset::simplexTDMA());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableSimplexTDMA(bool enable) {\n  setBit(Offset::simplexTDMA(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::adaptiveTDMA() const {\n  return getBit(Offset::adaptiveTDMA());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableAdaptiveTDMA(bool enable) {\n  setBit(Offset::adaptiveTDMA(), enable);\n}\nbool\nAnytoneCodeplug::ChannelElement::rxAPRS() const {\n  return getBit(Offset::rxAPRS());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableRXAPRS(bool enable) {\n  setBit(Offset::rxAPRS(), enable);\n}\n\nbool\nAnytoneCodeplug::ChannelElement::loneWorker() const {\n  return getBit(Offset::loneWorker());\n}\nvoid\nAnytoneCodeplug::ChannelElement::enableLoneWorker(bool enable) {\n  setBit(Offset::loneWorker(), enable);\n}\n\nQString\nAnytoneCodeplug::ChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::ChannelElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nChannel *\nAnytoneCodeplug::ChannelElement::toChannelObj(Context &ctx) const {\n  Q_UNUSED(ctx)\n\n  Channel *ch;\n\n  if ((Mode::Analog == mode()) || (Mode::MixedAnalog == mode())) {\n    if (Mode::MixedAnalog == mode())\n      logWarn() << \"Mixed mode channels are not supported (for now). Treat ch '\"\n                << name() <<\"' as analog channel.\";\n    FMChannel *ach = new FMChannel();\n    switch(this->admit()) {\n    case Admit::Always: ach->setAdmit(FMChannel::Admit::Always); break;\n    case Admit::Busy: ach->setAdmit(FMChannel::Admit::Free); break;\n    case Admit::Tone: ach->setAdmit(FMChannel::Admit::Tone); break;\n    default: ach->setAdmit(FMChannel::Admit::Always); break;\n    }\n    ach->setRXTone(rxTone());\n    ach->setTXTone(txTone());\n    ach->setBandwidth(bandwidth());\n    // no per channel squelch settings\n    ach->setSquelchDefault();\n    ach->extended()->enableTalkaround(talkaround());\n\n    // Create extension\n    AnytoneFMChannelExtension *ext = new AnytoneFMChannelExtension();\n    ach->setAnytoneChannelExtension(ext);\n    ext->enableRXCustomCTCSS(rxCTCSSIsCustom());\n    ext->enableTXCustomCTCSS(txCTCSSIsCustom());\n    ext->setCustomCTCSS(customCTCSSFrequency());\n    ext->setSquelchMode(squelchMode());\n\n    // done\n    ch = ach;\n  } else if ((Mode::Digital == mode()) || (Mode::MixedDigital == mode())) {\n    if (Mode::MixedDigital == mode())\n      logWarn() << \"Mixed mode channels are not supported (for now). Treat ch '\"\n                << name() <<\"' as digital channel.\";\n    DMRChannel *dch = new DMRChannel();\n    switch (this->admit()) {\n    case Admit::Always: dch->setAdmit(DMRChannel::Admit::Always); break;\n    case Admit::Free: dch->setAdmit(DMRChannel::Admit::Free); break;\n    case Admit::DifferentColorCode:dch->setAdmit(DMRChannel::Admit::ColorCode); break;\n    case Admit::SameColorCode:\n      logWarn() << \"Cannot decode admit cirit. 'same CC', use 'different CC' instead.\";\n      dch->setAdmit(DMRChannel::Admit::ColorCode);\n      break;\n    }\n    dch->setColorCode(colorCode());\n    dch->setTimeSlot(timeSlot());\n    dch->extended()->enableTalkaround(talkaround());\n    dch->extended()->enablePrivateCallConfirm(callConfirm());\n    dch->extended()->enableSMSConfirm(smsConfirm());\n    dch->extended()->enableDCDM(simplexTDMA());\n    dch->extended()->enableLoneWorker(loneWorker());\n\n    // Create extension\n    AnytoneDMRChannelExtension *ext = new AnytoneDMRChannelExtension();\n    dch->setAnytoneChannelExtension(ext);\n    ext->enableAdaptiveTDMA(adaptiveTDMA());\n    // Done\n    ch = dch;\n  } else {\n    logError() << \"Cannot create channel '\" << name()\n               << \"': Channel type \" << (unsigned)mode() << \"not supported.\";\n    return nullptr;\n  }\n\n  ch->setName(name());\n  ch->setRXFrequency(Frequency::fromHz(rxFrequency()));\n  ch->setTXFrequency(Frequency::fromHz(txFrequency()));\n  ch->setPower(power());\n  ch->setRXOnly(rxOnly());\n\n  // No per channel vox & tot setting\n  ch->setVOXDefault();\n  ch->setDefaultTimeout();\n\n  return ch;\n}\n\nbool\nAnytoneCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const {\n  if (Mode::Digital == mode()) {\n    // If channel is digital\n    DMRChannel *dc = c->as<DMRChannel>();\n    if (nullptr == dc)\n      return false;\n\n    // Check if default contact is set, in fact a valid contact index is mandatory.\n    if (! ctx.has<DMRContact>(contactIndex())) {\n      logError() << \"Cannot resolve contact index \" << contactIndex() << \" for channel '\"\n                 << c->name() << \"'.\";\n      return false;\n    }\n    dc->setContact(ctx.get<DMRContact>(contactIndex()));\n\n    // Set if RX group list is set\n    if (hasGroupListIndex() && ctx.has<RXGroupList>(groupListIndex()))\n      dc->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n\n    // Link radio ID\n    DMRRadioID *rid = ctx.get<DMRRadioID>(radioIDIndex());\n    if (rid == ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>())\n      dc->setRadioId(DefaultRadioID::get());\n    else\n      dc->setRadioId(rid);\n  } else if (Mode::Analog == mode()) {\n    // If channel is analog\n    FMChannel *ac = c->as<FMChannel>();\n    if (nullptr == ac)\n      return false;\n  }\n\n  // For both, analog and digital channels:\n\n  // If channel has scan list\n  if (hasScanListIndex() && ctx.has<ScanList>(scanListIndex()))\n    c->setScanList(ctx.get<ScanList>(scanListIndex()));\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  // Clear channel element\n  clear();\n\n  // set channel name\n  setName(c->name());\n  // set rx and tx frequencies\n  setRXFrequency(c->rxFrequency().inHz());\n  setTXFrequency(c->txFrequency().inHz());\n  // set power\n  if (c->defaultPower())\n    setPower(ctx.config()->settings()->power());\n  else\n    setPower(c->power());\n  // set tx-enable\n  enableRXOnly(c->rxOnly());\n  if (nullptr == c->scanList())\n    clearScanListIndex();\n  else\n    setScanListIndex(ctx.index(c->scanList()));\n\n  // Dispatch by channel type\n  if (c->is<FMChannel>()) {\n    const FMChannel *ac = c->as<const FMChannel>();\n    setMode(Mode::Analog);\n    // set admit criterion\n    switch (ac->admit()) {\n    case FMChannel::Admit::Always: setAdmit(Admit::Always); break;\n    case FMChannel::Admit::Free: setAdmit(Admit::Busy); break;\n    case FMChannel::Admit::Tone: setAdmit(Admit::Tone); break;\n    }\n    // squelch mode\n    setRXTone(ac->rxTone());\n    setTXTone(ac->txTone());\n    if (ac->rxTone().isValid())\n      setSquelchMode(AnytoneFMChannelExtension::SquelchMode::SubTone);\n    else\n      setSquelchMode(AnytoneFMChannelExtension::SquelchMode::Carrier);\n    // set bandwidth\n    setBandwidth(ac->bandwidth());\n    // Apply common settings\n    enableTalkaround(ac->extended()->talkaround());\n    // Apply FM settings\n\n    // Handle extension\n    if (AnytoneFMChannelExtension *ext = ac->anytoneChannelExtension()) {\n      setCustomCTCSSFrequency(ext->customCTCSS());\n      if (ext->rxCustomCTCSS())\n        enableRXCustomCTCSS();\n      if (ext->txCustomCTCSS())\n        enableTXCustomCTCSS();\n      setSquelchMode(ext->squelchMode());\n    }\n  } else if (c->is<DMRChannel>()) {\n    const DMRChannel *dc = c->as<const DMRChannel>();\n    // pack digital channel config.\n    setMode(Mode::Digital);\n    // set admit criterion\n    switch(dc->admit()) {\n    case DMRChannel::Admit::Always: setAdmit(Admit::Always); break;\n    case DMRChannel::Admit::Free: setAdmit(Admit::Free); break;\n    case DMRChannel::Admit::ColorCode: setAdmit(Admit::DifferentColorCode); break;\n    }\n    // set color code\n    setColorCode(dc->colorCode());\n    // set time-slot\n    setTimeSlot(dc->timeSlot());\n    // link transmit contact\n    if (nullptr == dc->contact())\n      setContactIndex(0);\n    else\n      setContactIndex(ctx.index(dc->contact()));\n    // link RX group list\n    if (nullptr == dc->groupList())\n      clearGroupListIndex();\n    else\n      setGroupListIndex(ctx.index(dc->groupList()));\n    // Set radio ID\n    if ((nullptr == dc->radioId()) || (DefaultRadioID::get() == dc->radioId())) {\n      if (nullptr == ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()) {\n        logWarn() << \"No default radio ID set: using index 0.\";\n        setRadioIDIndex(0);\n      } else {\n        setRadioIDIndex(ctx.index(ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()));\n      }\n    } else {\n      setRadioIDIndex(ctx.index(dc->radioId()));\n    }\n    // Apply common settings\n    enableTalkaround(dc->extended()->talkaround());\n    // Apply DMR settings\n    enableCallConfirm(dc->extended()->privateCallConfirm());\n    enableSMSConfirm(dc->extended()->smsConfirm());\n    enableSimplexTDMA(dc->extended()->dcdm());\n    enableLoneWorker(dc->extended()->loneWorker());\n    // Handle extension\n    if (AnytoneDMRChannelExtension *ext = dc->anytoneChannelExtension()) {\n      enableAdaptiveTDMA(ext->adaptiveTDMA());\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ChannelBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ChannelBitmapElement::ChannelBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ChannelBitmapElement::ChannelBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, ChannelBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ContactElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, ContactElement::size())\n{\n  // pass...\n}\n\nAnytoneCodeplug::ContactElement::~ContactElement() {\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::ContactElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nAnytoneCodeplug::ContactElement::isValid() const {\n  return !name().isEmpty();\n}\n\nDMRContact::Type\nAnytoneCodeplug::ContactElement::type() const {\n  switch (getUInt8(Offset::type())) {\n  case 0: return DMRContact::PrivateCall;\n  case 1: return DMRContact::GroupCall;\n  case 2: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nAnytoneCodeplug::ContactElement::setType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::PrivateCall: setUInt8(Offset::type(), 0); break;\n  case DMRContact::GroupCall: setUInt8(Offset::type(), 1); break;\n  case DMRContact::AllCall: setUInt8(Offset::type(), 2); break;\n  }\n}\n\nQString\nAnytoneCodeplug::ContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::ContactElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nunsigned\nAnytoneCodeplug::ContactElement::number() const {\n  return getBCD8_be(Offset::number());\n}\nvoid\nAnytoneCodeplug::ContactElement::setNumber(unsigned number) {\n  setBCD8_be(Offset::number(), number);\n}\n\nAnytoneContactExtension::AlertType\nAnytoneCodeplug::ContactElement::alertType() const {\n  switch (getUInt8(Offset::alertType())) {\n  case (uint8_t)AnytoneContactExtension::AlertType::None:\n    return AnytoneContactExtension::AlertType::None;\n  case (uint8_t)AnytoneContactExtension::AlertType::Ring:\n    return AnytoneContactExtension::AlertType::Ring;\n  case (uint8_t)AnytoneContactExtension::AlertType::Online:\n    return AnytoneContactExtension::AlertType::Online;\n  default:\n    break;\n  }\n  logWarn() << \"Unknown value \" << getUInt8(Offset::alertType())\n            << \" for alert type of AnyTone contact element. Maybe the codeplug implementation is\"\n               \" outdated. Consider reporting it at https://github.com/hmatuschek/qdmr.\";\n  return AnytoneContactExtension::AlertType::None;\n}\nvoid\nAnytoneCodeplug::ContactElement::setAlertType(AnytoneContactExtension::AlertType type) {\n  setUInt8(Offset::alertType(), (unsigned)type);\n}\n\nDMRContact *\nAnytoneCodeplug::ContactElement::toContactObj(Context &ctx) const {\n  Q_UNUSED(ctx);\n\n  // Common settings\n  DMRContact *cont = new DMRContact();\n  cont->setType(type());\n  cont->setName(name());\n  cont->setNumber(number());\n  cont->setRing(AnytoneContactExtension::AlertType::None != alertType());\n\n  // Create AnyTone specific extension\n  AnytoneContactExtension *ext = new AnytoneContactExtension();\n  cont->setAnytoneExtension(ext);\n  ext->setAlertType(alertType());\n\n  return cont;\n}\n\nbool\nAnytoneCodeplug::ContactElement::fromContactObj(const DMRContact *contact, Context &ctx) {\n  Q_UNUSED(ctx)\n\n  clear();\n\n  setType(contact->type());\n  setName(contact->name());\n  setNumber(contact->number());\n  setAlertType(contact->ring() ? AnytoneContactExtension::AlertType::Ring :\n                                 AnytoneContactExtension::AlertType::None);\n\n  if (AnytoneContactExtension *ext = contact->anytoneExtension()) {\n    setAlertType(ext->alertType());\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ContactBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ContactBitmapElement::ContactBitmapElement(uint8_t *ptr, size_t size)\n  : InvertedBitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ContactBitmapElement::ContactBitmapElement(uint8_t *ptr)\n  : InvertedBitmapElement(ptr, ContactBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DTMFContactElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr)\n  : Element(ptr, DTMFContactElement::size())\n{\n  // pass...\n}\n\nAnytoneCodeplug::DTMFContactElement::~DTMFContactElement() {\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DTMFContactElement::clear() {\n  memset(_data, 0, _size);\n}\n\nQString\nAnytoneCodeplug::DTMFContactElement::number() const {\n  QString number;\n  int n = getUInt8(Offset::numDigits());\n  for (int i=0; i<n; i++) {\n    uint8_t byte = _data[Offset::digits() + i/2];\n    if (0 == (i%2))\n      number.append(_anytone_bin_dtmf_tab[(byte>>4)&0xf]);\n    else\n      number.append(_anytone_bin_dtmf_tab[(byte>>0)&0xf]);\n  }\n  return number;\n}\nvoid\nAnytoneCodeplug::DTMFContactElement::setNumber(const QString &number) {\n  if (! validDTMFNumber(number))\n    return;\n  memset(_data+Offset::digits(), 0, Limit::digitCount()/2);\n  unsigned int n = std::min((unsigned int)number.length(), Limit::digitCount());\n  setUInt8(Offset::numDigits(), n);\n  for (unsigned int i=0; i<n; i++) {\n    if (0 == (i%2))\n      _data[Offset::digits() + i/2] |= (_anytone_bin_dtmf_tab.indexOf(number[i].toLatin1())<<4);\n    else\n      _data[Offset::digits() + i/2] |= (_anytone_bin_dtmf_tab.indexOf(number[i].toLatin1())<<0);\n  }\n}\n\nQString\nAnytoneCodeplug::DTMFContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::DTMFContactElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nDTMFContact *\nAnytoneCodeplug::DTMFContactElement::toContact() const {\n  return new DTMFContact(name(), number());\n}\n\nbool\nAnytoneCodeplug::DTMFContactElement::fromContact(const DTMFContact *contact) {\n  setNumber(contact->number());\n  setName(contact->name());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DTMFContactBytemapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DTMFContactBytemapElement::DTMFContactBytemapElement(uint8_t *ptr, size_t size)\n  : InvertedBytemapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DTMFContactBytemapElement::DTMFContactBytemapElement(uint8_t *ptr)\n  : InvertedBytemapElement(ptr, DTMFContactBytemapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::GroupListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element(ptr, GroupListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::GroupListElement::clear() {\n  memset(_data, 0x00, _size);\n  // set member indices to 0xffffffff\n  memset(_data, 0xff, Offset::betweenMembers()*Limit::members());\n}\n\nbool\nAnytoneCodeplug::GroupListElement::isValid() const {\n  return !name().isEmpty();\n}\n\nQString\nAnytoneCodeplug::GroupListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::GroupListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nbool\nAnytoneCodeplug::GroupListElement::hasMemberIndex(unsigned n) const {\n  return 0xffffffff != memberIndex(n);\n}\nunsigned\nAnytoneCodeplug::GroupListElement::memberIndex(unsigned n) const {\n  return getUInt32_le(Offset::members() + Offset::betweenMembers()*n);\n}\nvoid\nAnytoneCodeplug::GroupListElement::setMemberIndex(unsigned n, unsigned idx) {\n  setUInt32_le(Offset::members() + Offset::betweenMembers()*n, idx);\n}\nvoid\nAnytoneCodeplug::GroupListElement::clearMemberIndex(unsigned n) {\n  setMemberIndex(n, 0xffffffff);\n}\n\nRXGroupList *\nAnytoneCodeplug::GroupListElement::toGroupListObj() const {\n  return new RXGroupList(name());\n}\n\nbool\nAnytoneCodeplug::GroupListElement::linkGroupList(RXGroupList *lst, Context &ctx) const {\n  for (uint8_t i=0; i<Limit::members(); i++) {\n    // Disabled contact -> continue\n    if (! hasMemberIndex(i))\n      continue;\n    // Missing contact ignore.\n    if (! ctx.has<DMRContact>(memberIndex(i))) {\n      logWarn() << \"Cannot link contact \" << memberIndex(i) << \" to group list '\"\n                << this->name() << \"': Invalid contact index. Ignored.\";\n      continue;\n    }\n    lst->addContact(ctx.get<DMRContact>(memberIndex(i)));\n  }\n  return true;\n}\n\nbool\nAnytoneCodeplug::GroupListElement::fromGroupListObj(const RXGroupList *lst, Context &ctx) {\n  clear();\n\n  // set name of group list\n  setName(lst->name());\n\n  int j=0;\n  // set members\n  for (uint8_t i=0; i<Limit::members(); i++) {\n    // Skip non-private-call entries\n    while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) {\n      logWarn() << \"Contact '\" << lst->contact(j)->name() << \"' in group list '\" << lst->name()\n                << \"' is not a group call. Skip entry.\";\n      j++;\n    }\n\n    if (lst->count() > j) {\n      setMemberIndex(i, ctx.index(lst->contact(j))); j++;\n    } else {\n      clearMemberIndex(i);\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::GroupListBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::GroupListBitmapElement::GroupListBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::GroupListBitmapElement::GroupListBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, GroupListBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ScanListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element(ptr, ScanListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::ScanListElement::clear() {\n  memset(_data, 0, _size);\n  setPriorityChannels(PriChannel::Off);\n  clearPrimaryChannel();\n  clearSecondaryChannel();\n  setLookBackTimeA(Interval::fromSeconds(150));\n  setLookBackTimeB(Interval::fromSeconds(250));\n  setDropOutDelay(Interval::fromSeconds(290));\n  setDwellTime(Interval::fromSeconds(290));\n  setRevertChannel(RevertChannel::Selected);\n  // clear members\n  memset(_data+Offset::members(), 0xff, Offset::betweenMembers()*Limit::members());\n}\n\nAnytoneCodeplug::ScanListElement::PriChannel\nAnytoneCodeplug::ScanListElement::priorityChannels() const {\n  return (PriChannel) getUInt8(Offset::priorityChannel());\n}\nvoid\nAnytoneCodeplug::ScanListElement::setPriorityChannels(PriChannel sel) {\n  setUInt8(Offset::priorityChannel(), (unsigned)sel);\n}\n\nbool\nAnytoneCodeplug::ScanListElement::hasPrimary() const {\n  return 0xffff != getUInt16_le(Offset::primaryChannel());\n}\nbool\nAnytoneCodeplug::ScanListElement::primaryIsSelected() const {\n  return 0 == getUInt16_le(Offset::primaryChannel());\n}\nunsigned\nAnytoneCodeplug::ScanListElement::primary() const {\n  return ((unsigned)getUInt16_le(Offset::primaryChannel()))-1;\n}\nvoid\nAnytoneCodeplug::ScanListElement::setPrimary(unsigned idx) {\n  setUInt16_le(Offset::primaryChannel(), idx+1);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setPrimarySelected() {\n  setUInt16_le(Offset::primaryChannel(), 0);\n}\nvoid\nAnytoneCodeplug::ScanListElement::clearPrimaryChannel() {\n  setUInt16_le(Offset::primaryChannel(), 0xffff);\n}\n\nbool\nAnytoneCodeplug::ScanListElement::hasSecondary() const {\n  return 0xffff != getUInt16_le(Offset::secondaryChannel());\n}\nbool\nAnytoneCodeplug::ScanListElement::secondaryIsSelected() const {\n  return 0 == getUInt16_le(Offset::secondaryChannel());\n}\nunsigned\nAnytoneCodeplug::ScanListElement::secondary() const {\n  return ((unsigned)getUInt16_le(Offset::secondaryChannel()))-1;\n}\nvoid\nAnytoneCodeplug::ScanListElement::setSecondary(unsigned idx) {\n  setUInt16_le(Offset::secondaryChannel(), idx+1);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setSecondarySelected() {\n  setUInt16_le(Offset::secondaryChannel(), 0);\n}\nvoid\nAnytoneCodeplug::ScanListElement::clearSecondaryChannel() {\n  setUInt16_le(Offset::secondaryChannel(), 0xffff);\n}\n\nInterval\nAnytoneCodeplug::ScanListElement::lookBackTimeA() const {\n  return Interval::fromSeconds((unsigned)getUInt16_le(Offset::lookBackTimeA()) * 10);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setLookBackTimeA(const Interval& sec) {\n  setUInt16_le(Offset::lookBackTimeA(), sec.seconds()/10);\n}\n\nInterval AnytoneCodeplug::ScanListElement::lookBackTimeB() const {\n  return Interval::fromSeconds((unsigned)getUInt16_le(Offset::lookBackTimeB())*10);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setLookBackTimeB(const Interval &sec) {\n  setUInt16_le(Offset::lookBackTimeB(), sec.seconds()/10);\n}\n\nInterval\nAnytoneCodeplug::ScanListElement::dropOutDelay() const {\n  return Interval::fromSeconds((unsigned)getUInt16_le(Offset::dropOutDelay())*10);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setDropOutDelay(const Interval &sec) {\n  setUInt16_le(Offset::dropOutDelay(), sec.seconds()/10);\n}\n\nInterval\nAnytoneCodeplug::ScanListElement::dwellTime() const {\n  return Interval::fromSeconds((unsigned)getUInt16_le(Offset::dwellTime()) * 10);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setDwellTime(const Interval &sec) {\n  setUInt16_le(Offset::dwellTime(), sec.seconds()/10);\n}\n\nAnytoneCodeplug::ScanListElement::RevertChannel\nAnytoneCodeplug::ScanListElement::revertChannel() const {\n  return (RevertChannel)getUInt8(Offset::revertChannel());\n}\nvoid\nAnytoneCodeplug::ScanListElement::setRevertChannel(RevertChannel type) {\n  setUInt8(Offset::revertChannel(), (unsigned)type);\n}\n\nQString\nAnytoneCodeplug::ScanListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nbool\nAnytoneCodeplug::ScanListElement::hasMemberIndex(unsigned n) const {\n  return 0xffff != memberIndex(n);\n}\nunsigned\nAnytoneCodeplug::ScanListElement::memberIndex(unsigned n) const {\n  return getUInt16_le(Offset::members() + Offset::betweenMembers()*n);\n}\nvoid\nAnytoneCodeplug::ScanListElement::setMemberIndex(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::members() + Offset::betweenMembers()*n, idx);\n}\nvoid\nAnytoneCodeplug::ScanListElement::clearMemberIndex(unsigned n) {\n  setMemberIndex(n, 0xffff);\n}\n\nScanList *\nAnytoneCodeplug::ScanListElement::toScanListObj() const {\n  return new ScanList(name());\n}\n\nbool\nAnytoneCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx) const {\n  if (((PriChannel::Both == priorityChannels()) || (PriChannel::Primary==priorityChannels())) && hasPrimary()) {\n    if (primaryIsSelected())\n      lst->setPrimaryChannel(SelectedChannel::get());\n    else if (ctx.has<Channel>(primary()))\n      lst->setPrimaryChannel(ctx.get<Channel>(primary()));\n  }\n\n  if (((PriChannel::Both == priorityChannels()) || (PriChannel::Secondary==priorityChannels())) && hasSecondary()) {\n    if (secondaryIsSelected())\n      lst->setSecondaryChannel(SelectedChannel::get());\n    else if (ctx.has<Channel>(secondary()))\n      lst->setSecondaryChannel(ctx.get<Channel>(secondary()));\n  }\n\n  for (uint16_t i=0; i<Limit::members(); i++) {\n    if (! hasMemberIndex(i))\n      continue;\n    if (! ctx.has<Channel>(memberIndex(i))) {\n      logError() << \"Cannot link scanlist '\" << name() << \"', channel index \"\n                 << memberIndex(i) << \" unknown.\";\n      continue;\n    }\n    lst->addChannel(ctx.get<Channel>(memberIndex(i)));\n  }\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::ScanListElement::fromScanListObj(ScanList *lst, Context &ctx) {\n  clear();\n\n  setName(lst->name());\n\n  if (lst->primaryChannel() && lst->secondaryChannel()) {\n    setPriorityChannels(PriChannel::Both);\n  } else if (lst->primaryChannel()) {\n    setPriorityChannels(PriChannel::Primary);\n  } else if (lst->secondaryChannel()) {\n    setPriorityChannels(PriChannel::Secondary);\n  }\n\n  if (lst->primaryChannel()) {\n    if (SelectedChannel::get() == lst->primaryChannel())\n      setPrimarySelected();\n    else\n      setPrimary(ctx.index(lst->primaryChannel()));\n  }\n\n  if (lst->secondaryChannel()) {\n    if (SelectedChannel::get() == lst->secondaryChannel())\n      setSecondarySelected();\n    else\n      setSecondary(ctx.index(lst->secondaryChannel()));\n  }\n\n  for (int i=0; i<std::min((int)Limit::members(), lst->count()); i++) {\n    if (SelectedChannel::get() == lst->channel(i))\n      continue;\n    setMemberIndex(i, ctx.index(lst->channel(i)));\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ScanListBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ScanListBitmapElement::ScanListBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ScanListBitmapElement::ScanListBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, ScanListBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::RadioIDElement\n * ********************************************************************************************* */\nAnytoneCodeplug::RadioIDElement::RadioIDElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::RadioIDElement::RadioIDElement(uint8_t *ptr)\n  : Element(ptr, RadioIDElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::RadioIDElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nunsigned AnytoneCodeplug::RadioIDElement::number() const {\n  return getBCD8_be(Offset::number());\n}\nvoid\nAnytoneCodeplug::RadioIDElement::setNumber(unsigned number) {\n  setBCD8_be(Offset::number(), number);\n}\n\nQString\nAnytoneCodeplug::RadioIDElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::RadioIDElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nDMRRadioID *\nAnytoneCodeplug::RadioIDElement::toRadioID() const {\n  return new DMRRadioID(name(), number());\n}\nbool\nAnytoneCodeplug::RadioIDElement::fromRadioID(DMRRadioID *id) {\n  setName(id->name());\n  setNumber(id->number());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::RadioIDBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::RadioIDBitmapElement::RadioIDBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n   // pass...\n}\n\nAnytoneCodeplug::RadioIDBitmapElement::RadioIDBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, RadioIDBitmapElement::size())\n{\n  // pass...\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::PrimaryRadioIdElement\n * ********************************************************************************************* */\nAnytoneCodeplug::PrimaryRadioIdElement::PrimaryRadioIdElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::PrimaryRadioIdElement::clear() {\n  memset(_data, 0x00, size());\n}\n\nbool\nAnytoneCodeplug::PrimaryRadioIdElement::isValid() const {\n  return 0 != number() && ! name().isEmpty();\n}\n\nunsigned int\nAnytoneCodeplug::PrimaryRadioIdElement::number() const {\n  return getBCD8_be(Offset::id());\n}\n\nvoid\nAnytoneCodeplug::PrimaryRadioIdElement::setNumber(unsigned number) {\n  setBCD8_be(Offset::id(), number);\n}\n\n\nbool\nAnytoneCodeplug::PrimaryRadioIdElement::enabled() const {\n  return 0x01 == getUInt8(Offset::enabled());\n}\n\nvoid\nAnytoneCodeplug::PrimaryRadioIdElement::enable(bool enabled) {\n  setUInt8(Offset::enabled(), enabled ? 0x01 : 0x00);\n}\n\n\nQString\nAnytoneCodeplug::PrimaryRadioIdElement::name() const {\n  return readASCII(Offset::name(), Limit::name(), 0x00);\n}\n\nvoid\nAnytoneCodeplug::PrimaryRadioIdElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::name(), 0x00);\n}\n\nbool\nAnytoneCodeplug::PrimaryRadioIdElement::encode(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  bool enabled = this->enabled();\n  if (! flags.updateCodeplug())\n    enabled = false;\n\n  clear();\n\n  if (nullptr == ctx.config()->settings()->defaultId())\n    return true;\n\n  auto id = ctx.config()->settings()->defaultId();\n  setName(id->name());\n  setNumber(id->number());\n  enable(enabled);\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::PrimaryRadioIdElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  // pass...\n  return true;\n}\n\nbool\nAnytoneCodeplug::PrimaryRadioIdElement::link(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (! isValid()) return true;\n\n  // Check if ID is already present as a DMR radio id\n  auto id = ctx.config()->radioIDs()->find(number());\n  // If not, create one\n  if (nullptr == id) {\n    id = new DMRRadioID(name(), number());\n    ctx.config()->radioIDs()->add(id, 0);\n  }\n  // ... and set as default.\n  ctx.config()->settings()->setDefaultId(id);\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::GeneralSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::GeneralSettingsElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::displayFrequency() const {\n  return 0!=getUInt8(Offset::displayMode());\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::enableDisplayFrequency(bool enable) {\n  setUInt8(Offset::displayMode(), (enable ? 0x01 : 0x00));\n}\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::autoKeyLock() const {\n  return 0!=getUInt8(Offset::autoKeyLock());\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::enableAutoKeyLock(bool enable) {\n  setUInt8(Offset::autoKeyLock(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nAnytoneCodeplug::GeneralSettingsElement::autoShutdownDelay() const {\n  switch ((AutoShutdown) getUInt8(Offset::autoShutDown())) {\n  case AutoShutdown::Off:         return Interval::fromMinutes(0);\n  case AutoShutdown::After10min:  return Interval::fromMinutes(10);\n  case AutoShutdown::After30min:  return Interval::fromMinutes(30);\n  case AutoShutdown::After60min:  return Interval::fromMinutes(60);\n  case AutoShutdown::After120min: return Interval::fromMinutes(120);\n  }\n  return Interval();\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::setAutoShutdownDelay(Interval intv) {\n  if (0 == intv.minutes()) {\n    setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::Off);\n  } else if (intv.minutes() <= 10) {\n    setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After10min);\n  } else if (intv.minutes() <= 30) {\n    setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After30min);\n  } else if (intv.minutes() <= 60) {\n    setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After60min);\n  } else {\n    setUInt8(Offset::autoShutDown(), (unsigned)AutoShutdown::After120min);\n  }\n}\n\n\nBootSettings::BootDisplay\nAnytoneCodeplug::GeneralSettingsElement::bootDisplay() const {\n  switch ((BootDisplay) getUInt8(Offset::bootDisplay())) {\n    case BootDisplay::Default: return BootSettings::BootDisplay::Logo;\n    case BootDisplay::CustomText: return BootSettings::BootDisplay::Text;\n    case BootDisplay::CustomImage: return BootSettings::BootDisplay::Image;\n  }\n  return BootSettings::BootDisplay::Logo;\n}\n\nvoid\nAnytoneCodeplug::GeneralSettingsElement::setBootDisplay(BootSettings::BootDisplay mode) {\n  switch (mode) {\n  case BootSettings::BootDisplay::Logo: setUInt8(Offset::bootDisplay(), (unsigned)BootDisplay::Default); break;\n  case BootSettings::BootDisplay::Text: setUInt8(Offset::bootDisplay(), (unsigned)BootDisplay::CustomText); break;\n  case BootSettings::BootDisplay::Image: setUInt8(Offset::bootDisplay(), (unsigned)BootDisplay::CustomImage); break;\n  }\n}\n\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::bootPassword() const {\n  return getUInt8(Offset::bootPassword());\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::enableBootPassword(bool enable) {\n  setUInt8(Offset::bootPassword(), (enable ? 0x01 : 0x00));\n}\n\nLevel\nAnytoneCodeplug::GeneralSettingsElement::squelchLevelA() const {\n  return Level::fromValue(getUInt8(Offset::squelchLevelA()), {1,5});\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::setSquelchLevelA(Level level) {\n  setUInt8(Offset::squelchLevelA(), level.mapTo({1,5}));\n}\nLevel\nAnytoneCodeplug::GeneralSettingsElement::squelchLevelB() const {\n  return Level::fromValue(getUInt8(Offset::squelchLevelB()), {1,5});\n}\nvoid\nAnytoneCodeplug::GeneralSettingsElement::setSquelchLevelB(Level level) {\n  setUInt8(Offset::squelchLevelB(), level.mapTo({1, 5}));\n}\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // If auto-enable GPS is enabled\n  if (flags.autoEnableGPS()) {\n    // Check if GPS is required -> enable\n    if (ctx.config()->requiresGPS()) {\n      enableGPS(true);\n      // Set time zone based on system time zone.\n      setGPSTimeZone(QTimeZone::systemTimeZone());\n      enableGetGPSPosition(false);\n    } else {\n      enableGPS(false);\n    }\n  }\n\n  // Encode boot settings\n  setBootDisplay(ctx.config()->settings()->boot()->bootDisplay());\n  enableBootPassword(ctx.config()->settings()->boot()->bootPasswordEnabled());\n  enableDefaultChannel(\n        ctx.config()->settings()->boot()->defaultChannelEnabled() &&\n        (! ctx.config()->settings()->boot()->zoneA()->isNull()) &&\n        (! ctx.config()->settings()->boot()->zoneB()->isNull()));\n\n  if (defaultChannel()) {\n    setDefaultZoneIndexA(ctx.index(ctx.config()->settings()->boot()->zoneA()->as<Zone>()));\n    if (ctx.config()->settings()->boot()->channelA()->isNull() ||\n        (! ctx.config()->settings()->boot()->zoneA()->as<Zone>()->A()->has(\n           ctx.config()->settings()->boot()->channelA()->as<Channel>())))\n      setDefaultChannelAToVFO();\n    else\n      setDefaultChannelAIndex(ctx.config()->settings()->boot()->zoneA()->as<Zone>()->A()->indexOf(\n                                ctx.config()->settings()->boot()->channelA()->as<Channel>()));\n\n    setDefaultZoneIndexB(ctx.index(ctx.config()->settings()->boot()->zoneB()->as<Zone>()));\n    if (ctx.config()->settings()->boot()->channelB()->isNull() ||\n        (! ctx.config()->settings()->boot()->zoneB()->as<Zone>()->A()->has(\n           ctx.config()->settings()->boot()->channelB()->as<Channel>())))\n      setDefaultChannelBToVFO();\n    else\n      setDefaultChannelBIndex(ctx.config()->settings()->boot()->zoneB()->as<Zone>()->A()->indexOf(\n                                ctx.config()->settings()->boot()->channelB()->as<Channel>()));\n  }\n\n  // Handle audio settings:\n  setSquelchLevelA(ctx.config()->settings()->audio()->squelch());\n  setSquelchLevelB(ctx.config()->settings()->audio()->squelch());\n  // Set microphone gain\n  // For the 868UV, this setting sets both, FM and DMR mic gain.\n  // For all other devices, there is an additional FM mic gain setting.\n  setDMRMicGain(ctx.config()->settings()->audio()->micGain());\n  setMaxSpeakerVolume(ctx.config()->settings()->audio()->maxSpeakerVolume());\n\n  // Handle tone settings:\n  enableKeyTone(ctx.config()->settings()->tone()->keyToneEnabled());\n  enableSMSAlert(ctx.config()->settings()->tone()->smsToneEnabled());\n  enableCallAlert(ctx.config()->settings()->tone()->ringtoneEnabled());\n  enableStartupTone(ctx.config()->settings()->tone()->bootToneEnabled());\n  enableDMRTalkPermit(ctx.config()->settings()->tone()->talkPermit().testFlag(Channel::Type::DMR));\n  enableFMTalkPermit(ctx.config()->settings()->tone()->talkPermit().testFlag(Channel::Type::FM));\n  setCallToneMelody(*ctx.config()->settings()->tone()->callStartMelody());\n  setIdleToneMelody(*ctx.config()->settings()->tone()->channelIdleMelody());\n  enableDMRResetTone(ctx.config()->settings()->tone()->callResetEnabled());\n  setResetToneMelody(*ctx.config()->settings()->tone()->callResetMelody());\n\n  enableGPSUnitsImperial(GNSSSettings::Units::Archaic == ctx.config()->settings()->gnss()->units());\n\n  // Handle extensions\n  if (AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension()) {\n    setAutoShutdownDelay(ext->powerSaveSettings()->autoShutdown());\n    setVFOScanType(ext->vfoScanType());\n    enableVFOModeA(AnytoneSettingsExtension::VFOMode::VFO == ext->modeA());\n    enableVFOModeB(AnytoneSettingsExtension::VFOMode::VFO == ext->modeB());\n    if ((AnytoneSettingsExtension::VFOMode::VFO == ext->modeA()) || ext->zoneA()->isNull())\n      setMemoryZoneA(0);\n    else\n      setMemoryZoneA(ctx.index(ext->zoneA()->as<Zone>()));\n    if ((AnytoneSettingsExtension::VFOMode::VFO == ext->modeB()) || ext->zoneB()->isNull())\n      setMemoryZoneB(0);\n    else\n      setMemoryZoneB(ctx.index(ext->zoneB()->as<Zone>()));\n    enableActiveChannelB(AnytoneSettingsExtension::VFO::B == ext->selectedVFO());\n    enableSubChannel(ext->subChannelEnabled());\n    setMinVFOScanFrequencyUHF(ext->minVFOScanFrequencyUHF());\n    setMaxVFOScanFrequencyUHF(ext->maxVFOScanFrequencyUHF());\n    setMinVFOScanFrequencyVHF(ext->minVFOScanFrequencyVHF());\n    setMaxVFOScanFrequencyVHF(ext->maxVFOScanFrequencyVHF());\n\n    // Encode key settings\n    setFuncKeyAShort(ext->keySettings()->funcKeyAShort());\n    setFuncKeyALong(ext->keySettings()->funcKeyALong());\n    setFuncKeyBShort(ext->keySettings()->funcKeyBShort());\n    setFuncKeyBLong(ext->keySettings()->funcKeyBLong());\n    setFuncKeyCShort(ext->keySettings()->funcKeyCShort());\n    setFuncKeyCLong(ext->keySettings()->funcKeyCLong());\n    setFuncKey1Short(ext->keySettings()->funcKey1Short());\n    setFuncKey1Long(ext->keySettings()->funcKey1Long());\n    setFuncKey2Short(ext->keySettings()->funcKey2Short());\n    setFuncKey2Long(ext->keySettings()->funcKey2Long());\n    setLongPressDuration(ext->keySettings()->longPressDuration());\n    enableAutoKeyLock(ext->keySettings()->autoKeyLockEnabled());\n\n    // Encode display settings\n    enableDisplayFrequency(ext->displaySettings()->displayFrequencyEnabled());\n    setBrightness(ext->displaySettings()->brightness());\n    enableCallEndPrompt(ext->displaySettings()->callEndPromptEnabled());\n    setLastCallerDisplayMode(ext->displaySettings()->lastCallerDisplay());\n    enableDisplayClock(ext->displaySettings()->showClockEnabled());\n    enableDisplayCall(ext->displaySettings()->showCallEnabled());\n    setCallDisplayColor(ext->displaySettings()->callColor());\n    enableVolumeChangePrompt(ext->displaySettings()->volumeChangePromptEnabled());\n\n    // Encode audio settings\n    enableRecording(ext->audioSettings()->recordingEnabled());\n    enableEnhancedAudio(ext->audioSettings()->enhanceAudioEnabled());\n\n    // Encode menu settings\n    setMenuExitTime(ext->menuSettings()->duration());\n\n    // Encode auto-repeater settings\n    setAutoRepeaterDirectionA(ext->autoRepeaterSettings()->directionA());\n    setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB());\n    if (ext->autoRepeaterSettings()->vhfRef()->isNull())\n      clearAutoRepeaterOffsetFrequencyIndexVHF();\n    else\n      setAutoRepeaterOffsetFrequenyIndexVHF(\n            ctx.index(ext->autoRepeaterSettings()->vhfRef()->as<AnytoneAutoRepeaterOffset>()));\n    if (ext->autoRepeaterSettings()->uhfRef()->isNull())\n      clearAutoRepeaterOffsetFrequencyIndexUHF();\n    else\n      setAutoRepeaterOffsetFrequenyIndexUHF(\n            ctx.index(ext->autoRepeaterSettings()->uhfRef()->as<AnytoneAutoRepeaterOffset>()));\n    setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin());\n    setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax());\n    setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin());\n    setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax());\n\n    // Encode GPS Settings\n    setGPSTimeZone(ext->gpsSettings()->timeZone());\n\n    // Encode other settings\n    enableKeepLastCaller(ext->keepLastCallerEnabled());\n  } else if (! flags.updateCodeplug()) {\n    clearAutoRepeaterOffsetFrequencyIndexVHF();\n    clearAutoRepeaterOffsetFrequencyIndexUHF();\n  }\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // get microphone gain\n  ctx.config()->settings()->audio()->setMicGain(dmrMicGain());\n  ctx.config()->settings()->audio()->setMaxSpeakerVolume(this->maxSpeakerVolume());\n\n  // D868UV does not support speech synthesis?\n  ctx.config()->settings()->audio()->enableSpeechSynthesis(false);\n  ctx.config()->settings()->audio()->setSquelch(std::max(squelchLevelA(), squelchLevelB()));\n\n  // Handle tone settings\n  ctx.config()->settings()->tone()->setKeyToneVolume(\n    this->keyToneEnabled() ? Level::fromValue(5) : Level::null());\n  ctx.config()->settings()->tone()->enableSMSTone(smsAlert());\n  ctx.config()->settings()->tone()->enableRingtone(callAlert());\n  ctx.config()->settings()->tone()->enableBootTone(startupTone());\n  ctx.config()->settings()->tone()->setTalkPermit(\n    (dmrTalkPermit() ? Channel::Type::DMR : Channel::Type::None) |\n    (fmTalkPermit() ? Channel::Type::FM : Channel::Type::None) );\n  callToneMelody(*ctx.config()->settings()->tone()->callStartMelody());\n  idleToneMelody(*ctx.config()->settings()->tone()->channelIdleMelody());\n  ctx.config()->settings()->tone()->enableCallReset(dmrResetTone());\n  resetToneMelody(*ctx.config()->settings()->tone()->callResetMelody());\n\n  // Store boot settings\n  ctx.config()->settings()->boot()->setBootDisplay(bootDisplay());\n  ctx.config()->settings()->boot()->enableBootPassword(bootPassword());\n  ctx.config()->settings()->boot()->enableDefaultChannel(this->defaultChannel());\n\n  ctx.config()->settings()->gnss()->setUnits(\n        this->gpsUnitsImperial() ? GNSSSettings::Units::Archaic :\n                                   GNSSSettings::Units::Metric);\n\n  // Set extension\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension())\n    ext = ctx.config()->settings()->anytoneExtension();\n  else\n    ctx.config()->settings()->setAnytoneExtension(ext = new AnytoneSettingsExtension());\n\n  ext->powerSaveSettings()->setAutoShutdown(autoShutdownDelay());\n  ext->setVFOScanType(vfoScanType());\n  ext->setModeA(vfoModeA() ? AnytoneSettingsExtension::VFOMode::VFO\n                           : AnytoneSettingsExtension::VFOMode::Memory);\n  ext->setModeB(vfoModeB() ? AnytoneSettingsExtension::VFOMode::VFO\n                           : AnytoneSettingsExtension::VFOMode::Memory);\n  if ((! vfoModeA()) && ctx.has<Zone>(memoryZoneA()))\n    ext->zoneA()->set(ctx.get<Zone>(memoryZoneA()));\n  if ((! vfoModeB()) && ctx.has<Zone>(memoryZoneB()))\n    ext->zoneB()->set(ctx.get<Zone>(memoryZoneB()));\n  ext->setSelectedVFO(activeChannelB() ? AnytoneSettingsExtension::VFO::B\n                                       : AnytoneSettingsExtension::VFO::A);\n  ext->enableSubChannel(subChannel());\n  ext->setMinVFOScanFrequencyUHF(this->minVFOScanFrequencyUHF());\n  ext->setMaxVFOScanFrequencyUHF(this->maxVFOScanFrequencyUHF());\n  ext->setMinVFOScanFrequencyVHF(this->minVFOScanFrequencyVHF());\n  ext->setMaxVFOScanFrequencyVHF(this->maxVFOScanFrequencyVHF());\n\n  // Store key settings\n  ext->keySettings()->setFuncKeyAShort(funcKeyAShort());\n  ext->keySettings()->setFuncKeyALong(funcKeyALong());\n  ext->keySettings()->setFuncKeyBShort(funcKeyBShort());\n  ext->keySettings()->setFuncKeyBLong(funcKeyBLong());\n  ext->keySettings()->setFuncKeyCShort(funcKeyCShort());\n  ext->keySettings()->setFuncKeyCLong(funcKeyCLong());\n  ext->keySettings()->setFuncKey1Short(funcKey1Short());\n  ext->keySettings()->setFuncKey1Long(funcKey1Long());\n  ext->keySettings()->setFuncKey2Short(funcKey2Short());\n  ext->keySettings()->setFuncKey2Long(funcKey2Long());\n  ext->keySettings()->setLongPressDuration(longPressDuration());\n  ext->keySettings()->enableAutoKeyLock(autoKeyLock());\n\n  // Store display settings\n  ext->displaySettings()->enableDisplayFrequency(displayFrequency());\n  ext->displaySettings()->setBrightness(brightness());\n  ext->displaySettings()->enableVolumeChangePrompt(this->volumeChangePrompt());\n  ext->displaySettings()->enableCallEndPrompt(this->callEndPrompt());\n  ext->displaySettings()->setLastCallerDisplay(this->lastCallerDisplayMode());\n  ext->displaySettings()->enableShowClock(displayClock());\n  ext->displaySettings()->enableShowCall(displayCall());\n  ext->displaySettings()->setCallColor(this->callDisplayColor());\n\n  // Menu settings\n  ext->menuSettings()->setDuration(this->menuExitTime());\n\n  // Store audio settings\n  ext->audioSettings()->enableRecording(recording());\n  ext->audioSettings()->enableEnhanceAudio(this->enhanceAudio());\n\n  // Store auto-repeater settings\n  ext->autoRepeaterSettings()->setDirectionA(this->autoRepeaterDirectionA());\n  ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB());\n  ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF());\n  ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF());\n  ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF());\n  ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF());\n\n  // Store GPS settings\n  ext->gpsSettings()->setTimeZone(gpsTimeZone());\n\n  // Other settings\n  ext->enableKeepLastCaller(this->keepLastCaller());\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) {\n  // Link boot settings\n  if (this->defaultChannel()) {\n    if (! ctx.has<Zone>(this->defaultZoneIndexA())) {\n      errMsg(err) << \"Cannot link default zone A. Zone index \" << this->defaultZoneIndexA()\n                  << \" not defined.\";\n      return false;\n    }\n\n    Zone *zoneA = ctx.get<Zone>(this->defaultZoneIndexA());\n    ctx.config()->settings()->boot()->zoneA()->set(zoneA);\n    if (this->defaultChannelAIsVFO()) {\n      // pass...\n    } else if (this->defaultChannelAIndex() >= (unsigned int)zoneA->A()->count()) {\n      errMsg(err) << \"Cannot link default channel A. Index \" << this->defaultChannelAIndex()\n                  << \" not defined.\";\n      return false;\n    } else {\n      ctx.config()->settings()->boot()->channelA()->set(\n            zoneA->A()->get(this->defaultChannelAIndex())->as<Channel>());\n    }\n\n    if (! ctx.has<Zone>(this->defaultZoneIndexB())) {\n      errMsg(err) << \"Cannot link default zone B. Zone index \" << this->defaultZoneIndexB()\n                  << \" not defined.\";\n      return false;\n    }\n    Zone *zoneB = ctx.get<Zone>(this->defaultZoneIndexB());\n    ctx.config()->settings()->boot()->zoneB()->set(zoneB);\n    if (this->defaultChannelBIsVFO()) {\n      // pass...\n    } else if (this->defaultChannelBIndex() >= (unsigned int)zoneB->A()->count()) {\n      errMsg(err) << \"Cannot link default channel B. Index \" << this->defaultChannelBIndex()\n                  << \" not defined.\";\n      return false;\n    } else {\n      ctx.config()->settings()->boot()->channelB()->set(\n            zoneB->A()->get(this->defaultChannelBIndex())->as<Channel>());\n    }\n  }\n\n  if (! settings->anytoneExtension())\n    return false;\n  AnytoneSettingsExtension *ext = settings->anytoneExtension();\n\n  // Link repeater offsets\n  if (this->hasAutoRepeaterOffsetFrequencyIndexVHF()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexVHF())) {\n      errMsg(err) << \"Cannot link auto-repeater offset for VHF, index \"\n                  << this->autoRepeaterOffsetFrequencyIndexVHF() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->vhfRef()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexVHF()));\n  }\n\n  if (this->hasAutoRepeaterOffsetFrequencyIndexUHF()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexUHF())) {\n      errMsg(err) << \"Cannot link auto-repeater offset for UHF, index \"\n                  << this->autoRepeaterOffsetFrequencyIndexUHF() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->uhfRef()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexUHF()));\n  }\n\n  // Link auto-repeater\n  if (hasAutoRepeaterOffsetFrequencyIndexVHF()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexVHF())) {\n      errMsg(err) << \"Cannot link auto-repeater offset frequency for VHF, index \"\n                  << this->autoRepeaterOffsetFrequencyIndexVHF() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->vhfRef()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexVHF()));\n  }\n  if (hasAutoRepeaterOffsetFrequencyIndexUHF()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexUHF())) {\n      errMsg(err) << \"Cannot link auto-repeater offset frequency for UHF, index \"\n                  << this->autoRepeaterOffsetFrequencyIndexUHF() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->uhfRef()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(this->autoRepeaterOffsetFrequencyIndexUHF()));\n  }\n\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ExtendedSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nbool\nAnytoneCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (! flags.updateCodeplug())\n    this->clear();\n\n  if (nullptr == ctx.config()->settings()->anytoneExtension()) {\n    // If there is no extension, done\n    return true;\n  }\n\n  // Get extension\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n\n  // Encode display settings\n  setChannelBNameColor(ext->displaySettings()->channelBNameColor());\n  setZoneANameColor(ext->displaySettings()->zoneNameColor());\n  setZoneBNameColor(ext->displaySettings()->zoneBNameColor());\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // Get or add extension if not present\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Store display settings\n  ext->displaySettings()->setChannelBNameColor(channelBNameColor());\n  ext->displaySettings()->setZoneNameColor(zoneANameColor());\n  ext->displaySettings()->setZoneBNameColor(zoneBNameColor());\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) {\n  // Get or add extension if not present\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    errMsg(err) << \"Cannot link config extension: not set.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ZoneChannelListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ZoneChannelListElement::ZoneChannelListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pas...\n}\n\nAnytoneCodeplug::ZoneChannelListElement::ZoneChannelListElement(uint8_t *ptr)\n  : Element(ptr, ZoneChannelListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::ZoneChannelListElement::clear() {\n  memset(_data, 0x00, _size);\n  memset(_data + Offset::channelsA(), 0xff, Offset::betweenChannels()*Limit::zones());\n  memset(_data + Offset::channelsB(), 0xff, Offset::betweenChannels()*Limit::zones());\n}\n\nbool\nAnytoneCodeplug::ZoneChannelListElement::hasChannelA(unsigned n) const {\n  return 0xffff != channelIndexA(n);\n}\nunsigned\nAnytoneCodeplug::ZoneChannelListElement::channelIndexA(unsigned n) const {\n  return getUInt16_le(Offset::channelsA() + Offset::betweenChannels()*n);\n}\nvoid\nAnytoneCodeplug::ZoneChannelListElement::setChannelIndexA(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::channelsA() + Offset::betweenChannels()*n, idx);\n}\nvoid\nAnytoneCodeplug::ZoneChannelListElement::clearChannelIndexA(unsigned n) {\n  setChannelIndexA(n, 0xffff);\n}\n\nbool\nAnytoneCodeplug::ZoneChannelListElement::hasChannelB(unsigned n) const {\n  return 0xffff != channelIndexB(n);\n}\nunsigned\nAnytoneCodeplug::ZoneChannelListElement::channelIndexB(unsigned n) const {\n  return getUInt16_le(Offset::channelsB() + Offset::betweenChannels()*n);\n}\nvoid\nAnytoneCodeplug::ZoneChannelListElement::setChannelIndexB(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::channelsB() + Offset::betweenChannels()*n, idx);\n}\nvoid\nAnytoneCodeplug::ZoneChannelListElement::clearChannelIndexB(unsigned n) {\n  setChannelIndexB(n, 0xffff);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ZoneBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ZoneBitmapElement::ZoneBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ZoneBitmapElement::ZoneBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, ZoneBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::BootSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr)\n  : Element(ptr, BootSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::BootSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nQString\nAnytoneCodeplug::BootSettingsElement::introLine1() const {\n  return readASCII(Offset::introLine1(), Limit::introLineLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::BootSettingsElement::setIntroLine1(const QString &txt) {\n  writeASCII(Offset::introLine1(), txt, Limit::introLineLength(), 0x00);\n}\nQString\nAnytoneCodeplug::BootSettingsElement::introLine2() const {\n  return readASCII(Offset::introLine2(), Limit::introLineLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::BootSettingsElement::setIntroLine2(const QString &txt) {\n  writeASCII(Offset::introLine2(), txt, Limit::introLineLength(), 0x00);\n}\n\nQString\nAnytoneCodeplug::BootSettingsElement::password() const {\n  return readASCII(Offset::password(), Limit::passwordLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::BootSettingsElement::setPassword(const QString &txt) {\n  QRegularExpression pattern(\"[0-9]{0,8}\");\n  if (pattern.match(txt).hasMatch())\n    writeASCII(Offset::password(), txt, Limit::passwordLength(), 0x00);\n}\n\nbool\nAnytoneCodeplug::BootSettingsElement::fromConfig(const Flags &flags, Context &ctx) {\n  Q_UNUSED(flags)\n  setIntroLine1(ctx.config()->settings()->boot()->message1());\n  setIntroLine2(ctx.config()->settings()->boot()->message2());\n\n  if (ctx.config()->settings()->boot()->bootPasswordEnabled())\n    setPassword(ctx.config()->settings()->boot()->bootPassword());\n  else\n    setPassword(\"\");\n\n  return true;\n}\n\nbool\nAnytoneCodeplug::BootSettingsElement::updateConfig(Context &ctx) {\n  ctx.config()->settings()->boot()->setMessage1(introLine1());\n  ctx.config()->settings()->boot()->setMessage2(introLine2());\n\n  ctx.config()->settings()->boot()->setBootPassword(password());\n  ctx.config()->settings()->boot()->enableBootPassword(! password().isEmpty());\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DMRAPRSSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DMRAPRSSettingsElement::DMRAPRSSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DMRAPRSSettingsElement::DMRAPRSSettingsElement(uint8_t *ptr)\n  : Element(ptr, DMRAPRSSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nInterval\nAnytoneCodeplug::DMRAPRSSettingsElement::manualInterval() const {\n  return Interval::fromSeconds(getUInt8(Offset::manualInterval()));\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setManualInterval(const Interval &sec) {\n  setUInt8(Offset::manualInterval(), sec.seconds());\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::automatic() const {\n  return 0!=getUInt8(Offset::automaticInterval());\n}\nInterval\nAnytoneCodeplug::DMRAPRSSettingsElement::automaticInterval() const {\n  return Interval::fromSeconds(45 + 15*((unsigned)getUInt8(Offset::automaticInterval())));\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setAutomaticInterval(const Interval &sec) {\n  unsigned int s = sec.seconds();\n  if (s < 60) s = 60;\n  setUInt8(Offset::automaticInterval(), (s-45)/15);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::disableAutomatic() {\n  setUInt8(Offset::automaticInterval(), 0x00);\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::fixedLocation() const {\n  return getUInt8(Offset::fixedLocation());\n}\nQGeoCoordinate\nAnytoneCodeplug::DMRAPRSSettingsElement::location() const {\n  double latitude  = getUInt8(Offset::latitudeDeg()) + double(getUInt8(Offset::latitudeMin()))/60\n      + double(getUInt8(Offset::latitudeSec()))/3600;\n  if (getUInt8(Offset::latitudeHemi())) latitude *= -1;\n  double longitude = getUInt8(Offset::longitudeDeg()) + double(getUInt8(Offset::longitudeMin()))/60\n      + double(getUInt8(Offset::longitudeSec()))/3600;\n  if (getUInt8(Offset::longitudeHemi())) longitude *= -1;\n  return QGeoCoordinate(latitude, longitude);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setLocation(const QGeoCoordinate &pos) {\n  double latitude = pos.latitude();\n  bool south = (0 > latitude); latitude = std::abs(latitude);\n  unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60;\n  unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60;\n  unsigned lat_sec = int(latitude);\n  double longitude = pos.longitude();\n  bool west = (0 > longitude); longitude = std::abs(longitude);\n  unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60;\n  unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60;\n  unsigned lon_sec = int(longitude);\n  setUInt8(Offset::latitudeDeg(), lat_deg);  setUInt8(Offset::latitudeMin(), lat_min);\n  setUInt8(Offset::latitudeSec(), lat_sec); setUInt8(Offset::latitudeHemi(), (south ? 0x01 : 0x00));\n  setUInt8(Offset::longitudeDeg(), lon_deg); setUInt8(Offset::longitudeMin(), lon_min);\n  setUInt8(Offset::longitudeSec(), lon_sec); setUInt8(Offset::longitudeHemi(), (west ? 0x01 : 0x00));\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::enableFixedLocation(bool enable) {\n  setUInt8(Offset::fixedLocation(), (enable ? 0x01 : 0x00));\n}\n\nChannel::Power\nAnytoneCodeplug::DMRAPRSSettingsElement::power() const {\n  switch (getUInt8(Offset::power())) {\n  case 0x00: return Channel::Power::Low;\n  case 0x01: return Channel::Power::Mid;\n  case 0x02: return Channel::Power::High;\n  case 0x03: return Channel::Power::Max;\n  }\n  return Channel::Power::Low;\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt8(Offset::power(), 0x00);\n    break;\n  case Channel::Power::Mid:\n    setUInt8(Offset::power(), 0x01);\n    break;\n  case Channel::Power::High:\n    setUInt8(Offset::power(), 0x02);\n    break;\n  case Channel::Power::Max:\n    setUInt8(Offset::power(), 0x03);\n    break;\n  }\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::hasChannel(unsigned n) const {\n  return 0xffff != channelIndex(n);\n}\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::channelIsVFOA(unsigned n) const {\n  return 0x0fa0 == channelIndex(n);\n}\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::channelIsVFOB(unsigned n) const {\n  return 0x0fa1 == channelIndex(n);\n}\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::channelIsSelected(unsigned n) const {\n  return 0x0fa2 == channelIndex(n);\n}\nunsigned\nAnytoneCodeplug::DMRAPRSSettingsElement::channelIndex(unsigned n) const {\n  return getUInt16_le(0x000c + 2*n);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setChannelIndex(unsigned n, unsigned idx) {\n  setUInt16_le(0x000c + 2*n, idx);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setChannelVFOA(unsigned n) {\n  setChannelIndex(n, 0x0fa0);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setChannelVFOB(unsigned n) {\n  setChannelIndex(n, 0x0fa1);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setChannelSelected(unsigned n) {\n  setChannelIndex(n, 0x0fa2);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::clearChannel(unsigned n) {\n  setChannelIndex(n, 0xffff);\n}\n\nunsigned\nAnytoneCodeplug::DMRAPRSSettingsElement::destination() const {\n  return getBCD8_be(0x001c);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setDestination(unsigned id) {\n  setBCD8_be(0x001c, id);\n}\n\nDMRContact::Type\nAnytoneCodeplug::DMRAPRSSettingsElement::callType() const {\n  switch (getUInt8(0x0020)) {\n  case 0x00: return DMRContact::PrivateCall;\n  case 0x01: return DMRContact::GroupCall;\n  case 0x02: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::PrivateCall: setUInt8(0x0020, 0x00); break;\n  case DMRContact::GroupCall: setUInt8(0x0020, 0x01); break;\n  case DMRContact::AllCall: setUInt8(0x0020, 0x02); break;\n  }\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::timeSlotOverride() const {\n  return 0 != getUInt8(0x0021);\n}\nDMRChannel::TimeSlot\nAnytoneCodeplug::DMRAPRSSettingsElement::timeslot() const {\n  if (1 == getUInt8(0x0021))\n    return DMRChannel::TimeSlot::TS1;\n  else if (2 == getUInt8(0x0021))\n    return DMRChannel::TimeSlot::TS2;\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::overrideTimeSlot(DMRChannel::TimeSlot ts) {\n  if (DMRChannel::TimeSlot::TS1 == ts)\n    setUInt8(0x0021, 0x01);\n  else\n    setUInt8(0x0021, 0x02);\n}\nvoid\nAnytoneCodeplug::DMRAPRSSettingsElement::disableTimeSlotOverride() {\n  setUInt8(0x0021, 0x00);\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::fromConfig(const Flags &flags, Context &ctx) {\n  Q_UNUSED(flags)\n\n  // Encode fixed location if valid and enabled\n  if (ctx.config()->settings()->gnss()->fixedPosition().isValid()) {\n    setLocation(ctx.config()->settings()->gnss()->fixedPosition());\n    // Enable if there are no GNSS enabled\n    enableFixedLocation(ctx.config()->settings()->gnss()->fixedPositionEnabled());\n  }\n\n  if (1 < ctx.count<DMRAPRSSystem>()) {\n    logDebug() << \"D868UV only supports a single independent GPS positioning system.\";\n  } else if (0 == ctx.count<DMRAPRSSystem>()) {\n    return true;\n  }\n\n  DMRAPRSSystem *sys = ctx.get<DMRAPRSSystem>(0);\n  setDestination(sys->contact()->number());\n  setCallType(sys->contact()->type());\n  setManualInterval(sys->period());\n  setAutomaticInterval(sys->period());\n  disableTimeSlotOverride();\n  if (! sys->hasRevertChannel()) {\n    setChannelSelected(0);\n  } else if (sys->revertChannelRef()->is<DMRChannel>()) {\n    setChannelIndex(0, ctx.index(sys->revertChannel()));\n  } else {\n    clearChannel(0);\n  }\n  return true;\n}\n\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (location().isValid()) {\n    ctx.config()->settings()->gnss()->setFixedPosition(location());\n    ctx.config()->settings()->gnss()->enableFixedPosition(fixedLocation());\n  }\n\n  return true;\n}\n\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::createGPSSystem(uint8_t i, Context &ctx) {\n  DMRAPRSSystem *sys = new DMRAPRSSystem(QString(\"GPS sys %1\").arg(i+1), nullptr, nullptr, automaticInterval());\n  ctx.config()->posSystems()->add(sys); ctx.add(sys, i);\n  return true;\n}\n\nbool\nAnytoneCodeplug::DMRAPRSSettingsElement::linkGPSSystem(uint8_t i, Context &ctx) {\n  DMRContact *cont = nullptr;\n  // Find matching contact, if not found -> create one.\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    if (ctx.get<DMRContact>(i)->number() == destination()) {\n      cont = ctx.get<DMRContact>(i);\n      break;\n    }\n  }\n  if (nullptr == cont) {\n    cont = new DMRContact(callType(), QString(\"GPS target\"), destination());\n    ctx.config()->contacts()->add(cont);\n  }\n  ctx.get<DMRAPRSSystem>(i)->setContact(cont);\n\n  // Check if there is a revert channel set\n  if ((! channelIsSelected(i)) && (ctx.has<Channel>(channelIndex(i))) && (ctx.get<Channel>(channelIndex(i)))->is<DMRChannel>()) {\n    DMRChannel *ch = ctx.get<Channel>(channelIndex(i))->as<DMRChannel>();\n    ctx.get<DMRAPRSSystem>(i)->setRevertChannel(ch);\n  } else {\n    ctx.get<DMRAPRSSystem>(i)->resetRevertChannel();\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DMRAPRSMessageElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DMRAPRSMessageElement::DMRAPRSMessageElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DMRAPRSMessageElement::DMRAPRSMessageElement(uint8_t *ptr)\n  : Element(ptr, DMRAPRSMessageElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DMRAPRSMessageElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nQString\nAnytoneCodeplug::DMRAPRSMessageElement::message() const {\n  return readASCII(Offset::message(), Limit::length(), 0x00);\n}\n\nvoid\nAnytoneCodeplug::DMRAPRSMessageElement::setMessage(const QString &message) {\n  writeASCII(Offset::message(), message, Limit::length(), 0x00);\n}\n\nbool\nAnytoneCodeplug::DMRAPRSMessageElement::fromConfig(Codeplug::Flags flags, Context &ctx) {\n  Q_UNUSED(flags); Q_UNUSED(ctx)\n  return true;\n}\n\nbool\nAnytoneCodeplug::DMRAPRSMessageElement::updateConfig(Context &ctx) const {\n  Q_UNUSED(ctx)\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::RepeaterOffsetListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::RepeaterOffsetListElement::RepeaterOffsetListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::RepeaterOffsetListElement::RepeaterOffsetListElement(uint8_t *ptr)\n  : Element(ptr, RepeaterOffsetListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::RepeaterOffsetListElement::clear() {\n  memset(_data, 0x00, _size);\n  for (unsigned int i=0; i<Limit::numEntries(); i++)\n    clearOffset(i);\n}\n\nbool\nAnytoneCodeplug::RepeaterOffsetListElement::isSet(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return false;\n  return 0 != getUInt32_le(Offset::frequencies() + n * Offset::betweenFrequencies());\n}\n\nFrequency\nAnytoneCodeplug::RepeaterOffsetListElement::offset(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return Frequency::fromHz(0);\n\n  return Frequency::fromHz(\n        ((unsigned long long)getUInt32_le(Offset::frequencies()\n                                          + n * Offset::betweenFrequencies()))*10);\n}\n\nvoid\nAnytoneCodeplug::RepeaterOffsetListElement::setOffset(unsigned int n, Frequency freq) {\n  if (n >= Limit::numEntries())\n    return;\n\n  setUInt32_le(Offset::frequencies() + n*Offset::betweenFrequencies(), freq.inHz()/10);\n}\n\nvoid\nAnytoneCodeplug::RepeaterOffsetListElement::clearOffset(unsigned int n) {\n  if (n >= Limit::numEntries())\n    return;\n  setUInt32_le(Offset::frequencies() + n*Offset::betweenFrequencies(), 0);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::MessageListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::MessageListElement::MessageListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::MessageListElement::MessageListElement(uint8_t *ptr)\n  : Element(ptr, MessageListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::MessageListElement::clear() {\n  memset(_data, 0x00, _size);\n  clearNext();\n  clearIndex();\n}\n\nbool\nAnytoneCodeplug::MessageListElement::hasNext() const {\n  return 0xff != next();\n}\nunsigned\nAnytoneCodeplug::MessageListElement::next() const {\n  return getUInt8(Offset::next());\n}\nvoid\nAnytoneCodeplug::MessageListElement::setNext(unsigned idx) {\n  setUInt8(Offset::next(), idx);\n}\nvoid\nAnytoneCodeplug::MessageListElement::clearNext() {\n  setNext(0xff);\n}\n\nbool\nAnytoneCodeplug::MessageListElement::hasIndex() const {\n  return 0xff != index();\n}\nunsigned\nAnytoneCodeplug::MessageListElement::index() const {\n  return getUInt8(Offset::index());\n}\nvoid\nAnytoneCodeplug::MessageListElement::setIndex(unsigned idx) {\n  setUInt8(Offset::index(), idx);\n}\nvoid\nAnytoneCodeplug::MessageListElement::clearIndex() {\n  setIndex(0xff);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::MessageElement\n * ********************************************************************************************* */\nAnytoneCodeplug::MessageElement::MessageElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::MessageElement::MessageElement(uint8_t *ptr)\n  : Element(ptr, MessageElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::MessageElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nQString\nAnytoneCodeplug::MessageElement::message() const {\n  return readASCII(Offset::message(), Limit::messageLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::MessageElement::setMessage(const QString &msg) {\n  writeASCII(Offset::message(), msg, Limit::messageLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::MessageBytemapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::MessageBytemapElement::MessageBytemapElement(uint8_t *ptr, size_t size)\n  : InvertedBytemapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::MessageBytemapElement::MessageBytemapElement(uint8_t *ptr)\n  : InvertedBytemapElement(ptr, MessageBytemapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::AnalogQuickCallElement\n * ********************************************************************************************* */\nAnytoneCodeplug::AnalogQuickCallElement::AnalogQuickCallElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::AnalogQuickCallElement::AnalogQuickCallElement(uint8_t *ptr)\n  : Element(ptr, AnalogQuickCallElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::AnalogQuickCallElement::clear() {\n  memset(_data, 0x00, _size);\n  clearContactIndex();\n}\n\nAnytoneCodeplug::AnalogQuickCallElement::Type\nAnytoneCodeplug::AnalogQuickCallElement::type() const {\n  return (Type)getUInt8(Offset::type());\n}\nvoid\nAnytoneCodeplug::AnalogQuickCallElement::setType(Type type) {\n  setUInt8(Offset::type(), (unsigned)type);\n}\n\nbool\nAnytoneCodeplug::AnalogQuickCallElement::hasContactIndex() const {\n  return 0xff != contactIndex();\n}\nunsigned\nAnytoneCodeplug::AnalogQuickCallElement::contactIndex() const {\n  return getUInt8(Offset::contactIndex());\n}\nvoid\nAnytoneCodeplug::AnalogQuickCallElement::setContactIndex(unsigned idx) {\n  setUInt8(Offset::contactIndex(), idx);\n}\nvoid\nAnytoneCodeplug::AnalogQuickCallElement::clearContactIndex() {\n  setContactIndex(0xff);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::AnalogQuickCallsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::AnalogQuickCallsElement::AnalogQuickCallsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::AnalogQuickCallsElement::AnalogQuickCallsElement(uint8_t *ptr)\n  : Element(ptr, AnalogQuickCallsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::AnalogQuickCallsElement::clear() {\n  memset(_data, 0x00, _size);\n  for (unsigned int i=0; i<Limit::numEntries(); i++)\n    AnalogQuickCallElement(quickCall(i)).clear();\n}\n\nuint8_t *\nAnytoneCodeplug::AnalogQuickCallsElement::quickCall(unsigned int n) const {\n  n = std::min(Limit::numEntries(), n);\n  return _data + n*AnalogQuickCallElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::StatusMessagesElement\n * ********************************************************************************************* */\nAnytoneCodeplug::StatusMessagesElement::StatusMessagesElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::StatusMessagesElement::StatusMessagesElement(uint8_t *ptr)\n  : Element(ptr, StatusMessagesElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::StatusMessagesElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nQString\nAnytoneCodeplug::StatusMessagesElement::message(unsigned int n) const {\n  n = std::min(Limit::numMessages(), n);\n  return readASCII(Offset::messages()+n*Offset::betweenMessages(), Limit::messageLength(), 0x00);\n}\n\nvoid\nAnytoneCodeplug::StatusMessagesElement::setMessage(unsigned int n, const QString &msg) {\n  if (n >= Limit::numMessages())\n    return;\n  writeASCII(Offset::messages()+n*Offset::betweenMessages(), msg, Limit::messageLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::StatusMessageBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::StatusMessageBitmapElement::StatusMessageBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::StatusMessageBitmapElement::StatusMessageBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, StatusMessageBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::HotKeyElement\n * ********************************************************************************************* */\nAnytoneCodeplug::HotKeyElement::HotKeyElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::HotKeyElement::HotKeyElement(uint8_t *ptr)\n  : Element(ptr, HotKeyElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::HotKeyElement::clear() {\n  memset(_data, 0x00, _size);\n  clearContactIndex();\n  clearMessageIndex();\n}\n\nAnytoneCodeplug::HotKeyElement::Type\nAnytoneCodeplug::HotKeyElement::type() const {\n  return (Type)getUInt8(Offset::type());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setType(Type type) {\n  setUInt8(Offset::type(), (unsigned)type);\n}\n\nAnytoneCodeplug::HotKeyElement::MenuItem\nAnytoneCodeplug::HotKeyElement::menuItem() const {\n  return (MenuItem) getUInt8(Offset::menuItem());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setMenuItem(MenuItem item) {\n  setUInt8(Offset::menuItem(), (unsigned)item);\n}\n\nAnytoneCodeplug::HotKeyElement::CallType\nAnytoneCodeplug::HotKeyElement::callType() const {\n  return (CallType)getUInt8(Offset::callType());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setCallType(CallType type) {\n  setUInt8(Offset::callType(), (unsigned)type);\n}\n\nAnytoneCodeplug::HotKeyElement::DigiCallType\nAnytoneCodeplug::HotKeyElement::digiCallType() const {\n  return (DigiCallType)getUInt8(Offset::digiCallType());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setDigiCallType(DigiCallType type) {\n  setUInt8(Offset::digiCallType(), (unsigned)type);\n}\n\nbool\nAnytoneCodeplug::HotKeyElement::hasContactIndex() const {\n  return 0xffffffff != contactIndex();\n}\nunsigned\nAnytoneCodeplug::HotKeyElement::contactIndex() const {\n  return getUInt32_le(Offset::contactIndex());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setContactIndex(unsigned idx) {\n  setUInt32_le(Offset::contactIndex(), idx);\n}\nvoid\nAnytoneCodeplug::HotKeyElement::clearContactIndex() {\n  setContactIndex(0xffffffff);\n}\n\nbool\nAnytoneCodeplug::HotKeyElement::hasMessageIndex() const {\n  return 0xff != messageIndex();\n}\nunsigned\nAnytoneCodeplug::HotKeyElement::messageIndex() const {\n  return getUInt8(Offset::messageIndex());\n}\nvoid\nAnytoneCodeplug::HotKeyElement::setMessageIndex(unsigned idx) {\n  setUInt8(Offset::messageIndex(), idx);\n}\nvoid\nAnytoneCodeplug::HotKeyElement::clearMessageIndex() {\n  setMessageIndex(0xff);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::HotKeySettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr)\n  : Element(ptr, HotKeySettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::HotKeySettingsElement::clear() {\n  memset(_data, 0x00, _size);\n  for (unsigned int i=0; i<Limit::numEntries(); i++)\n    HotKeyElement(hotKeySetting(i)).clear();\n}\n\nuint8_t *\nAnytoneCodeplug::HotKeySettingsElement::hotKeySetting(unsigned int n) const {\n  n = std::min(Limit::numEntries(), n);\n  return _data + Offset::hotKeySettings() + n*Offset::betweenHotKeySettings();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::AlarmSettingElement::AnalogAlarm\n * ********************************************************************************************* */\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::AnalogAlarm(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::AnalogAlarm(uint8_t *ptr)\n  : Element(ptr, AlarmSettingElement::AnalogAlarm::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::clear() {\n  memset(_data, 0x00, _size);\n  setChannelSelected();\n}\n\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::Action\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::action() const {\n  return (Action) getUInt8(Offset::action());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setAction(Action action) {\n  setUInt8(Offset::action(), (unsigned)action);\n}\n\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::ENIType\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::encodingType() const {\n  return (ENIType) getUInt8(Offset::encodingType());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setEncodingType(ENIType type) {\n  setUInt8(Offset::encodingType(), (unsigned)type);\n}\n\nunsigned\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::emergencyIndex() const {\n  return getUInt8(Offset::emergencyIndex());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setEmergencyIndex(unsigned idx) {\n  setUInt8(Offset::emergencyIndex(), idx);\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::duration() const {\n  return Interval::fromSeconds(getUInt8(0x0003));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setDuration(const Interval &sec) {\n  setUInt8(Offset::duration(), sec.seconds());\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::txDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::txDuration()));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setTXDuration(const Interval &sec) {\n  setUInt8(Offset::txDuration(), sec.seconds());\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::rxDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::rxDuration()));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setRXDuration(const Interval &sec) {\n  setUInt8(Offset::rxDuration(), sec.seconds());\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::channelIsSelected() const {\n  return getUInt8(Offset::channelIsSelected());\n}\nunsigned\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::channelIndex() const {\n  return getUInt16_le(Offset::channelIndex());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setChannelIndex(unsigned idx) {\n  setUInt16_le(Offset::channelIndex(), idx);\n  setUInt8(Offset::channelIsSelected(), 0x00);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setChannelSelected() {\n  setUInt8(Offset::channelIsSelected(), 0x01);\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::repeatContinuously() const {\n  return 0 == repetitions();\n}\nunsigned\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::repetitions() const {\n  return getUInt8(Offset::repetitions());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setRepetitions(unsigned num) {\n  setUInt8(Offset::repetitions(), num);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::AnalogAlarm::setRepatContinuously() {\n  setRepetitions(0x00);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::AlarmSettingElement::DigitalAlarm\n * ********************************************************************************************* */\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::DigitalAlarm(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::DigitalAlarm(uint8_t *ptr)\n  : Element(ptr, AlarmSettingElement::DigitalAlarm::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::clear() {\n  memset(_data, 0x00, _size);\n  setChannelSelected();\n}\n\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::Action\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::action() const {\n  return (Action) getUInt8(Offset::action());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setAction(Action action) {\n  setUInt8(Offset::action(), (unsigned)action);\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::duration() const {\n  return Interval::fromSeconds(getUInt8(Offset::duration()));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setDuration(const Interval &sec) {\n  setUInt8(Offset::duration(), sec.seconds());\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::txDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::txDuration()));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setTXDuration(const Interval &sec) {\n  setUInt8(Offset::txDuration(), sec.seconds());\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::rxDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::rxDuration()));\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setRXDuration(const Interval &sec) {\n  setUInt8(Offset::rxDuration(), sec.seconds());\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::channelIsSelected() const {\n  return getUInt8(Offset::channelIsSelected());\n}\nunsigned\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::channelIndex() const {\n  return getUInt16_le(Offset::channelIndex());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setChannelIndex(unsigned idx) {\n  setUInt16_le(Offset::channelIndex(), idx);\n  setUInt8(Offset::channelIsSelected(), 0x00);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setChannelSelected() {\n  setUInt8(Offset::channelIsSelected(), 0x01);\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::repeatContinuously() const {\n  return 0 == repetitions();\n}\nunsigned\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::repetitions() const {\n  return getUInt8(Offset::repetitions());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setRepetitions(unsigned num) {\n  setUInt8(Offset::repetitions(), num);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setRepatContinuously() {\n  setRepetitions(0x00);\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::voiceBroadcastDuration() const {\n  return Interval::fromMinutes(getUInt8(Offset::voiceBroadcastDuration())+1);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setVoiceBroadcastDuration(const Interval &min) {\n  unsigned int m = min.minutes();\n  if (1 > m) m = 1;\n  setUInt8(Offset::voiceBroadcastDuration(), m-1);\n}\n\nInterval\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::areaBroadcastDuration() const {\n  return Interval::fromMinutes(getUInt8(Offset::areaBroadcastDuration())+1);\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::setAreaBroadcastDuration(const Interval &min) {\n  unsigned int m = min.minutes();\n  if (1 > m) m = 1;\n  setUInt8(Offset::areaBroadcastDuration(), m-1);\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::vox() const {\n  return getUInt8(Offset::vox());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::enableVOX(bool enable) {\n  setUInt8(Offset::vox(), (enable ? 0x01 : 0x00));\n}\n\nbool\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::rxAlarm() const {\n  return getUInt8(Offset::rxAlarm());\n}\nvoid\nAnytoneCodeplug::AlarmSettingElement::DigitalAlarm::enableRXAlarm(bool enable) {\n  setUInt8(Offset::rxAlarm(), (enable ? 0x01 : 0x00));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::AlarmSettingElement\n * ********************************************************************************************* */\nAnytoneCodeplug::AlarmSettingElement::AlarmSettingElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::AlarmSettingElement::AlarmSettingElement(uint8_t *ptr)\n  : Element(ptr, AlarmSettingElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::AlarmSettingElement::clear() {\n  AnalogAlarm(analog()).clear();\n  DigitalAlarm(digital()).clear();\n}\n\nuint8_t *\nAnytoneCodeplug::AlarmSettingElement::analog() const {\n  return _data + Offset::analog();\n}\nuint8_t *\nAnytoneCodeplug::AlarmSettingElement::digital() const {\n  return _data + Offset::digital();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DigitalAlarmExtensionElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DigitalAlarmExtensionElement::DigitalAlarmExtensionElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DigitalAlarmExtensionElement::DigitalAlarmExtensionElement(uint8_t *ptr)\n  : Element(ptr, DigitalAlarmExtensionElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DigitalAlarmExtensionElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nDMRContact::Type\nAnytoneCodeplug::DigitalAlarmExtensionElement::callType() const {\n  switch (getUInt8(Offset::callType())) {\n  case 0x00: return DMRContact::PrivateCall;\n  case 0x01: return DMRContact::GroupCall;\n  case 0x02: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nAnytoneCodeplug::DigitalAlarmExtensionElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::PrivateCall: setUInt8(Offset::callType(), 0x00); break;\n  case DMRContact::GroupCall: setUInt8(Offset::callType(), 0x01); break;\n  case DMRContact::AllCall: setUInt8(Offset::callType(), 0x02); break;\n  }\n}\n\nunsigned\nAnytoneCodeplug::DigitalAlarmExtensionElement::destination() const {\n  return getBCD8_be(Offset::destination());\n}\nvoid\nAnytoneCodeplug::DigitalAlarmExtensionElement::setDestination(unsigned number) {\n  setBCD8_be(Offset::destination(), number);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneIDElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneIDElement::FiveToneIDElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneIDElement::FiveToneIDElement(uint8_t *ptr)\n  : Element(ptr, FiveToneIDElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::FiveToneIDElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nAnytoneCodeplug::FiveToneIDElement::Standard\nAnytoneCodeplug::FiveToneIDElement::standard() const {\n  return (Standard) getUInt8(Offset::standard());\n}\nvoid\nAnytoneCodeplug::FiveToneIDElement::setStandard(Standard std) {\n  setUInt8(Offset::standard(), (unsigned)std);\n}\n\nInterval\nAnytoneCodeplug::FiveToneIDElement::toneDuration() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::toneDuration()));\n}\nvoid\nAnytoneCodeplug::FiveToneIDElement::setToneDuration(const Interval &ms) {\n  setUInt8(Offset::toneDuration(), ms.milliseconds());\n}\n\nQString\nAnytoneCodeplug::FiveToneIDElement::id() const {\n  unsigned len = getUInt8(Offset::idLength());\n  QString id;\n  for (unsigned i=0; i<len; i++) {\n    uint8_t b = getUInt8(Offset::id()+(i/2));\n    if (0 == (i%2))\n      id.append(QString::number((b>>4)&0xf,16));\n    else\n      id.append(QString::number((b>>0)&0xf,16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::FiveToneIDElement::setID(const QString &id) {\n  unsigned len = 0;\n  for (int i=0; i<id.length(); i++) {\n    bool ok;\n    if (0 == (len%2)) {\n      setUInt4(Offset::id()+len/2, 4, id.mid(i, 1).toUInt(&ok, 16));\n    } else {\n      setUInt4(Offset::id()+len/2, 0, id.mid(i, 1).toUInt(&ok, 16));\n    }\n    len++;\n  }\n  setUInt8(Offset::idLength(), len);\n}\n\nQString\nAnytoneCodeplug::FiveToneIDElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::FiveToneIDElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneIDBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneIDBitmapElement::FiveToneIDBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneIDBitmapElement::FiveToneIDBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, FiveToneIDBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneIDListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneIDListElement::FiveToneIDListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneIDListElement::FiveToneIDListElement(uint8_t *ptr)\n  : Element(ptr, FiveToneIDListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::FiveToneIDListElement::clear() {\n  memset(_data, 0, _size);\n}\n\nuint8_t *\nAnytoneCodeplug::FiveToneIDListElement::member(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return nullptr;\n  return _data + n*FiveToneIDElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneFunctionElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneFunctionElement::FiveToneFunctionElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneFunctionElement::FiveToneFunctionElement(uint8_t *ptr)\n  : Element(ptr, FiveToneFunctionElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::FiveToneFunctionElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nAnytoneCodeplug::FiveToneFunctionElement::Function\nAnytoneCodeplug::FiveToneFunctionElement::function() const {\n  return (Function) getUInt8(Offset::function());\n}\nvoid\nAnytoneCodeplug::FiveToneFunctionElement::setFunction(Function function) {\n  setUInt8(Offset::function(), (unsigned)function);\n}\n\nAnytoneCodeplug::FiveToneFunctionElement::Response\nAnytoneCodeplug::FiveToneFunctionElement::response() const {\n  return (Response) getUInt8(Offset::response());\n}\nvoid\nAnytoneCodeplug::FiveToneFunctionElement::setResponse(Response response) {\n  setUInt8(Offset::response(), (unsigned)response);\n}\n\nQString\nAnytoneCodeplug::FiveToneFunctionElement::id() const {\n  unsigned len = getUInt8(Offset::idLength());\n  QString id;\n  for (unsigned i=0; i<len; i++) {\n    uint8_t b = getUInt8(Offset::id()+(i/2));\n    if (0 == (i%2))\n      id.append(QString::number((b>>4)&0xf, 16));\n    else\n      id.append(QString::number((b>>0)&0xf, 16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::FiveToneFunctionElement::setID(const QString &id) {\n  unsigned len = 0;\n  for (int i=0; i<id.length(); i++) {\n    bool ok;\n    if (0 == (len%2)) {\n      setUInt4(Offset::id()+len/2, 4, id.mid(i, 1).toUInt(&ok, 16));\n    } else {\n      setUInt4(Offset::id()+len/2, 0, id.mid(i, 1).toUInt(&ok, 16));\n    }\n    len++;\n  }\n  setUInt8(Offset::idLength(), len);\n}\n\nQString\nAnytoneCodeplug::FiveToneFunctionElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::FiveToneFunctionElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneFunctionListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneFunctionListElement::FiveToneFunctionListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneFunctionListElement::FiveToneFunctionListElement(uint8_t *ptr)\n  : Element(ptr, FiveToneFunctionListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::FiveToneFunctionListElement::clear() {\n  memset(_data, 0, _size);\n}\n\nuint8_t *\nAnytoneCodeplug::FiveToneFunctionListElement::function(unsigned int n) const {\n  if (n >= Limit::numFunctions())\n    return nullptr;\n  return _data + n*FiveToneFunctionElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::FiveToneSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::FiveToneSettingsElement::FiveToneSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::FiveToneSettingsElement::FiveToneSettingsElement(uint8_t *ptr)\n  : Element(ptr, FiveToneSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nAnytoneCodeplug::FiveToneSettingsElement::Response\nAnytoneCodeplug::FiveToneSettingsElement::decodingResponse() const {\n  return (Response) getUInt8(Offset::decodingResponse());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setDecodingResponse(Response response) {\n  setUInt8(Offset::decodingResponse(), (unsigned)response);\n}\n\nAnytoneCodeplug::FiveToneSettingsElement::Standard\nAnytoneCodeplug::FiveToneSettingsElement::decodingStandard() const {\n  return (Standard) getUInt8(Offset::decodingStandard());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setDecodingStandard(Standard standard) {\n  setUInt8(Offset::decodingStandard(), (unsigned)standard);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::decodingToneDuration() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::decodingToneDuration()));\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setDecodingToneDuration(const Interval &ms) {\n  setUInt8(Offset::decodingToneDuration(), ms.milliseconds());\n}\n\nQString\nAnytoneCodeplug::FiveToneSettingsElement::id() const {\n  unsigned len = getUInt8(Offset::idLength());\n  QString id;\n  for (unsigned i=0; i<len; i++) {\n    uint8_t b = getUInt8(Offset::id()+(i/2));\n    if (0 == (i%2))\n      id.append(QString::number((b>>4)&0xf, 16));\n    else\n      id.append(QString::number((b>>0)&0xf, 16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setID(const QString &id) {\n  unsigned len = 0;\n  for (int i=0; i<id.length(); i++) {\n    bool ok;\n    if (0 == (len%2)) {\n      setUInt4(Offset::id()+len/2, 4, id.mid(i, 1).toUInt(&ok, 16));\n    } else {\n      setUInt4(Offset::id()+len/2, 0, id.mid(i, 1).toUInt(&ok, 16));\n    }\n    len++;\n  }\n  setUInt8(Offset::idLength(), len);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::postEncodeDelay() const {\n  return Interval::fromMilliseconds((unsigned) getUInt8(Offset::postDecodeDelay()) * 10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setPostEncodeDelay(const Interval &ms) {\n  setUInt8(Offset::postDecodeDelay(), ms.milliseconds()/10);\n}\n\nbool\nAnytoneCodeplug::FiveToneSettingsElement::hasPTTID() const {\n  return 0 != pttID();\n}\nunsigned\nAnytoneCodeplug::FiveToneSettingsElement::pttID() const {\n  return getUInt8(Offset::pttId());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setPTTID(unsigned id) {\n  setUInt8(Offset::pttId(), id);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::clearPTTID() {\n  setPTTID(0);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::autoResetTime() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::autoResetTime())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setAutoResetTime(const Interval &s) {\n  setUInt8(Offset::autoResetTime(), s.seconds()/10);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::firstDelay() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::firstDelay())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setFirstDelay(const Interval &ms) {\n  setUInt8(Offset::firstDelay(), ms.seconds()/10);\n}\n\nbool\nAnytoneCodeplug::FiveToneSettingsElement::sidetoneEnabled() const {\n  return getUInt8(Offset::sidetoneEnabled());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::enableSidetone(bool enable) {\n  setUInt8(Offset::sidetoneEnabled(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nAnytoneCodeplug::FiveToneSettingsElement::stopCode() const {\n  return getUInt8(Offset::stopCode());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setStopCode(unsigned code) {\n  setUInt8(Offset::stopCode(), code);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::stopTime() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::stopTime())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setStopTime(const Interval &ms) {\n  setUInt8(Offset::stopTime(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::decodeTime() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::decodeTime())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setDecodeTime(const Interval &ms) {\n  setUInt8(Offset::decodeTime(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::delayAfterStop() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::delayAfterStop())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setDelayAfterStop(const Interval &ms) {\n  setUInt8(Offset::delayAfterStop(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::preTime() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preTime())*10);\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setPreTime(const Interval &ms) {\n  setUInt8(Offset::preTime(), ms.milliseconds()/10);\n}\n\nAnytoneCodeplug::FiveToneSettingsElement::Standard\nAnytoneCodeplug::FiveToneSettingsElement::botStandard() const {\n  return (Standard) getUInt8(Offset::botStandard());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setBOTStandard(Standard standard) {\n  setUInt8(Offset::botStandard(), (unsigned)standard);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::botToneDuration() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::botToneDuration()));\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setBOTToneDuration(const Interval &ms) {\n  setUInt8(Offset::botToneDuration(), ms.milliseconds());\n}\n\nQString\nAnytoneCodeplug::FiveToneSettingsElement::botID() const {\n  unsigned len = getUInt8(Offset::botIdLength());\n  QString id;\n  for (unsigned i=0; i<len; i++) {\n    uint8_t b = getUInt8(Offset::botId()+(i/2));\n    if (0 == (i%2))\n      id.append(QString::number((b>>4)&0xf, 16));\n    else\n      id.append(QString::number((b>>0)&0xf, 16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setBOTID(const QString &id) {\n  unsigned len = 0;\n  for (int i=0; i<id.length(); i++) {\n    bool ok;\n    if (0 == (len%2)) {\n      setUInt4(Offset::botId()+len/2, 4, id.mid(i, 1).toUInt(&ok, 16));\n    } else {\n      setUInt4(Offset::botId()+len/2, 0, id.mid(i, 1).toUInt(&ok, 16));\n    }\n    len++;\n  }\n  setUInt8(Offset::botIdLength(), len);\n}\n\nAnytoneCodeplug::FiveToneSettingsElement::Standard\nAnytoneCodeplug::FiveToneSettingsElement::eotStandard() const {\n  return (Standard) getUInt8(Offset::eotStandard());\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setEOTStandard(Standard standard) {\n  setUInt8(Offset::eotStandard(), (unsigned)standard);\n}\n\nInterval\nAnytoneCodeplug::FiveToneSettingsElement::eotToneDuration() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::eotToneDuration()));\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setEOTToneDuration(const Interval &ms) {\n  setUInt8(Offset::eotToneDuration(), ms.milliseconds());\n}\n\nQString\nAnytoneCodeplug::FiveToneSettingsElement::eotID() const {\n  unsigned len = getUInt8(Offset::eotIdLength());\n  QString id;\n  for (unsigned i=0; i<len; i++) {\n    uint8_t b = getUInt8(Offset::eotId()+(i/2));\n    if (0 == (i%2))\n      id.append(QString::number((b>>4)&0xf, 16));\n    else\n      id.append(QString::number((b>>0)&0xf, 16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::FiveToneSettingsElement::setEOTID(const QString &id) {\n  unsigned len = 0;\n  for (int i=0; i<id.length(); i++) {\n    bool ok;\n    if (0 == (len%2)) {\n      setUInt4(Offset::eotId()+len/2, 4, id.mid(i, 1).toUInt(&ok, 16));\n    } else {\n      setUInt4(Offset::eotId()+len/2, 0, id.mid(i, 1).toUInt(&ok, 16));\n    }\n    len++;\n  }\n  setUInt8(Offset::eotIdLength(), len);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::TwoToneIDElement\n * ********************************************************************************************* */\nAnytoneCodeplug::TwoToneIDElement::TwoToneIDElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::TwoToneIDElement::TwoToneIDElement(uint8_t *ptr)\n  : Element(ptr, TwoToneIDElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::TwoToneIDElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\ndouble\nAnytoneCodeplug::TwoToneIDElement::firstTone() const {\n  return ((double)getUInt16_le(Offset::firstTone()))/10;\n}\nvoid\nAnytoneCodeplug::TwoToneIDElement::setFirstTone(double f) {\n  setUInt16_le(Offset::firstTone(), f*10);\n}\n\ndouble\nAnytoneCodeplug::TwoToneIDElement::secondTone() const {\n  return ((double)getUInt16_le(Offset::secondTone()))/10;\n}\nvoid\nAnytoneCodeplug::TwoToneIDElement::setSecondTone(double f) {\n  setUInt16_le(Offset::secondTone(), f*10);\n}\n\nQString\nAnytoneCodeplug::TwoToneIDElement::name() const {\n  return readASCII(Offset::name(), 7, 0x00);\n}\nvoid\nAnytoneCodeplug::TwoToneIDElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::TwoToneIDBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::TwoToneIDBitmapElement::TwoToneIDBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::TwoToneIDBitmapElement::TwoToneIDBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, TwoToneIDBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::TwoToneFunctionElement\n * ********************************************************************************************* */\nAnytoneCodeplug::TwoToneFunctionElement::TwoToneFunctionElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::TwoToneFunctionElement::TwoToneFunctionElement(uint8_t *ptr)\n  : Element(ptr, TwoToneFunctionElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::TwoToneFunctionElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\ndouble\nAnytoneCodeplug::TwoToneFunctionElement::firstTone() const {\n  return ((double)getUInt16_le(Offset::firstTone()))/10;\n}\nvoid\nAnytoneCodeplug::TwoToneFunctionElement::setFirstTone(double f) {\n  setUInt16_le(Offset::firstTone(), f*10);\n}\n\ndouble\nAnytoneCodeplug::TwoToneFunctionElement::secondTone() const {\n  return ((double)getUInt16_le(Offset::secondTone()))/10;\n}\nvoid\nAnytoneCodeplug::TwoToneFunctionElement::setSecondTone(double f) {\n  setUInt16_le(Offset::secondTone(), f*10);\n}\n\nAnytoneCodeplug::TwoToneFunctionElement::Response\nAnytoneCodeplug::TwoToneFunctionElement::response() const {\n  return (Response) getUInt8(Offset::response());\n}\nvoid\nAnytoneCodeplug::TwoToneFunctionElement::setResponse(Response resp) {\n  setUInt8(Offset::response(), (unsigned)resp);\n}\n\nQString\nAnytoneCodeplug::TwoToneFunctionElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nAnytoneCodeplug::TwoToneFunctionElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::TwoToneFunctionBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::TwoToneFunctionBitmapElement::TwoToneFunctionBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::TwoToneFunctionBitmapElement::TwoToneFunctionBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, TwoToneFunctionBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::TwoToneSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::TwoToneSettingsElement::TwoToneSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::TwoToneSettingsElement::TwoToneSettingsElement(uint8_t *ptr)\n  : Element(ptr, TwoToneSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nInterval\nAnytoneCodeplug::TwoToneSettingsElement::firstToneDuration() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::firstToneDuration()) * 100);\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::setFirstToneDuration(const Interval &ms) {\n  setUInt8(Offset::firstToneDuration(), ms.milliseconds()/100);\n}\n\nInterval\nAnytoneCodeplug::TwoToneSettingsElement::secondToneDuration() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::secondToneDuration())*100);\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::setSecondToneDuration(const Interval &ms) {\n  setUInt8(Offset::secondToneDuration(), ms.milliseconds()/100);\n}\n\nInterval\nAnytoneCodeplug::TwoToneSettingsElement::longToneDuration() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::longToneDuration())*100);\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::setLongToneDuration(const Interval &ms) {\n  setUInt8(Offset::longToneDuration(), ms.milliseconds()/100);\n}\n\nInterval\nAnytoneCodeplug::TwoToneSettingsElement::gapDuration() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::gapDuration())*100);\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::setGapDuration(const Interval &ms) {\n  setUInt8(Offset::gapDuration(), ms.milliseconds()/100);\n}\n\nInterval\nAnytoneCodeplug::TwoToneSettingsElement::autoResetTime() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::autoResetTime())*10);\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::setAutoResetTime(const Interval &sec) {\n  setUInt8(Offset::autoResetTime(), sec.seconds()/10);\n}\n\nbool\nAnytoneCodeplug::TwoToneSettingsElement::sidetone() const {\n  return getUInt8(Offset::sidetone());\n}\nvoid\nAnytoneCodeplug::TwoToneSettingsElement::enableSidetone(bool enable) {\n  setUInt8(Offset::sidetone(), (enable ? 0x01 : 0x00));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DTMFSettingsElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr)\n  : Element(ptr, DTMFSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DTMFSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\n\nunsigned\nAnytoneCodeplug::DTMFSettingsElement::intervalSymbol() const {\n  return getUInt8(Offset::intervalSymbol());\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setIntervalSymbol(unsigned symb) {\n  setUInt8(Offset::intervalSymbol(), symb);\n}\n\nunsigned\nAnytoneCodeplug::DTMFSettingsElement::groupCode() const {\n  return getUInt8(Offset::groupCode());\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setGroupCode(unsigned symb) {\n  setUInt8(Offset::groupCode(), symb);\n}\n\nAnytoneCodeplug::DTMFSettingsElement::Response\nAnytoneCodeplug::DTMFSettingsElement::response() const {\n  return (Response)getUInt8(Offset::response());\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setResponse(Response resp) {\n  setUInt8(Offset::response(), (unsigned)resp);\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::preTime() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preTime())*10);\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setPreTime(const Interval &ms) {\n  setUInt8(Offset::preTime(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::firstDigitDuration() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::firstDigitDuration())*10);\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setFirstDigitDuration(const Interval &ms) {\n  setUInt8(Offset::firstDigitDuration(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::autoResetTime() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::autoResetTime())*10);\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setAutoResetTime(const Interval &sec) {\n  setUInt8(Offset::autoResetTime(), sec.seconds()/10);\n}\n\nQString\nAnytoneCodeplug::DTMFSettingsElement::id() const {\n  QString id;\n  for (unsigned int i=0; (i<Limit::idLength())&&(0xff!=getUInt8(Offset::id()+i)); i++) {\n    id.append(QString::number(getUInt8(Offset::id()+i),16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setID(const QString &id) {\n  int len = std::min((qsizetype)Limit::idLength(), id.length());\n  bool ok;\n  for (int i=0; i<len; i++)\n    setUInt8(Offset::id()+i, id.mid(i,1).toUInt(&ok, 16));\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::postEncodingDelay() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::postEncodingDelay())*10);\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setPostEncodingDelay(const Interval &ms) {\n  setUInt8(Offset::postEncodingDelay(), ms.milliseconds()/10);\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::pttIDPause() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::pttIDPause())*10);\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setPTTIDPause(const Interval &sec) {\n  setUInt8(Offset::pttIDPause(), sec.seconds()/10);\n}\n\nbool\nAnytoneCodeplug::DTMFSettingsElement::pttIDEnabled() const {\n  return getUInt8(Offset::pttIDEnabled());\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::enablePTTID(bool enable) {\n  setUInt8(Offset::pttIDEnabled(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nAnytoneCodeplug::DTMFSettingsElement::dCodePause() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::dCodePause()));\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setDCodePause(const Interval &sec) {\n  setUInt8(Offset::dCodePause(), sec.seconds());\n}\n\nbool\nAnytoneCodeplug::DTMFSettingsElement::sidetone() const {\n  return getUInt8(Offset::sidetone());\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::enableSidetone(bool enable) {\n  setUInt8(Offset::sidetone(), (enable ? 0x01 : 0x00));\n}\n\nQString\nAnytoneCodeplug::DTMFSettingsElement::botID() const {\n  QString id;\n  for (unsigned int i=0; (i<Limit::botIdLength())&&(0xff!=getUInt8(Offset::botID()+i)); i++) {\n    id.append(QString::number(getUInt8(Offset::botID()+i),16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setBOTID(const QString &id) {\n  int len = std::min(qsizetype(Limit::botIdLength()), id.length());\n  bool ok;\n  for (int i=0; i<len; i++)\n    setUInt8(Offset::botID()+i, id.mid(i,1).toUInt(&ok, 16));\n}\n\nQString\nAnytoneCodeplug::DTMFSettingsElement::eotID() const {\n  QString id;\n  for (unsigned int i=0; (i<Limit::eotIdLength())&&(0xff!=getUInt8(Offset::eotID()+i)); i++) {\n    id.append(QString::number(getUInt8(Offset::eotID()+i),16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setEOTID(const QString &id) {\n  int len = std::min(qsizetype(Limit::eotIdLength()), id.length());\n  bool ok;\n  for (int i=0; i<len; i++)\n    setUInt8(Offset::eotID()+i, id.mid(i,1).toUInt(&ok, 16));\n}\n\nQString\nAnytoneCodeplug::DTMFSettingsElement::remoteKillID() const {\n  QString id;\n  for (unsigned int i=0; (i<Limit::remoteKillIdLength()) && (0xff!=getUInt8(Offset::remoteKillID()+i)); i++) {\n    id.append(QString::number(getUInt8(Offset::remoteKillID()+i),16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setRemoteKillID(const QString &id) {\n  int len = std::min(qsizetype(Limit::remoteKillIdLength()), id.length());\n  bool ok;\n  for (int i=0; i<len; i++)\n    setUInt8(Offset::remoteKillID()+i, id.mid(i,1).toUInt(&ok, 16));\n}\n\n\nQString\nAnytoneCodeplug::DTMFSettingsElement::remoteStunID() const {\n  QString id;\n  for (unsigned int i=0; (i<Limit::remteStunIdLength()) && (0xff!=getUInt8(Offset::remoteStunID()+i)); i++) {\n    id.append(QString::number(getUInt8(Offset::remoteStunID()+i),16));\n  }\n  return id;\n}\nvoid\nAnytoneCodeplug::DTMFSettingsElement::setRemoteStunID(const QString &id) {\n  int len = std::min(qsizetype(Limit::remteStunIdLength()), id.length());\n  bool ok;\n  for (int i=0; i<len; i++)\n    setUInt8(Offset::remoteStunID()+i, id.mid(i,1).toUInt(&ok, 16));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DTMFIDListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DTMFIDListElement::DTMFIDListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DTMFIDListElement::DTMFIDListElement(uint8_t *ptr)\n  : Element(ptr, DTMFIDListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DTMFIDListElement::clear() {\n  memset(_data, 0xff, _size);\n}\n\nbool\nAnytoneCodeplug::DTMFIDListElement::hasNumber(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return false;\n  return 0xff != getUInt8(n*Limit::numberLength());\n}\n\nQString\nAnytoneCodeplug::DTMFIDListElement::number(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return \"\";\n  uint8_t *num = _data + n*Limit::numberLength();\n  return decode_dtmf_bin(num, Limit::numberLength(), 0xff);\n}\n\nvoid\nAnytoneCodeplug::DTMFIDListElement::setNumber(unsigned int n, const QString &number) {\n  if (n >= Limit::numEntries())\n    return;\n  uint8_t *num = _data + n*Limit::numberLength();\n  encode_dtmf_bin(number, num, Limit::numberLength(), 0xff);\n}\n\nvoid\nAnytoneCodeplug::DTMFIDListElement::clearNumber(unsigned int n) {\n  if (n >= Limit::numEntries())\n    return;\n  uint8_t *num = _data + n*Limit::numberLength();\n  memset(num, 0xff, Limit::numberLength());\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::WFMChannelListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::WFMChannelListElement::WFMChannelListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::WFMChannelListElement::WFMChannelListElement(uint8_t *ptr)\n  : Element(ptr, WFMChannelListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::WFMChannelListElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nbool\nAnytoneCodeplug::WFMChannelListElement::hasChannel(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return false;\n  return 0 != getBCD8_le(n*Offset::betweenChannels());\n}\n\nFrequency\nAnytoneCodeplug::WFMChannelListElement::channel(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return Frequency();\n  return Frequency::fromHz(((unsigned long long)getBCD8_le(n*Offset::betweenChannels()))*100);\n}\n\nvoid\nAnytoneCodeplug::WFMChannelListElement::setChannel(unsigned int n, Frequency freq) {\n  if (n >= Limit::numEntries())\n    return;\n  setBCD8_le(n*Offset::betweenChannels(), freq.inHz()/100);\n}\n\nvoid\nAnytoneCodeplug::WFMChannelListElement::clearChannel(unsigned int n) {\n  if (n >= Limit::numEntries())\n    return;\n  setBCD8_le(n*Offset::betweenChannels(), 0);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::WFMChannelBitmapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::WFMChannelBitmapElement::WFMChannelBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::WFMChannelBitmapElement::WFMChannelBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, WFMChannelBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::WFMVFOElement\n * ********************************************************************************************* */\nAnytoneCodeplug::WFMVFOElement::WFMVFOElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::WFMVFOElement::WFMVFOElement(uint8_t *ptr)\n  : Element(ptr, WFMVFOElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::WFMVFOElement::clear() {\n  memset(_data, 0x00, _size);\n  setFrequency(Frequency::fromMHz(88));\n}\n\nFrequency\nAnytoneCodeplug::WFMVFOElement::frequency() const {\n  return Frequency::fromHz(((unsigned long long)getBCD8_le(0))*100);\n}\n\nvoid\nAnytoneCodeplug::WFMVFOElement::setFrequency(Frequency freq) {\n  setBCD8_le(0, freq.inHz()/100);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::DMREncryptionKeyListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::DMREncryptionKeyListElement::DMREncryptionKeyListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::DMREncryptionKeyListElement::DMREncryptionKeyListElement(uint8_t *ptr)\n  : Element(ptr, DMREncryptionKeyListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::DMREncryptionKeyListElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nbool\nAnytoneCodeplug::DMREncryptionKeyListElement::hasKey(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return false;\n  return 0 != getUInt16_be(n*Offset::betweenKeys());\n}\n\nQByteArray\nAnytoneCodeplug::DMREncryptionKeyListElement::key(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return QByteArray::fromHex(\"0000\");\n  return QByteArray((char *)_data + n*Offset::betweenKeys(), 2);\n}\n\nvoid\nAnytoneCodeplug::DMREncryptionKeyListElement::setKey(unsigned int n, const BasicEncryptionKey &key) {\n  if (n >= Limit::numEntries())\n    return;\n  memcpy(_data + n*Offset::betweenKeys(), key.key().data(), 2);\n}\n\nvoid\nAnytoneCodeplug::DMREncryptionKeyListElement::clearKey(unsigned int n) {\n  if (n >= Limit::numEntries())\n    return;\n  setUInt16_be(n*Offset::betweenKeys(), 0000);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::EnhancedEncryptionKeyListElement\n * ********************************************************************************************* */\nAnytoneCodeplug::EnhancedEncryptionKeyListElement::EnhancedEncryptionKeyListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::EnhancedEncryptionKeyListElement::EnhancedEncryptionKeyListElement(uint8_t *ptr)\n  : Element(ptr, EnhancedEncryptionKeyListElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::EnhancedEncryptionKeyListElement::clear() {\n  memset(_data, 0x00, _size);\n  for (unsigned int i=0; i<Limit::numEntries(); i++) {\n    setKey(i, QByteArray::fromHex(\"FFFF\"));\n  }\n}\n\nQByteArray\nAnytoneCodeplug::EnhancedEncryptionKeyListElement::key(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return QByteArray();\n  return QByteArray::fromRawData((const char *)_data + Offset::keys() + n*Offset::betweenKeys(), 2);\n}\n\nvoid\nAnytoneCodeplug::EnhancedEncryptionKeyListElement::setKey(unsigned int n, const QByteArray &key) {\n  if ((n >= Limit::numEntries()) || (2 != key.size()))\n    return;\n  memcpy(_data + Offset::keys() + n*Offset::betweenKeys(), key.constData(), 2);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug::ContactMapElement\n * ********************************************************************************************* */\nAnytoneCodeplug::ContactMapElement::ContactMapElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneCodeplug::ContactMapElement::ContactMapElement(uint8_t *ptr)\n  : Element(ptr, ContactMapElement::size())\n{\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::ContactMapElement::clear() {\n  memset(_data, 0xff, _size);\n}\n\nbool\nAnytoneCodeplug::ContactMapElement::isValid() const {\n  return (0xffffffff!=getUInt32_le(Offset::id()))\n      && (0xffffffff!=getUInt32_le(Offset::index()));\n}\n\nbool\nAnytoneCodeplug::ContactMapElement::isGroup() const {\n  uint32_t tmp = getUInt32_le(Offset::id());\n  return tmp & 0x01;\n}\nunsigned\nAnytoneCodeplug::ContactMapElement::id() const {\n  uint32_t tmp = getUInt32_le(Offset::id());\n  tmp = tmp >> 1;\n  return decode_dmr_id_bcd_le((uint8_t *)&tmp);\n}\nvoid\nAnytoneCodeplug::ContactMapElement::setID(unsigned id, bool group) {\n  uint32_t tmp; encode_dmr_id_bcd_le((uint8_t *)&tmp, id);\n  tmp = ( (tmp << 1) | (group ? 1 : 0) );\n  setUInt32_le(Offset::id(), tmp);\n}\n\nunsigned\nAnytoneCodeplug::ContactMapElement::index() const {\n  return getUInt32_le(Offset::index());\n}\nvoid\nAnytoneCodeplug::ContactMapElement::setIndex(unsigned idx) {\n  setUInt32_le(Offset::index(), idx);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneCodeplug\n * ********************************************************************************************* */\nAnytoneCodeplug::AnytoneCodeplug(const QString &label, QObject *parent)\n  : Codeplug(parent), _label(label)\n{\n  // pass...\n}\n\nAnytoneCodeplug::~AnytoneCodeplug() {\n  // pass...\n}\n\nvoid\nAnytoneCodeplug::clear() {\n  while (this->numImages())\n    remImage(0);\n\n  addImage(_label);\n\n  // Allocate bitmaps\n  this->allocateBitmaps();\n}\n\nbool\nAnytoneCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // AM channels are indexed separately.\n  ctx.addTable(&AMChannel::staticMetaObject);\n\n  // Map radio IDs\n  for (int i=0; i<config->radioIDs()->count(); i++) {\n    if (config->radioIDs()->get(i)->is<DMRRadioID>())\n      ctx.add(config->radioIDs()->get(i)->as<DMRRadioID>(), i);\n  }\n\n  // Map digital and DTMF contacts\n  for (int i=0, d=0, a=0; i<config->contacts()->count(); i++) {\n    if (config->contacts()->contact(i)->is<DMRContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DMRContact>(), d); d++;\n    } else if (config->contacts()->contact(i)->is<DTMFContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DTMFContact>(), a); a++;\n    } else {\n      logInfo() << \"Cannot index contact '\" << config->contacts()->contact(i)->name()\n                << \"'. Contact type '\" << config->contacts()->contact(i)->metaObject()->className()\n                << \"' not supported by or implemented for AnyTone devices.\";\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i);\n\n  // Map channels\n  for (int i=0, common=0, am=0; i<config->channelList()->count(); i++) {\n    if (config->channelList()->channel(i)->is<DMRChannel>() ||\n        config->channelList()->channel(i)->is<FMChannel>()) {\n      ctx.add(config->channelList()->channel(i), common++);\n    } else if (config->channelList()->channel(i)->is<AMChannel>()) {\n      ctx.add(config->channelList()->channel(i), am++);\n    }\n  }\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i);\n\n  // Map DMR APRS systems\n  for (int i=0,a=0,d=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<DMRAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<DMRAPRSSystem>(), d); d++;\n    } else if (config->posSystems()->system(i)->is<FMAPRSSystem>()) {\n      auto *aprs = config->posSystems()->system(i)->as<FMAPRSSystem>();\n      ctx.add(aprs, a); a++;\n      // Index FM APRS frequencies (referenced in channel extensions).\n      if (auto *ext = aprs->anytoneExtension()) {\n        for (int j=0; j<ext->frequencies()->count(); j++) {\n          // Index 0 = default, so index is 1-based\n          ctx.add(ext->frequencies()->get(j)->as<AnytoneAPRSFrequency>(), j+1);\n        }\n      }\n    }\n  }\n\n  // Map roaming\n  for (int i=0; i<config->roamingZones()->count(); i++)\n    ctx.add(config->roamingZones()->zone(i), i);\n  for (int i=0; i<config->roamingChannels()->count(); i++)\n    ctx.add(config->roamingChannels()->channel(i), i);\n\n  // Map auto-repeater offsets\n  if (config->settings()->anytoneExtension()) {\n    auto *autoRep = config->settings()->anytoneExtension()->autoRepeaterSettings();\n    for (int i=0; i<autoRep->offsets()->count(); i++)\n      ctx.add(autoRep->offsets()->get(i)->as<AnytoneAutoRepeaterOffset>(), i);\n  }\n\n  // Map SMS templates\n  for (int i=0; i<config->smsExtension()->smsTemplates()->count(); i++)\n    ctx.add(config->smsExtension()->smsTemplates()->get(i)->as<SMSTemplate>(), i);\n\n  if (config->commercialExtension()) {\n    // Map all encryption keys\n    for (int i=0, basic=0, enhanced=0, aes=0; i<config->commercialExtension()->encryptionKeys()->count(); i++) {\n      auto key = config->commercialExtension()->encryptionKeys()->key(i);\n      if (key->is<BasicEncryptionKey>())\n        ctx.add(key->as<BasicEncryptionKey>(), basic++);\n      else if (key->is<ARC4EncryptionKey>())\n        ctx.add(key->as<ARC4EncryptionKey>(), enhanced++);\n      else if (key->is<AESEncryptionKey>())\n        ctx.add(key->as<AESEncryptionKey>(), aes++);\n    }\n  }\n\n  return true;\n}\n\n\nbool\nAnytoneCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! this->createElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode AnyTone codeplug: Creation of config objects failed.\";\n    return false;\n  }\n\n  if (! this->linkElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode AnyTone codeplug: Linking of config objects failed.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nConfig *\nAnytoneCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot pre-process codeplug for anytone device.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(intermediate, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Split multi-VFO zones.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nAnytoneCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process codeplug for anytone device.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot post-process codeplug for anytone device.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nAnytoneCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  Context ctx(config);\n  // Register table for auto-repeater offsets\n  ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject);\n  // Register table for FM APRS frequencies\n  ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject);\n\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Cannot encode anytone codeplug.\";\n    return false;\n  }\n\n  // If codeplug is generated from scratch -> clear and reallocate\n  if (! flags.updateCodeplug()) {\n    // Clear codeplug\n    this->clear();\n    // Then allocate elements\n    this->allocateUpdated();\n  }\n\n  // First set bitmaps\n  this->setBitmaps(ctx);\n  // Allocate all memory elements representing the common config\n  this->allocateForEncoding();\n\n  // Then encode everything.\n  return this->encodeElements(flags, ctx, err);\n}\n\nbool\nAnytoneCodeplug::decode(Config *config, const ErrorStack &err) {\n  // Maps code-plug indices to objects\n  Context ctx(config);\n\n  // Register table for auto-repeater offsets\n  ctx.addTable(&AnytoneAutoRepeaterOffset::staticMetaObject);\n  // Register table for FM APRS frequencies\n  ctx.addTable(&AnytoneAPRSFrequency::staticMetaObject);\n\n  return this->decodeElements(ctx, err);\n}\n"
  },
  {
    "path": "lib/anytone_codeplug.hh",
    "content": "#ifndef ANYTONECODEPLUG_HH\n#define ANYTONECODEPLUG_HH\n\n#include \"anytone_settingsextension.hh\"\n#include \"bootsettings.hh\"\n#include \"channel.hh\"\n#include \"codeplug.hh\"\n#include \"contact.hh\"\n#include <QGeoCoordinate>\n\nclass RadioSettings;\n\n\n/** Base class interface for all Anytone radio codeplugs.\n *\n * This class extends the generic @c CodePlug to provide an interface to the multi-step up and\n * download of the binary codeplug. In contrast to the majority of radios, the Anytone codeplugs\n * are heavily segmented and only valid sections are read from a written to the device.\n *\n * @ingroup anytone */\nclass AnytoneCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements encoding of CTCSS tones. */\n  struct CTCSS {\n  public:\n    /** Encodes Signaling::Code CTCSS tones. */\n    static uint8_t encode(const SelectiveCall &tone);\n    /** Decodes to Signaling::Code CTCSS tones. */\n    static SelectiveCall decode(uint8_t code);\n\n  protected:\n    /** Translation table. */\n    static SelectiveCall _codeTable[52];\n  };\n\n  /** Represents the base class for inverted bytemaps in all AnyTone codeplugs.\n   * This is obviously a result of a lazy firmware developer. There is already some code in the\n   * firmware to handle bitmaps. The developer, however, copied some BS code from elsewhere. It is\n   * inverted, because erased flash memory is usually initialized with 0xff. However, the AnyTone\n   * memory gets erased to 0x00. So the inversion is not necessary. Someone really took pride in\n   * his/her work and consequently, I need to implement some BS elements more. */\n  class InvertedBytemapElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    InvertedBytemapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Clears the bitmap, disables all channels. */\n    void clear();\n\n    /** Returns @c true if the given index is valid. */\n    virtual bool isEncoded(unsigned int idx) const ;\n    /** Enables/disables the specified index. */\n    virtual void setEncoded(unsigned int idx, bool enable);\n    /** Enables the first n elements. */\n    virtual void enableFirst(unsigned int n);\n  };\n\n  /** Represents the base class for channel encodings in all AnyTone codeplugs.\n   *\n   * Memory layout of encoded channel (0x40 bytes):\n   * @verbinclude anytone_channel.txt\n   */\n  class ChannelElement: public Element\n  {\n  public:\n    /** Defines all possible channel modes, see @c channelMode. */\n    enum class Mode {\n      Analog      = 0,               ///< Analog channel.\n      Digital     = 1,               ///< Digital (DMR) channel.\n      MixedAnalog = 2,               ///< Mixed, analog channel with digital RX.\n      MixedDigital = 3               ///< Mixed, digital channel with analog RX.\n    };\n\n    /** Defines all possible power settings.*/\n    enum Power {\n      POWER_LOW    = 0,              ///< Low power, usually 1W.\n      POWER_MIDDLE = 1,              ///< Medium power, usually 2.5W.\n      POWER_HIGH   = 2,              ///< High power, usually 5W.\n      POWER_TURBO  = 3               ///< Higher power, usually 7W on VHF and 6W on UHF.\n    };\n\n    /** Defines all possible repeater modes. */\n    enum class RepeaterMode {\n      Simplex  = 0,                  ///< Simplex mode, that is TX frequency = RX frequency. @c tx_offset is ignored.\n      Positive = 1,                  ///< Repeater mode with positive @c tx_offset.\n      Negative = 2                   ///< Repeater mode with negative @c tx_offset.\n    };\n\n    /** Possible analog signaling modes. */\n    enum class SignalingMode {\n      None = 0,                      ///< None.\n      CTCSS = 1,                     ///< Use CTCSS tones\n      DCS = 2                        ///< Use DCS codes.\n    };\n\n    /** Defines possible admit criteria. */\n    enum class Admit {\n      Always = 0,                  ///< For both channel types.\n      Free = 1,                    ///< For digital channels.\n      DifferentColorCode = 2,      ///< For digital channels.\n      SameColorCode = 3,               ///< For digital channels.\n      Tone = 1,                    ///< For analog channels\n      Busy = 2                     ///< For analog channels.\n    };\n\n    /** Defines all possible optional signalling settings. */\n    enum class OptSignaling {\n      Off = 0,                    ///< None.\n      DTMF = 1,                   ///< Use DTMF.\n      TwoTone = 2,                ///< Use 2-tone.\n      FiveTone = 3                ///< Use 5-tone.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelElement();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    /** Resets the channel. */\n    void clear();\n\n    /** Returns the RX frequency in Hz. */\n    virtual unsigned rxFrequency() const;\n    /** Sets the RX frequency in Hz. */\n    virtual void setRXFrequency(unsigned hz);\n\n    /** Returns the TX frequency offset in Hz.\n     * This method returns an unsigned value, the sign of the offset frequency is stored in\n     * @c repeaterMode(). */\n    virtual unsigned txOffset() const;\n    /** Sets the TX frequency offset in Hz.\n     * This method accepts unsigned values, the sign of the offset frequency is stored in\n     * @c repeaterMode(). */\n    virtual void setTXOffset(unsigned hz);\n    /** Returns the TX frequency in Hz. */\n    virtual unsigned txFrequency() const;\n    /** Sets the TX frequency indirectly. That is, relative to the RX frequency which must be set\n     * first. This method also updates the @c repeaterMode. */\n    virtual void setTXFrequency(unsigned hz);\n\n    /** Returns the channel mode (analog, digtital, etc). */\n    virtual Mode mode() const;\n    /** Sets the channel mode. */\n    virtual void setMode(Mode mode);\n\n    /** Returns the channel power. */\n    virtual Channel::Power power() const;\n    /** Sets the channel power. */\n    virtual void setPower(Channel::Power power);\n\n    /** Returns the band width of the channel. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the band width of the channel. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns the transmit offset direction. */\n    virtual RepeaterMode repeaterMode() const;\n    /** Sets the transmit offset direction. */\n    virtual void setRepeaterMode(RepeaterMode mode);\n\n    /** Returns the RX signaling mode */\n    virtual SignalingMode rxSignalingMode() const;\n    /** Sets the RX signaling mode */\n    virtual void setRXSignalingMode(SignalingMode mode);\n    /** Simplified access to RX signaling (tone). */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the RX signaling (tone). */\n    virtual void setRXTone(const SelectiveCall &code);\n\n    /** Returns the TX signaling mode */\n    virtual SignalingMode txSignalingMode() const;\n    /** Sets the TX signaling mode */\n    virtual void setTXSignalingMode(SignalingMode mode);\n    /** Simplified access to TX signaling (tone). */\n    virtual SelectiveCall txTone() const;\n    /** Sets the RX signaling (tone). */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns @c true if RX and TX frequencies are swapped. */\n    virtual bool rxTxSwapped() const;\n    /** Swaps RX and TX frequencies. */\n    virtual void enableSwapRxTx(bool enable);\n\n    /** Returns @c true if the RX only is enabled. */\n    virtual bool rxOnly() const;\n    /** Enables/disables RX only. */\n    virtual void enableRXOnly(bool enable);\n    /** Returns @c true if the call confirm is enabled. */\n    virtual bool callConfirm() const;\n    /** Enables/disables call confirm. */\n    virtual void enableCallConfirm(bool enable);\n    /** Returns @c true if the talkaround is enabled. */\n    virtual bool talkaround() const;\n    /** Enables/disables talkaround. */\n    virtual void enableTalkaround(bool enable);\n\n    /** Returns @c true if the TX CTCSS tone frequency is custom (non standard). */\n    virtual bool txCTCSSIsCustom() const;\n    /** Returns the TX CTCSS tone. */\n    virtual SelectiveCall txCTCSS() const;\n    /** Sets the TX CTCSS tone. */\n    virtual void setTXCTCSS(const SelectiveCall &tone);\n    /** Enables TX custom CTCSS frequency. */\n    virtual void enableTXCustomCTCSS();\n    /** Returns @c true if the RX CTCSS tone frequency is custom (non standard). */\n    virtual bool rxCTCSSIsCustom() const;\n    /** Returns the RX CTCSS tone. */\n    virtual SelectiveCall rxCTCSS() const;\n    /** Sets the RX CTCSS tone. */\n    virtual void setRXCTCSS(const SelectiveCall &tone);\n    /** Enables RX custom CTCSS frequency. */\n    virtual void enableRXCustomCTCSS();\n\n    /** Returns the TX DCS code. */\n    virtual SelectiveCall txDCS() const;\n    /** Sets the TX DCS code. */\n    virtual void setTXDCS(const SelectiveCall &code);\n    /** Returns the RX DCS code. */\n    virtual SelectiveCall rxDCS() const;\n    /** Sets the RX DCS code. */\n    virtual void setRXDCS(const SelectiveCall &code);\n\n    /** Returns the custom CTCSS frequency in Hz. */\n    virtual double customCTCSSFrequency() const;\n    /** Sets the custom CTCSS frequency in Hz. */\n    virtual void setCustomCTCSSFrequency(double hz);\n\n    /** Returns the 2-tone decode index (0-based). */\n    virtual unsigned twoToneDecodeIndex() const;\n    /** Sets the 2-tone decode index (0-based). */\n    virtual void setTwoToneDecodeIndex(unsigned idx);\n\n    /** Returns the transmit contact index (0-based). */\n    virtual unsigned contactIndex() const;\n    /** Sets the transmit contact index (0-based). */\n    virtual void setContactIndex(unsigned idx);\n\n    /** Returns the radio ID index (0-based). */\n    virtual unsigned radioIDIndex() const;\n    /** Sets the radio ID index (0-based). */\n    virtual void setRadioIDIndex(unsigned idx);\n\n    /** Returns @c true if the sequelch is silent and @c false if open. */\n    virtual AnytoneFMChannelExtension::SquelchMode squelchMode() const;\n    /** Enables/disables silent squelch. */\n    virtual void setSquelchMode(AnytoneFMChannelExtension::SquelchMode mode);\n\n    /** Returns the admit criterion. */\n    virtual Admit admit() const;\n    /** Sets the admit criterion. */\n    virtual void setAdmit(Admit admit);\n\n    /** Returns the optional signalling type. */\n    virtual OptSignaling optionalSignaling() const;\n    /** Sets the optional signaling type. */\n    virtual void setOptionalSignaling(OptSignaling sig);\n\n    /** Returns @c true, if a scan list index is set. */\n    virtual bool hasScanListIndex() const;\n    /** Returns the scan list index (0-based). */\n    virtual unsigned scanListIndex() const;\n    /** Sets the scan list index (0-based). */\n    virtual void setScanListIndex(unsigned idx);\n    /** Clears the scan list index. */\n    virtual void clearScanListIndex();\n\n    /** Returns @c true, if a group list index is set. */\n    virtual bool hasGroupListIndex() const;\n    /** Returns the scan list index (0-based). */\n    virtual unsigned groupListIndex() const;\n    /** Sets the group list index (0-based). */\n    virtual void setGroupListIndex(unsigned idx);\n    /** Clears the group list index. */\n    virtual void clearGroupListIndex();\n\n    /** Returns the two-tone ID index (0-based). */\n    virtual unsigned twoToneIDIndex() const;\n    /** Sets the two-tone ID index (0-based). */\n    virtual void setTwoToneIDIndex(unsigned idx);\n    /** Returns the five-tone ID index (0-based). */\n    virtual unsigned fiveToneIDIndex() const;\n    /** Sets the five-tone ID index (0-based). */\n    virtual void setFiveToneIDIndex(unsigned idx);\n    /** Returns the DTFM ID index (0-based). */\n    virtual unsigned dtmfIDIndex() const;\n    /** Sets the DTMF ID index (0-based). */\n    virtual void setDTMFIDIndex(unsigned idx);\n\n    /** Returns the color code. */\n    virtual unsigned colorCode() const;\n    /** Sets the color code. */\n    virtual void setColorCode(unsigned code);\n\n    /** Returns the time slot. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns @c true if SMS confirmation is enabled. */\n    virtual bool smsConfirm() const;\n    /** Enables/disables SMS confirmation. */\n    virtual void enableSMSConfirm(bool enable);\n    /** Returns @c true if simplex TDMA is enabled. */\n    virtual bool simplexTDMA() const;\n    /** Enables/disables simplex TDMA confirmation. */\n    virtual void enableSimplexTDMA(bool enable);\n    /** Returns @c true if adaptive TDMA is enabled. */\n    virtual bool adaptiveTDMA() const;\n    /** Enables/disables adaptive TDMA. */\n    virtual void enableAdaptiveTDMA(bool enable);\n    /** Returns @c true if RX APRS is enabled. */\n    virtual bool rxAPRS() const;\n    /** Enables/disables RX APRS. */\n    virtual void enableRXAPRS(bool enable);\n    /** Returns @c true if lone worker is enabled. */\n    virtual bool loneWorker() const;\n    /** Enables/disables lone worker. */\n    virtual void enableLoneWorker(bool enable);\n\n    /** Returns the channel name. */\n    virtual QString name() const;\n    /** Sets the channel name. */\n    virtual void setName(const QString &name);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(Context &ctx) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx);\n\n  public:\n    /** Some limits for the channel element. */\n    struct Limit: Element::Limit {\n      /// Maximum name length.\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Internal used offsets within the channel element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int rxFrequency()       { return 0x0000; }\n      static constexpr unsigned int txFrequencyOffset() { return 0x0004; }\n      static constexpr Bit channelMode()                { return {0x0008, 0}; }\n      static constexpr Bit power()                      { return {0x0008, 2}; }\n      static constexpr Bit bandwidth()                  { return {0x0008, 4}; }\n      static constexpr Bit repeaterMode()               { return {0x0008, 6}; }\n      static constexpr Bit rxSignalingMode()            { return {0x0009, 0}; }\n      static constexpr Bit txSignalingMode()            { return {0x0009, 2}; }\n      static constexpr Bit swapRXTX()                   { return {0x0009, 4}; }\n      static constexpr Bit rxOnly()                     { return {0x0009, 5}; }\n      static constexpr Bit callConfirm()                { return {0x0009, 6}; }\n      static constexpr Bit talkaround()                 { return {0x0009, 7}; }\n      static constexpr unsigned int txCTCSS()           { return 0x000a; }\n      static constexpr unsigned int rxCTCSS()           { return 0x000b; }\n      static constexpr unsigned int txDCS()             { return 0x000c; }\n      static constexpr unsigned int rxDCS()             { return 0x000e; }\n      static constexpr unsigned int customCTCSS()       { return 0x0010; }\n      static constexpr unsigned int twoFunctionIndex()  { return 0x0012; }\n      static constexpr unsigned int contactIndex()      { return 0x0014; }\n      static constexpr unsigned int radioIdIndex()      { return 0x0018; }\n      static constexpr Bit squelchMode()                { return {0x0019, 4}; }\n      static constexpr Bit admitCriterion()             { return {0x001a, 0}; }\n      static constexpr Bit optionalSingnaling()         { return {0x001a, 4}; }\n      static constexpr unsigned int scanListIndex()     { return 0x001b; }\n      static constexpr unsigned int groupListIndex()    { return 0x001c; }\n      static constexpr unsigned int twoToneIDIndex()    { return 0x001d; }\n      static constexpr unsigned int fiveToneIDIndex()   { return 0x001e; }\n      static constexpr unsigned int dtmfIDIndex()       { return 0x001f; }\n      static constexpr unsigned int colorCode()         { return 0x0020; }\n      static constexpr Bit timeSlot()                   { return {0x0021, 0}; }\n      static constexpr Bit smsConfirm()                 { return {0x0021, 1}; }\n      static constexpr Bit simplexTDMA()                { return {0x0021, 2}; }\n      static constexpr Bit adaptiveTDMA()               { return {0x0021, 4}; }\n      static constexpr Bit rxAPRS()                     { return {0x0021, 5}; }\n      static constexpr Bit loneWorker()                 { return {0x0021, 7}; }\n      static constexpr unsigned int name()              { return 0x0023; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the channel bitmaps in all AnyTone codeplugs. */\n  class ChannelBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ChannelBitmapElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0200; }\n  };\n\n\n  /** Represents the base class for contacts in all AnyTone codeplugs.\n   *\n   * Memory layout of encoded contact (0x64 bytes):\n   * @verbinclude anytone_contact.txt\n   */\n  class ContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ContactElement();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0064; }\n\n    /** Resets the contact element. */\n    void clear();\n    /** Returns @c true if the element is valid. */\n    bool isValid() const;\n\n    /** Returns the contact type. */\n    virtual DMRContact::Type type() const;\n    /** Sets the contact type. */\n    virtual void setType(DMRContact::Type type);\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Returns the contact number. */\n    virtual unsigned number() const;\n    /** Sets the contact number. */\n    virtual void setNumber(unsigned number);\n\n    /** Returns the alert type. */\n    virtual AnytoneContactExtension::AlertType alertType() const;\n    /** Sets the alert type. */\n    virtual void setAlertType(AnytoneContactExtension::AlertType type);\n\n    /** Assembles a @c DigitalContact from this contact. */\n    virtual DMRContact *toContactObj(Context &ctx) const;\n    /** Constructs this contact from the give @c DigitalContact. */\n    virtual bool fromContactObj(const DMRContact *contact, Context &ctx);\n\n  public:\n    /** Some limits for the contact. */\n    struct Limit: public Element::Limit {\n      /// Maximum name length.\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int type()         { return 0x0000; }\n      static constexpr unsigned int name()         { return 0x0001; }\n      static constexpr unsigned int number()       { return 0x0023; }\n      static constexpr unsigned int alertType()    { return 0x0027; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the contact bitmaps in all AnyTone codeplugs. */\n  class ContactBitmapElement: public InvertedBitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactBitmapElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0500; }\n  };\n\n\n  /** Represents the base class for analog (DTMF) contacts in all AnyTone codeplugs.\n   *\n   * Encoding of the DTMF contact (0x30 bytes):\n   * @verbinclude anytone_dtmfcontact.txt */\n  class DTMFContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit DTMFContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~DTMFContactElement();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0018; }\n\n    /** Resets the contact element. */\n    void clear();\n\n    /** Returns the number of the contact. */\n    virtual QString number() const;\n    /** Sets the number of the contact. */\n    virtual void setNumber(const QString &number);\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Creates an DTMF contact from the entry. */\n    virtual DTMFContact *toContact() const;\n    /** Encodes an DTMF contact from the given one. */\n    virtual bool fromContact(const DTMFContact *contact);\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      static constexpr unsigned int digitCount() { return 14; }       ///< The max number of digits.\n      static constexpr unsigned int nameLength() { return 15; }       ///< Maximum name length.\n    };\n\n  protected:\n    /** Internal used offsets within the codeplug. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int digits()    { return 0x0000; }\n      static constexpr unsigned int numDigits() { return 0x0007; }\n      static constexpr unsigned int name()      { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the DTMF contact byte map, indicating which contacts are valid. */\n  class DTMFContactBytemapElement: public InvertedBytemapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFContactBytemapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit DTMFContactBytemapElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n  };\n\n\n  /** Represents the base class for group lists in all AnyTone codeplugs.\n   *\n   * Encoding of a group list (0x120 bytes):\n   * @verbinclude anytone_grouplist.txt */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0120; }\n\n    /** Clears the group list. */\n    void clear();\n    /** Returns @c true if the group list is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the group list. */\n    virtual QString name() const;\n    /** Sets the name of the group list. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if the n-th member index is valid. */\n    virtual bool hasMemberIndex(unsigned n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned memberIndex(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMemberIndex(unsigned n, unsigned idx);\n    /** Clears the n-th member index. */\n    virtual void clearMemberIndex(unsigned n);\n\n    /** Constructs a new @c RXGroupList from this group list.\n     * None of the members are added yet. Call @c linkGroupList\n     * to do that. */\n    virtual RXGroupList *toGroupListObj() const;\n    /** Populates the @c RXGroupList from this group list. The CodeplugContext\n     * is used to map the member indices. */\n    virtual bool linkGroupList(RXGroupList *lst, Context &ctx) const;\n    /** Constructs this group list from the given @c RXGroupList. */\n    virtual bool fromGroupListObj(const RXGroupList *lst, Context &ctx);\n\n  public:\n    /** Some limits for the group list. */\n    struct Limit: public Element::Limit {\n      /** Maximum number of members. */\n      static constexpr unsigned int members() { return 64; }\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members()          { return 0x0000; }\n      static constexpr unsigned int betweenMembers()   { return 0x0004; }\n      static constexpr unsigned int name()             { return 0x0100; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which group list element is valid. */\n  class GroupListBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit GroupListBitmapElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n\n  /** Represents the base class for scan lists in all AnyTone codeplugs.\n   *\n   * Memory layout of encoded scanlist (0x90 bytes):\n   * @verbinclude anytone_scanlist.txt */\n  class ScanListElement: public Element\n  {\n  public:\n    /** Defines all possible priority channel selections. */\n    enum class PriChannel {\n      Off = 0,                   ///< Off.\n      Primary = 1,               ///< Priority Channel Select 1.\n      Secondary = 2,             ///< Priority Channel Select 2.\n      Both = 3                   ///< Priority Channel Select 1 + Priority Channel Select 2.\n    };\n\n    /** Defines all possible reply channel selections. */\n    enum class RevertChannel {\n      Selected = 0,              ///< Selected channel.\n      SelectedActive = 1,        ///< Selected + active channel.\n      Primary = 2,               ///< Primary channel.\n      Secondary = 3,             ///< Secondary channel.\n      LastCalled = 4,            ///< Last Called.\n      LastUsed = 5,              ///< Last Used.\n      PrimaryActive = 6,         ///< Primary + active channel.\n      SecondaryActive = 7        ///< Secondary + active channel.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ScanListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n\n    /** Resets the scan list. */\n    void clear();\n\n    /** Returns the priority channel selection. */\n    virtual PriChannel priorityChannels() const;\n    /** Sets the priority channel selection. */\n    virtual void setPriorityChannels(PriChannel sel);\n\n    /** Returns @c true if the primary channel is set. */\n    virtual bool hasPrimary() const;\n    /** Returns @c true if the primary channel is set to the selected channel. */\n    virtual bool primaryIsSelected() const;\n    /** Returns the primary channel index. */\n    virtual unsigned primary() const;\n    /** Sets the primary channel index. */\n    virtual void setPrimary(unsigned idx);\n    /** Sets the primary channel to be selected channel. */\n    virtual void setPrimarySelected();\n    /** Clears the primary channel index. */\n    virtual void clearPrimaryChannel();\n\n    /** Returns @c true if the secondary channel is set. */\n    virtual bool hasSecondary() const;\n    /** Returns @c true if the secondary channel is set to the selected channel. */\n    virtual bool secondaryIsSelected() const;\n    /** Returns the secondary channel index. */\n    virtual unsigned secondary() const;\n    /** Sets the secondary channel index. */\n    virtual void setSecondary(unsigned idx);\n    /** Sets the secondary channel to be selected channel. */\n    virtual void setSecondarySelected();\n    /** Clears the secondary channel index. */\n    virtual void clearSecondaryChannel();\n\n    /** Returns the look back time A in seconds. */\n    virtual Interval lookBackTimeA() const;\n    /** Sets the look back time A in seconds. */\n    virtual void setLookBackTimeA(const Interval &sec);\n    /** Returns the look back time B in seconds. */\n    virtual Interval lookBackTimeB() const;\n    /** Sets the look back time B in seconds. */\n    virtual void setLookBackTimeB(const Interval& sec);\n    /** Returns the drop out delay in seconds. */\n    virtual Interval dropOutDelay() const;\n    /** Sets the drop out delay in seconds. */\n    virtual void setDropOutDelay(const Interval& sec);\n    /** Returns the dwell time in seconds. */\n    virtual Interval dwellTime() const;\n    /** Sets the dwell time in seconds. */\n    virtual void setDwellTime(const Interval& sec);\n\n    /** Returns the revert channel type. */\n    virtual RevertChannel revertChannel() const;\n    /** Sets the revert channel type. */\n    virtual void setRevertChannel(RevertChannel type);\n\n    /** Returns the name of the scan list. */\n    virtual QString name() const;\n    /** Sets the name of the scan list. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if the n-th member index is set. */\n    virtual bool hasMemberIndex(unsigned n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned memberIndex(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMemberIndex(unsigned n, unsigned idx);\n    /** Clears the n-th member index. */\n    virtual void clearMemberIndex(unsigned n);\n\n    /** Constructs a ScanList object from this definition. This only sets the properties of\n     * the scan list. To associate all members with the scan list object, call @c linkScanListObj. */\n    virtual ScanList *toScanListObj() const;\n    /** Links all channels (members and primary channels) with the given scan-list object. */\n    virtual bool linkScanListObj(ScanList *lst, Context &ctx) const;\n    /** Constructs the binary representation from the give config. */\n    virtual bool fromScanListObj(ScanList *lst, Context &ctx);\n\n  public:\n    /** Some limits for the scan list. */\n    struct Limit: public Element::Limit {\n      /// Maximum number of members.\n      static constexpr unsigned int members() { return 50; }\n      /// Maximum name length.\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int priorityChannel()              { return 0x0001; }\n      static constexpr unsigned int primaryChannel()               { return 0x0002; }\n      static constexpr unsigned int secondaryChannel()             { return 0x0004; }\n      static constexpr unsigned int lookBackTimeA()                { return 0x0006; }\n      static constexpr unsigned int lookBackTimeB()                { return 0x0008; }\n      static constexpr unsigned int dropOutDelay()                 { return 0x000a; }\n      static constexpr unsigned int dwellTime()                    { return 0x000c; }\n      static constexpr unsigned int revertChannel()                { return 0x000e; }\n      static constexpr unsigned int name()                         { return 0x000f; }\n      static constexpr unsigned int members()                      { return 0x0020; }\n      static constexpr unsigned int betweenMembers()               { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which scanlist elements are valid. */\n  class ScanListBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00000020; }\n  };\n\n\n  /** Represents the base class for radio IDs in all AnyTone codeplugs.\n   *\n   * Memory layout of encoded scanlist (0x20 bytes):\n   * @verbinclude anytone_radioid.txt */\n  class RadioIDElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    RadioIDElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    RadioIDElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the radio ID. */\n    void clear();\n\n    /** Returns the number of the radio ID. */\n    virtual unsigned number() const;\n    /** Sets the number of the radio ID. */\n    virtual void setNumber(unsigned number);\n\n    /** Returns the name of the radio ID. */\n    virtual QString name() const;\n    /** Sets the name of the radio ID. */\n    virtual void setName(const QString &name);\n\n    /** Encodes the given RadioID. */\n    virtual bool fromRadioID(DMRRadioID *id);\n    /** Constructs a new radio id. */\n    virtual DMRRadioID *toRadioID() const;\n\n  public:\n    /** Some limits for the radio ID element. */\n    struct Limit: public Element::Limit {\n      /// Maximum name length\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int number() { return 0x0000; }\n      static constexpr unsigned int name()   { return 0x0005; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which radio IDs are valid. */\n  class RadioIDBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    RadioIDBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    RadioIDBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n\n  /** Encodes the primary radio ID setting. */\n  class PrimaryRadioIdElement: public Element\n  {\n  public:\n    /** Constructor. */\n    explicit PrimaryRadioIdElement(uint8_t *ptr);\n\n    static constexpr unsigned int size() { return 0x0020; }\n\n    void clear() override;\n    bool isValid() const override;\n\n    /** Returns the DMR id. */\n    virtual unsigned int number() const;\n    /** Sets the DMR id. */\n    virtual void setNumber(unsigned int number);\n\n    /** Returns the primary radio name. */\n    virtual QString name() const;\n    /** Sets the primary radio name. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if the id is enabled. That is, overrides all other DMR ids. */\n    virtual bool enabled() const;\n    /** Enables/disables primary DMR id. */\n    virtual void enable(bool enable);\n\n    /** Encodes default DMR id as primary DMR id. */\n    virtual bool encode(const Flags &flags, Context &ctx, const ErrorStack &err);\n    /** Decodes primary DMR id. Actually does nothing.\n     * The decoding is delayed to the linking stage. */\n    virtual bool decode(Context &ctx, const ErrorStack &err);\n    /** Links primary DMR id as default DMR id. */\n    virtual bool link(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits for the primary DMR id. */\n    struct Limit: Element::Limit {\n      /// Maximum name length.\n      static constexpr unsigned int name() { return 26; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset  {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int id() { return 0x0000; }\n      static constexpr unsigned int enabled() { return 0x0004; }\n      static constexpr unsigned int name() { return 0x0005; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the base class for the settings elements in all AnyTone codeplugs.\n   * This class only implements those few settings, common to all devices and encoded the same way.\n   * It also defines all common settings as interfaces.\n   *\n   * Memory layout of encoded general settings (0xd0 bytes):\n   * @verbinclude anytone_generalsettings.txt\n   */\n  class GeneralSettingsElement: public Element\n  {\n  public:\n    /** Possible automatic shutdown delays. */\n    enum class AutoShutdown {\n      Off = 0, After10min = 1, After30min  = 2, After60min  = 3, After120min = 4,\n    };\n    /** Possible encoding of boot display settings. */\n    enum class BootDisplay {\n      Default = 0, CustomText = 1, CustomImage = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Resets the general settings. */\n    void clear();\n\n    /** Returns @c true, if the key tone is enabled. */\n    virtual bool keyToneEnabled() const = 0;\n    /** Enables/disables the key-tone. */\n    virtual void enableKeyTone(bool enable) = 0;\n\n    /** Returns @c true if the radio displays frequencies instead of channels is enabled. */\n    virtual bool displayFrequency() const;\n    /** Enables/disables the frequency display. */\n    virtual void enableDisplayFrequency(bool enable);\n    /** Returns @c true if auto key-lock is enabled. */\n    virtual bool autoKeyLock() const;\n    /** Enables/disables auto key-lock. */\n    virtual void enableAutoKeyLock(bool enable);\n    /** Returns the auto-shutdown delay in minutes. */\n    virtual Interval autoShutdownDelay() const;\n    /** Sets the auto-shutdown delay in minutes. */\n    virtual void setAutoShutdownDelay(Interval min);\n    /** Returns the boot display mode. */\n    virtual BootSettings::BootDisplay bootDisplay() const;\n    /** Sets the boot display mode. */\n    virtual void setBootDisplay(BootSettings::BootDisplay mode);\n    /** Returns @c true if boot password is enabled. */\n    virtual bool bootPassword() const;\n    /** Enables/disables boot password. */\n    virtual void enableBootPassword(bool enable);\n    /** Squelch level of VFO A, (0=off). */\n    virtual Level squelchLevelA() const;\n    /** Returns the squelch level for VFO A, (0=off). */\n    virtual void setSquelchLevelA(Level level);\n    /** Squelch level of VFO B, (0=off). */\n    virtual Level squelchLevelB() const;\n    /** Returns the squelch level for VFO B, (0=off). */\n    virtual void setSquelchLevelB(Level level);\n\n    /** Returns the VFO scan type. */\n    virtual AnytoneSettingsExtension::VFOScanType vfoScanType() const = 0;\n    /** Sets the VFO scan type. */\n    virtual void setVFOScanType(AnytoneSettingsExtension::VFOScanType type) = 0;\n    /** Returns the mirophone gain. */\n    virtual Level dmrMicGain() const = 0;\n    /** Sets the microphone gain. */\n    virtual void setDMRMicGain(Level gain) = 0;\n\n    /** Returns the key function for a short press on the function key 1/A. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const = 0;\n    /** Sets the key function for a short press on the function key 1/A. */\n    virtual void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a short press on the function key 2/B. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const = 0;\n    /** Sets the key function for a short press on the function key 2/B. */\n    virtual void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a short press on the function key 3/C. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const = 0;\n    /** Sets the key function for a short press on the function key 3/C. */\n    virtual void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a short press on the function key 1. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const = 0;\n    /** Sets the key function for a short press on the function key 1. */\n    virtual void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a short press on the function key 2. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const = 0;\n    /** Sets the key function for a short press on the function key 2. */\n    virtual void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n\n    /** Returns the key function for a long press on the function key 1. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const = 0;\n    /** Sets the key function for a long press on the function key 1. */\n    virtual void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a long press on the function key 2. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const = 0;\n    /** Sets the key function for a long press on the function key 2. */\n    virtual void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a long press on the function key 3. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const = 0;\n    /** Sets the key function for a long press on the function key 3. */\n    virtual void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a long press on the function key 1. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const = 0;\n    /** Sets the key function for a long press on the function key 1. */\n    virtual void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n    /** Returns the key function for a long press on the function key 2. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const = 0;\n    /** Sets the key function for a long press on the function key 2. */\n    virtual void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) = 0;\n\n    /** Returns the long-press duration in ms. */\n    virtual Interval longPressDuration() const = 0;\n    /** Sets the long-press duration in ms. */\n    virtual void setLongPressDuration(Interval ms) = 0;\n\n    /** Returns @c true if the knob is locked. */\n    virtual bool knobLock() const = 0;\n    /** Enables/disables the knob lock. */\n    virtual void enableKnobLock(bool enable) = 0;\n    /** Returns @c true if the keypad is locked. */\n    virtual bool keypadLock() const = 0;\n    /** Enables/disables the keypad lock. */\n    virtual void enableKeypadLock(bool enable) = 0;\n    /** Returns @c true if the sidekeys are locked. */\n    virtual bool sidekeysLock() const = 0;\n    /** Enables/disables the sidekeys lock. */\n    virtual void enableSidekeysLock(bool enable) = 0;\n    /** Returns @c true if the \"professional\" key is locked. */\n    virtual bool keyLockForced() const = 0;\n    /** Enables/disables the \"professional\" key lock. */\n    virtual void enableKeyLockForced(bool enable) = 0;\n\n  public:\n    /** Returns @c true if the VFO A is in VFO mode. */\n    virtual bool vfoModeA() const = 0;\n    /** Enables/disables VFO mode for VFO A. */\n    virtual void enableVFOModeA(bool enable) = 0;\n    /** Returns @c true if the VFO B is in VFO mode. */\n    virtual bool vfoModeB() const = 0;\n    /** Enables/disables VFO mode for VFO B. */\n    virtual void enableVFOModeB(bool enable) = 0;\n\n    /** Returns the memory zone for VFO A. */\n    virtual unsigned memoryZoneA() const = 0;\n    /** Sets the memory zone for VFO A. */\n    virtual void setMemoryZoneA(unsigned zone) = 0;\n    /** Returns the memory zone for VFO B. */\n    virtual unsigned memoryZoneB() const = 0;\n    /** Sets the memory zone for VFO B. */\n    virtual void setMemoryZoneB(unsigned zone) = 0;\n\n    /** Returns @c true if recording is enabled. */\n    virtual bool recording() const = 0;\n    /** Enables/disables recording. */\n    virtual void enableRecording(bool enable) = 0;\n\n    /** Returns the display brightness. */\n    virtual unsigned brightness() const = 0;\n    /** Sets the display brightness. */\n    virtual void setBrightness(unsigned level) = 0;\n\n    /** Returns @c true if GPS is enabled. */\n    virtual bool gps() const = 0;\n    /** Enables/disables recording. */\n    virtual void enableGPS(bool enable) = 0;\n    /** Returns @c true if SMS alert is enabled. */\n    virtual bool smsAlert() const = 0;\n    /** Enables/disables SMS alert. */\n    virtual void enableSMSAlert(bool enable) = 0;\n    /** Returns @c true if the active channel is VFO B. */\n    virtual bool activeChannelB() const = 0;\n    /** Enables/disables VFO B as the active channel. */\n    virtual void enableActiveChannelB(bool enable) = 0;\n    /** Returns @c true if sub channel is enabled. */\n    virtual bool subChannel() const = 0;\n    /** Enables/disables sub channel. */\n    virtual void enableSubChannel(bool enable) = 0;\n    /** Returns @c true if call alert is enabled. */\n    virtual bool callAlert() const = 0;\n    /** Enables/disables call alert. */\n    virtual void enableCallAlert(bool enable) = 0;\n\n    /** Returns the GPS time zone. */\n    virtual QTimeZone gpsTimeZone() const = 0;\n    /** Sets the GPS time zone. */\n    virtual void setGPSTimeZone(const QTimeZone &zone) = 0;\n    /** Returns @c true if the talk permit tone is enabled for digital channels. */\n    virtual bool dmrTalkPermit() const = 0;\n    /** Returns @c true if the talk permit tone is enabled for digital channels. */\n    virtual bool fmTalkPermit() const = 0;\n    /** Enables/disables the talk permit tone for digital channels. */\n    virtual void enableDMRTalkPermit(bool enable) = 0;\n    /** Enables/disables the talk permit tone for analog channels. */\n    virtual void enableFMTalkPermit(bool enable) = 0;\n    /** Returns @c true if the reset tone is enabled for digital calls. */\n    virtual bool dmrResetTone() const = 0;\n    /** Enables/disables the reset tone for digital calls. */\n    virtual void enableDMRResetTone(bool enable) = 0;\n\n    /** Returns @c true if the idle channel tone is enabled. */\n    virtual bool idleChannelTone() const = 0;\n    /** Enables/disables the idle channel tone. */\n    virtual void enableIdleChannelTone(bool enable) = 0;\n    /** Returns the menu exit time in seconds. */\n    virtual Interval menuExitTime() const = 0;\n    /** Sets the menu exit time in seconds. */\n    virtual void setMenuExitTime(Interval intv) = 0;\n    /** Returns @c true if the startup tone is enabled. */\n    virtual bool startupTone() const = 0;\n    /** Enables/disables the startup tone. */\n    virtual void enableStartupTone(bool enable) = 0;\n    /** Returns @c true if the call-end prompt is enabled. */\n    virtual bool callEndPrompt() const = 0;\n    /** Enables/disables the call-end prompt. */\n    virtual void enableCallEndPrompt(bool enable) = 0;\n    /** Returns the maximum volume. */\n    virtual Level maxSpeakerVolume() const = 0;\n    /** Sets the maximum volume. */\n    virtual void setMaxSpeakerVolume(Level level) = 0;\n    /** Returns @c true if get GPS position is enabled. */\n    virtual bool getGPSPosition() const = 0;\n    /** Enables/disables get GPS position. */\n    virtual void enableGetGPSPosition(bool enable) = 0;\n\n    /** Returns @c true if the volume change prompt is enabled. */\n    virtual bool volumeChangePrompt() const = 0;\n    /** Enables/disables the volume change prompt. */\n    virtual void enableVolumeChangePrompt(bool enable) = 0;\n    /** Returns the auto repeater offset direction for VFO A. */\n    virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const = 0;\n    /** Sets the auto-repeater offset direction for VFO A. */\n    virtual void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) = 0;\n    /** Returns the last-caller display mode. */\n    virtual AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const = 0;\n    /** Sets the last-caller display mode. */\n    virtual void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) = 0;\n\n    /** Returns @c true if the clock is shown. */\n    virtual bool displayClock() const = 0;\n    /** Enables/disables clock display. */\n    virtual void enableDisplayClock(bool enable) = 0;\n    /** Returns @c true if the audio is \"enhanced\". */\n    virtual bool enhanceAudio() const = 0;\n    /** Enables/disables \"enhanced\" audio. */\n    virtual void enableEnhancedAudio(bool enable) = 0;\n    /** Returns the minimum VFO scan frequency for the UHF band in Hz. */\n    virtual Frequency minVFOScanFrequencyUHF() const = 0;\n    /** Sets the minimum VFO scan frequency for the UHF band in Hz. */\n    virtual void setMinVFOScanFrequencyUHF(Frequency hz) = 0;\n    /** Returns the maximum VFO scan frequency for the UHF band in Hz. */\n    virtual Frequency maxVFOScanFrequencyUHF() const = 0;\n    /** Sets the maximum VFO scan frequency for the UHF band in Hz. */\n    virtual void setMaxVFOScanFrequencyUHF(Frequency hz) = 0;\n\n    /** Returns the minimum VFO scan frequency for the VHF band in Hz. */\n    virtual Frequency minVFOScanFrequencyVHF() const = 0;\n    /** Sets the minimum VFO scan frequency for the VHF band in Hz. */\n    virtual void setMinVFOScanFrequencyVHF(Frequency hz) = 0;\n    /** Returns the maximum VFO scan frequency for the VHF band in Hz. */\n    virtual Frequency maxVFOScanFrequencyVHF() const = 0;\n    /** Sets the maximum VFO scan frequency for the VHF band in Hz. */\n    virtual void setMaxVFOScanFrequencyVHF(Frequency hz) = 0;\n\n    /** Returns @c true if the auto-repeater offset frequency for UHF is set. */\n    virtual bool hasAutoRepeaterOffsetFrequencyIndexUHF() const = 0;\n    /** Returns the auto-repeater offset frequency index for UHF. */\n    virtual unsigned autoRepeaterOffsetFrequencyIndexUHF() const = 0;\n    /** Sets the auto-repeater offset frequency index for UHF. */\n    virtual void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) = 0;\n    /** Clears the auto-repeater offset frequency index for UHF. */\n    virtual void clearAutoRepeaterOffsetFrequencyIndexUHF() = 0;\n    /** Returns @c true if the auto-repeater offset frequency for VHF is set. */\n    virtual bool hasAutoRepeaterOffsetFrequencyIndexVHF() const = 0;\n    /** Returns the auto-repeater offset frequency index for UHF. */\n    virtual unsigned autoRepeaterOffsetFrequencyIndexVHF() const = 0;\n    /** Sets the auto-repeater offset frequency index for VHF. */\n    virtual void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) = 0;\n    /** Clears the auto-repeater offset frequency index for VHF. */\n    virtual void clearAutoRepeaterOffsetFrequencyIndexVHF() = 0;\n\n    /** Returns @c true if the current contact is shown. */\n    virtual bool showCurrentContact() const = 0;\n    /** Enables/disables display of current contact. */\n    virtual void enableShowCurrentContact(bool enable) = 0;\n\n    /** Returns the call-tone melody. */\n    virtual void callToneMelody(Melody &melody) const = 0;\n    /** Sets the call-tone melody. */\n    virtual void setCallToneMelody(const Melody &melody) = 0;\n    /** Returns the idle-tone melody. */\n    virtual void idleToneMelody(Melody &melody) const = 0;\n    /** Sets the idle-tone melody. */\n    virtual void setIdleToneMelody(const Melody &melody) = 0;\n    /** Returns the reset-tone melody. */\n    virtual void resetToneMelody(Melody &melody) const = 0;\n    /** Sets the reset-tone melody. */\n    virtual void setResetToneMelody(const Melody &melody) = 0;\n\n    /** Returns @c true if the default boot channel is enabled. */\n    virtual bool defaultChannel() const = 0;\n    /** Enables/disables default boot channel. */\n    virtual void enableDefaultChannel(bool enable) = 0;\n    /** Returns the default zone index (0-based) for VFO A. */\n    virtual unsigned defaultZoneIndexA() const = 0;\n    /** Sets the default zone (0-based) for VFO A. */\n    virtual void setDefaultZoneIndexA(unsigned idx) = 0;\n    /** Returns the default zone index (0-based) for VFO B. */\n    virtual unsigned defaultZoneIndexB() const = 0;\n    /** Sets the default zone (0-based) for VFO B. */\n    virtual void setDefaultZoneIndexB(unsigned idx) = 0;\n    /** Returns @c true if the default channel for VFO A is VFO. */\n    virtual bool defaultChannelAIsVFO() const = 0;\n    /** Returns the default channel index for VFO A.\n     * Must be within default zone. If 0xff, default channel is VFO. */\n    virtual unsigned defaultChannelAIndex() const = 0;\n    /** Sets the default channel index for VFO A. */\n    virtual void setDefaultChannelAIndex(unsigned idx) = 0;\n    /** Sets the default channel for VFO A to be VFO. */\n    virtual void setDefaultChannelAToVFO() = 0;\n    /** Returns @c true if the default channel for VFO B is VFO. */\n    virtual bool defaultChannelBIsVFO() const = 0;\n    /** Returns the default channel index for VFO B.\n     * Must be within default zone. If 0xff, default channel is VFO. */\n    virtual unsigned defaultChannelBIndex() const = 0;\n    /** Sets the default channel index for VFO B. */\n    virtual void setDefaultChannelBIndex(unsigned idx) = 0;\n    /** Sets the default channel for VFO B to be VFO. */\n    virtual void setDefaultChannelBToVFO() = 0;\n\n    /** Returns @c true if the call is displayed instead of the name. */\n    virtual bool displayCall() const = 0;\n    /** Enables/disables call display. */\n    virtual void enableDisplayCall(bool enable) = 0;\n\n    /** Returns the display color for callsigns. */\n    virtual AnytoneDisplaySettingsExtension::Color callDisplayColor() const = 0;\n    /** Sets the display color for callsigns. */\n    virtual void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) = 0;\n\n    /** Returns @c true if the GPS units are imperial. */\n    virtual bool gpsUnitsImperial() const = 0;\n    /** Enables/disables imperial GPS units. */\n    virtual void enableGPSUnitsImperial(bool enable) = 0;\n\n    /** Returns the minimum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual Frequency autoRepeaterMinFrequencyVHF() const = 0;\n    /** Sets the minimum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual void setAutoRepeaterMinFrequencyVHF(Frequency Hz) = 0;\n    /** Returns the maximum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual Frequency autoRepeaterMaxFrequencyVHF() const = 0;\n    /** Sets the maximum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual void setAutoRepeaterMaxFrequencyVHF(Frequency Hz) = 0;\n\n    /** Returns the minimum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual Frequency autoRepeaterMinFrequencyUHF() const = 0;\n    /** Sets the minimum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual void setAutoRepeaterMinFrequencyUHF(Frequency Hz) = 0;\n    /** Returns the maximum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual Frequency autoRepeaterMaxFrequencyUHF() const = 0;\n    /** Sets the maximum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual void setAutoRepeaterMaxFrequencyUHF(Frequency Hz) = 0;\n    /** Returns the auto-repeater direction for VFO B. */\n    virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const = 0;\n    /** Sets the auto-repeater direction for VFO B. */\n    virtual void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) = 0;\n\n    /** Returns @c true if the last heard is shown while pressing PTT. */\n    virtual bool showLastHeard() const = 0;\n    /** Enables/disables showing last heard. */\n    virtual void enableShowLastHeard(bool enable) = 0;\n\n    /** Returns @c true if the last caller is kept when changing channel. */\n    virtual bool keepLastCaller() const = 0;\n    /** Enables/disables keeping the last caller when changing the channel. */\n    virtual void enableKeepLastCaller(bool enable) = 0;\n\n    /** Encodes the general settings. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err);\n    /** Updates the abstract config from general settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err);\n    /** Links the general settings. */\n    virtual bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err);\n\n  protected:\n    /** Internal used offsets within the element. */\n    struct Offset : public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int displayMode()     { return 0x0001; }\n      static constexpr unsigned int autoKeyLock()     { return 0x0002; }\n      static constexpr unsigned int autoShutDown()    { return 0x0003; }\n      static constexpr unsigned int bootDisplay()     { return 0x0006; }\n      static constexpr unsigned int bootPassword()    { return 0x0007; }\n      static constexpr unsigned int squelchLevelA()   { return 0x0009; }\n      static constexpr unsigned int squelchLevelB()   { return 0x000a; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class for the extended settings element in many AnyTone codeplugs. That\n   *  is, every device after the D868UVE. It provides additional settings to the\n   *  @c AnytoneGeneralSettingsElement.\n   *\n   *  As these elements differ heavily from device to device, there is no common encoding. This\n   *  class only defines an interface to get/set common settings. */\n  class ExtendedSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ExtendedSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Returns the color of the channel name for VFO B. */\n    virtual AnytoneDisplaySettingsExtension::Color channelBNameColor() const = 0;\n    /** Sets the channel name color for the VFO B. */\n    virtual void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color) = 0;\n\n    /** Returns the color of the zone name for VFO A. */\n    virtual AnytoneDisplaySettingsExtension::Color zoneANameColor() const = 0;\n    /** Sets the zone name color for the VFO A. */\n    virtual void setZoneANameColor(AnytoneDisplaySettingsExtension::Color) = 0;\n\n    /** Returns the color of the zone name for VFO B. */\n    virtual AnytoneDisplaySettingsExtension::Color zoneBNameColor() const = 0;\n    /** Sets the zone name color for the VFO B. */\n    virtual void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color) = 0;\n\n    /** Encodes the settings from the config. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Update config from settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Link config from settings extension. */\n    virtual bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n  };\n\n\n  /** Represents the base class for zone channel list for all AnyTone codeplugs.\n   * Zone channel lists assign a default channel to each zone for VFO A and B.\n   *\n   * Memory layout of ecoded zone channel lists (size 0x400 bytes):\n   * @verbinclude anytone_zonechannellist.txt */\n  class ZoneChannelListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneChannelListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ZoneChannelListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0400; }\n\n    /** Resets the zone channel list. */\n    void clear();\n\n    /** Returns @c true if the channel index for VFO A is set for the n-th zone. */\n    virtual bool hasChannelA(unsigned n) const;\n    /** Returns the channel index (0-based) for VFO A for the n-th zone. */\n    virtual unsigned channelIndexA(unsigned n) const;\n    /** Sets the channel index (0-based) for VFO A for the n-th zone. */\n    virtual void setChannelIndexA(unsigned n, unsigned idx);\n    /** Clears the channel index (0-based) for VFO A for the n-th zone. */\n    virtual void clearChannelIndexA(unsigned n);\n\n    /** Returns @c true if the channel index for VFO B is set for the n-th zone. */\n    virtual bool hasChannelB(unsigned n) const;\n    /** Returns the channel index (0-based) for VFO B for the n-th zone. */\n    virtual unsigned channelIndexB(unsigned n) const;\n    /** Sets the channel index (0-based) for VFO B for the n-th zone. */\n    virtual void setChannelIndexB(unsigned n, unsigned idx);\n    /** Clears the channel index (0-based) for VFO B for the n-th zone. */\n    virtual void clearChannelIndexB(unsigned n);\n\n  public:\n    /** Some limits for the channel lists. */\n    struct Limit: public Element::Limit {\n      /// Maximum number of channels per VFO.\n      static constexpr unsigned int zones() { return 250; }\n    };\n\n  protected:\n    /** Internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int channelsA()       { return 0x0000;}\n      static constexpr unsigned int channelsB()       { return 0x0200;}\n      static constexpr unsigned int betweenChannels() { return 0x0002;}\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which zones are valid. */\n  class ZoneBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n\n  /** Represents the base class of the boot settings for all AnyTone codeplug.\n   *\n   * Memory layout of encoded boot settings (size 0x0030):\n   * @verbinclude anytone_bootsettings.txt */\n  class BootSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BootSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    BootSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the boot settings. */\n    void clear();\n\n    /** Returns the first intro line. */\n    virtual QString introLine1() const;\n    /** Sets the first intro line. */\n    virtual void setIntroLine1(const QString &txt);\n    /** Returns the second intro line. */\n    virtual QString introLine2() const;\n    /** Sets the second intro line. */\n    virtual void setIntroLine2(const QString &txt);\n\n    /** Returns the password. */\n    virtual QString password() const;\n    /** Sets the password. */\n    virtual void setPassword(const QString &txt);\n\n    /** Updates the general settings from the given abstract configuration. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx);\n    /** Updates the abstract configuration from this general settings. */\n    virtual bool updateConfig(Context &ctx);\n\n  public:\n    /** Some limits for boot settings. */\n    struct Limit: public Element::Limit {\n      /** Maximum intro line length. */\n      static constexpr unsigned int introLineLength() { return 16; }\n      /** Maximum password length. */\n      static constexpr unsigned int passwordLength()  { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int introLine1() { return 0x0000; }\n      static constexpr unsigned int introLine2() { return 0x0010; }\n      static constexpr unsigned int password()   { return 0x0020; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class of DMR APRS settings for all AnyTone codeplugs.\n   *\n   * Memory encoding of the DMR APRS settings (size 0x0030 bytes):\n   * @verbinclude anytone_dmraprssettings.txt */\n  class DMRAPRSSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DMRAPRSSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit DMRAPRSSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the APRS settings. */\n    void clear();\n\n    /** Returns the Manual TX interval in seconds. */\n    virtual Interval manualInterval() const;\n    /** Sets the manual TX interval in seconds. */\n    virtual void setManualInterval(const Interval& sec);\n\n    /** Returns @c true if the automatic APRS is enabled. */\n    virtual bool automatic() const;\n    /** Returns the automatic transmit interval in seconds. */\n    virtual Interval automaticInterval() const;\n    /** Sets the automatic transmit interval in seconds. */\n    virtual void setAutomaticInterval(const Interval& sec);\n    /** Disables the automatic APRS. To enable it, set an interval. */\n    virtual void disableAutomatic();\n\n    /** Returns @c true if the fixed location beacon is enabled. */\n    virtual bool fixedLocation() const;\n    /** Returns the location of the fixed position. */\n    virtual QGeoCoordinate location() const;\n    /** Sets the location of the fixed position. */\n    virtual void setLocation(const QGeoCoordinate &pos);\n    /** Enables/disables fixed location beacon. */\n    virtual void enableFixedLocation(bool enable);\n\n    /** Returns the transmit power. */\n    virtual Channel::Power power() const;\n    /** Sets the transmit power. */\n    virtual void setPower(Channel::Power power);\n\n    /** Returns @c true if the n-th channel is set. */\n    virtual bool hasChannel(unsigned n) const;\n    /** Returns @c true if the n-th channel is VFO A. */\n    virtual bool channelIsVFOA(unsigned n) const;\n    /** Returns @c true if the n-th channel is VFO B. */\n    virtual bool channelIsVFOB(unsigned n) const;\n    /** Returns @c true if the n-th channel is selected channel. */\n    virtual bool channelIsSelected(unsigned n) const;\n    /** Returns the index of the n-th channel. */\n    virtual unsigned channelIndex(unsigned n) const;\n    /** Sets the n-th channel index. */\n    virtual void setChannelIndex(unsigned n, unsigned idx);\n    /** Sets the n-th channel to VFO A. */\n    virtual void setChannelVFOA(unsigned n);\n    /** Sets the n-th channel to VFO B. */\n    virtual void setChannelVFOB(unsigned n);\n    /** Sets the n-th channel to selected channel. */\n    virtual void setChannelSelected(unsigned n);\n    /** Resets the n-th channel. */\n    virtual void clearChannel(unsigned n);\n\n    /** Returns the destination DMR ID to send the APRS information to. */\n    virtual unsigned destination() const;\n    /** Sets the destination DMR ID to send the APRS information to. */\n    virtual void setDestination(unsigned id);\n\n    /** Returns the call type. */\n    virtual DMRContact::Type callType() const;\n    /** Sets the call type. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns @c true if the timeslot of the channel is overridden. */\n    virtual bool timeSlotOverride() const;\n    /** Returns the timeslot (only valid if @c timeSlotOverride returns @c true). */\n    virtual DMRChannel::TimeSlot timeslot() const;\n    /** Sets the timeslot. */\n    virtual void overrideTimeSlot(DMRChannel::TimeSlot ts);\n    /** Disables TS override. */\n    virtual void disableTimeSlotOverride();\n\n    /** Updates the GPS settings from the given config. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx);\n    /** Updates the global config. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Creates GPS system from this GPS settings. */\n    virtual bool createGPSSystem(uint8_t i, Context &ctx);\n    /** Links GPS system from this GPS settings. */\n    virtual bool linkGPSSystem(uint8_t i, Context &ctx);\n\n  protected:\n    /** Internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int manualInterval()        { return 0x0000; }\n      static constexpr unsigned int automaticInterval()     { return 0x0001; }\n      static constexpr unsigned int fixedLocation()         { return 0x0002; }\n      static constexpr unsigned int latitudeDeg()           { return 0x0003; }\n      static constexpr unsigned int latitudeMin()           { return 0x0004; }\n      static constexpr unsigned int latitudeSec()           { return 0x0005; }\n      static constexpr unsigned int latitudeHemi()          { return 0x0006; }\n      static constexpr unsigned int longitudeDeg()          { return 0x0007; }\n      static constexpr unsigned int longitudeMin()          { return 0x0008; }\n      static constexpr unsigned int longitudeSec()          { return 0x0009; }\n      static constexpr unsigned int longitudeHemi()         { return 0x000a; }\n      static constexpr unsigned int power()                 { return 0x000b; }\n      static constexpr unsigned int channelIndices()        { return 0x000c; }\n      static constexpr unsigned int betweenChannelIndices() { return 0x0002; }\n      static constexpr unsigned int destinationId()         { return 0x001c; }\n      static constexpr unsigned int callType()              { return 0x0020; }\n      static constexpr unsigned int timeSlot()              { return 0x0021; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class of a DMR APRS message for all AnyTone codeplugs. */\n  class DMRAPRSMessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DMRAPRSMessageElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DMRAPRSMessageElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    void clear();\n\n    /** Returns the message. */\n    virtual QString message() const;\n    /** Sets the message. */\n    void setMessage(const QString &message);\n\n    /** Encodes the message. */\n    virtual bool fromConfig(Codeplug::Flags flags, Context &ctx);\n    /** Decodes the message. */\n    virtual bool updateConfig(Context &ctx) const;\n\n  public:\n    /** Some limits for the message. */\n    struct Limit {\n      static constexpr unsigned int length() { return 32; }      ///< Maximum message length.\n    };\n\n  protected:\n    /** Some internal used offset. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int message() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the table of repeater offset frequencies.\n   *\n   * Memory representation of the offset frequency table (size 0x03F0 bytes):\n   * @verbinclude anytone_repeateroffsetfrequencies.txt */\n  class RepeaterOffsetListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    RepeaterOffsetListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit RepeaterOffsetListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x03f0; }\n\n    void clear();\n\n    /** Returns @c true, if the n-th offset frequency is set. */\n    virtual bool isSet(unsigned int n) const;\n    /** Returns the n-th offset frequency. */\n    virtual Frequency offset(unsigned int n) const;\n    /** Sets the n-th offset frequency. */\n    virtual void setOffset(unsigned int n, Frequency freq);\n    /** Clears the n-th offset frequency. */\n    virtual void clearOffset(unsigned int n);\n\n  public:\n    /** Some limits for the offset frequency table. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 250; }      ///< Max number of entries in the table.\n    };\n\n  protected:\n    /** Some internal used offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int frequencies() { return 0x0000; }\n      static constexpr unsigned int betweenFrequencies() { return sizeof(uint32_t); }\n      /// @endcond\n    };\n  };\n\n  /** Represents the base class of prefabricated message linked list for all AnyTone codeplugs.\n   * This element is some weird linked list that determines some order for the prefabricated\n   * SMS messages.\n   *\n   * Memory encoding of the message list (size 0x0010 bytes):\n   * @verbinclude anytone_messagelist.txt */\n  class MessageListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit MessageListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Clears the message list item. */\n    void clear();\n\n    /** Returns @c true if there is a next message (EOL otherwise). */\n    virtual bool hasNext() const;\n    /** Returns the index of the next message in list. */\n    virtual unsigned next() const;\n    /** Sets the index of the next message in list. */\n    virtual void setNext(unsigned idx);\n    /** Clears the next message index. */\n    virtual void clearNext();\n\n    /** Returns @c true if there is a message index. */\n    virtual bool hasIndex() const;\n    /** Returns the index of the message. */\n    virtual unsigned index() const;\n    /** Sets the index of the message. */\n    virtual void setIndex(unsigned idx);\n    /** Clears the index of the message. */\n    virtual void clearIndex();\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int next()  { return 0x0002; }\n      static constexpr unsigned int index() { return 0x0003; }\n    };\n  };\n\n\n  /** Represents base class of a preset message for all AnyTone codeplugs.\n   *\n   * Memory encoding of the message (0x100 bytes):\n   * @verbinclude anytone_message.txt */\n  class MessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    MessageElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Resets the message. */\n    void clear();\n\n    /** Returns the message text. */\n    virtual QString message() const;\n    /** Sets the message text. */\n    virtual void setMessage(const QString &msg);\n\n  public:\n    /** Some limits for the message. */\n    struct Limit: public Element::Limit {\n      /** Maximum message length. */\n      static constexpr unsigned int messageLength() { return 99; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int message() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bytemap indicating which message is valid. */\n  class MessageBytemapElement: public InvertedBytemapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageBytemapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageBytemapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n  };\n\n  /** Represents base class of a analog quick call entry for all AnyTone codeplugs.\n   *\n   * Memory encoding of the message (0x0002 bytes):\n   * @verbinclude anytone_analogquickcall.txt */\n  class AnalogQuickCallElement: public Element\n  {\n  public:\n    /** Analog quick-call types. */\n    enum class Type {\n      None     = 0,                ///< None, quick-call disabled.\n      DTMF     = 1,                ///< DTMF call.\n      TwoTone  = 2,                ///< 2-tone call.\n      FiveTone = 3                 ///< 5-tone call\n    };\n\n  protected:\n    /** Hidden constructor. */\n    AnalogQuickCallElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit AnalogQuickCallElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0002; }\n\n    /** Resets the quick call entry. */\n    void clear();\n\n    /** Returns the call type. */\n    virtual Type type() const;\n    /** Sets the type of the quick call. */\n    virtual void setType(Type type);\n\n    /** Returns @c true if an analog contact index is set. */\n    virtual bool hasContactIndex() const;\n    /** Returns the analog contact index. */\n    virtual unsigned contactIndex() const;\n    /** Sets the analog contact index. */\n    virtual void setContactIndex(unsigned idx);\n    /** Clears the contact index. */\n    virtual void clearContactIndex();\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int type()         { return 0x0000; }\n      static constexpr unsigned int contactIndex() { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the list of analog quick-call settings for all AnyTone codeplugs.\n   *\n   * Memory reresentation of the quick-call settings (size 0x0100 bytes):\n   * @verbinclude anytone_analogquickcalls.txt */\n  class AnalogQuickCallsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AnalogQuickCallsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AnalogQuickCallsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Clears the quick calls. */\n    void clear();\n\n    /** Returns a pointer to the n-th entry. */\n    uint8_t *quickCall(unsigned int n) const;\n\n  public:\n    /** Some limits for the quick calls. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 4; }   ///< The maximum number of quick-call entries.\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int quickCalls() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the list of status messages for all AnyTone codeplugs.\n   *\n   * Memory reresentation of the status messages (size 0x0400 bytes):\n   * @verbinclude anytone_statusmessages.txt */\n  class StatusMessagesElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    StatusMessagesElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    StatusMessagesElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0400; }\n\n    void clear();\n\n    /** Returns the n-th status message. */\n    virtual QString message(unsigned int n) const;\n    /** Sets the n-th status message. */\n    virtual void setMessage(unsigned int n, const QString &msg);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int numMessages()   { return 32; }    ///< Maximum number of messages.\n      static constexpr unsigned int messageLength() { return 32; }    ///< Maximum length of the messages.\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int messages()        { return 0x0000; }\n      static constexpr unsigned int betweenMessages() { return 0x0020; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap, indicating which status messages are valid. */\n  class StatusMessageBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    StatusMessageBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    StatusMessageBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n  };\n\n\n  /** Represents the base class of a hot-key setting entry for all AnyTone codeplugs.\n   *\n   * Memory encoding of a hot-key setting (size 0x0030 bytes):\n   * @verbinclude anytone_hotkey.txt */\n  class HotKeyElement: public Element\n  {\n  public:\n    /** Hot-key types. */\n    enum class Type {\n      Call = 0,             ///< Perform a call.\n      Menu = 1              ///< Show a menu item.\n    };\n\n    /** Possible menu items. */\n    enum class MenuItem {\n      SMS = 1,          ///< Show SMS menu.\n      NewSMS = 2,       ///< Create new SMS.\n      HotText = 3,      ///< Send a hot-text.\n      Inbox = 4,        ///< Show SMS inbox.\n      Outbox = 5,       ///< Show SMS outbox.\n      Contacts = 6,     ///< Show contact list.\n      ManualDial = 7,   ///< Show manual dial.\n      CallLog = 8       ///< Show call log.\n    };\n\n    /** Possible call types. */\n    enum class CallType {\n      Analog = 0,       ///< Perform an analog call.\n      Digital = 1       ///< Perform a digital call.\n    };\n\n    /** Possible digital call sub-types. */\n    enum class DigiCallType {\n      Off = 0xff,          ///< Call disabled.\n      GroupCall = 0,       ///< Perform a group call.\n      PrivateCall= 1,      ///< Perform private call.\n      AllCall = 2,         ///< Perform all call.\n      HotText = 3,         ///< Send a text message.\n      CallTip = 4,         ///< Send a call tip (?).\n      StatusMessage = 5    ///< Send a state message.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    HotKeyElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit HotKeyElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the hot-key entry. */\n    void clear();\n\n    /** Returns the type of the hot-key entry. */\n    virtual Type type() const;\n    /** Sets the type of the hot-key entry. */\n    virtual void setType(Type type);\n\n    /** If @c type returns @c Type::Menu, returns the menu item. */\n    virtual MenuItem menuItem() const;\n    /** Sets the menu item. For this setting to have an effect, the @c type must be set to\n     * @c Type::Menu. */\n    virtual void setMenuItem(MenuItem item);\n\n    /** If @c type returns @c Type::Call, returns the type of the call. */\n    virtual CallType callType() const;\n    /** Sets the call type. For this settings to have an effect, the type must be set to\n     * @c Type::Call. */\n    virtual void setCallType(CallType type);\n\n    /** If @c type returns @c Type::Call and @c callType @c CalLType::Digital, returns the digital\n     * call type. */\n    virtual DigiCallType digiCallType() const;\n    /** Sets the digital call type. For this setting to have an effect, the @c type must be\n     * @c Type::Call and @c callType must be @c CallType::Digital. */\n    virtual void setDigiCallType(DigiCallType type);\n\n    /** Returns @c true if the contact index is set. */\n    virtual bool hasContactIndex() const;\n    /** If @c type is @c Type::Call, returns the contact index. This is either an index of an\n     * analog quick call if @c callType is CallType::Analog or a contact index if @c callType is\n     * @c CallType::Digital. If set to 0xffffffff the index is invalid. */\n    virtual unsigned contactIndex() const;\n    /** Sets the contact index. This can either be an index of an analog quick-call or a contact\n     * index. */\n    virtual void setContactIndex(unsigned idx);\n    /** Clears the contact index. */\n    virtual void clearContactIndex();\n\n    /** Returns @c true if a message index is set. */\n    virtual bool hasMessageIndex() const;\n    /** Returns the message index. This can either be an index of an SMS or an index of a status\n     * message. */\n    virtual unsigned messageIndex() const;\n    /** Sets the message index. */\n    virtual void setMessageIndex(unsigned idx);\n    /** Clears the message index. */\n    virtual void clearMessageIndex();\n\n  protected:\n    /** Some internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int type()         { return 0x0000; }\n      static constexpr unsigned int menuItem()     { return 0x0001; }\n      static constexpr unsigned int callType()     { return 0x0002; }\n      static constexpr unsigned int digiCallType() { return 0x0003; }\n      static constexpr unsigned int contactIndex() { return 0x0004; }\n      static constexpr unsigned int messageIndex() { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the list of hot-key settings for all AnyTone codeplugs.\n   *\n   * See @c HotKeyElement for encoding of each element.\n   *\n   *  Memory encoding of the hot-key settings (size 0x0360 bytes):\n   * @verbinclude anytone_hotkeysettings.txt */\n  class HotKeySettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    HotKeySettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    HotKeySettingsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0360; }\n\n    void clear();\n\n    /** Returns a pointer to the n-th hot key setting. */\n    virtual uint8_t *hotKeySetting(unsigned int n) const;\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 18; }   ///< Maximum number of hot-key entries.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int hotKeySettings()        { return 0x0000; }\n      static constexpr unsigned int betweenHotKeySettings() { return HotKeySettingsElement::size(); }\n      /// @endcond\n    };\n  };\n\n  /** Represents the base class of alarm setting entry for all AnyTone codeplugs.\n   *\n   * Memory encoding of an alarm setting (size 0x0020 bytes):\n   * @verbinclude anytone_alarmsetting.txt */\n  class AlarmSettingElement: public Element\n  {\n  public:\n    /** Represents the base class of an analog alarm setting for all AnyTone codeplugs.\n     *\n     * Memory representation of an analog alarm setting (size 0x000a bytes):\n     * @verbinclude anytone_analogalarm.txt */\n    class AnalogAlarm: public Element\n    {\n    public:\n      /** Possible analog alarm types. */\n      enum class Action {\n        None = 0,            ///< No alarm at all.\n        Background = 1,      ///< Transmit and background.\n        TXAlarm = 2,         ///< Transmit and alarm\n        Both = 3,            ///< Both?\n      };\n\n      /** Possible alarm signalling types. */\n      enum class ENIType {\n        None = 0,            ///< No alarm code signalling.\n        DTMF = 1,            ///< Send alarm code as DTMF.\n        FiveTone = 2         ///< Send alarm code as 5-tone.\n      };\n\n    protected:\n      /** Hidden constructor. */\n      AnalogAlarm(uint8_t *ptr, unsigned size);\n\n    public:\n      /** Constructor. */\n      AnalogAlarm(uint8_t *ptr);\n\n      /** Returns the size of the element. */\n      static constexpr unsigned int size() { return 0x000a; }\n\n      /** Resets the alarm. */\n      void clear();\n\n      /** Returns the alarm action. */\n      virtual Action action() const;\n      /** Sets the alarm action. */\n      virtual void setAction(Action action);\n\n      /** Returns the encoding type. */\n      virtual ENIType encodingType() const;\n      /** Sets the encoding type. */\n      virtual void setEncodingType(ENIType type);\n\n      /** Returns the emergency ID index. */\n      virtual unsigned emergencyIndex() const;\n      /** Sets the emergency ID index. */\n      virtual void setEmergencyIndex(unsigned idx);\n\n      /** Returns the alarm duration in seconds. */\n      virtual Interval duration() const;\n      /** Sets the alarm duration in seconds. */\n      virtual void setDuration(const Interval &sec);\n      /** Returns the TX duration in seconds. */\n      virtual Interval txDuration() const;\n      /** Sets the TX duration in seconds. */\n      virtual void setTXDuration(const Interval &sec);\n      /** Returns the RX duration in seconds. */\n      virtual Interval rxDuration() const;\n      /** Sets the RX duration in seconds. */\n      virtual void setRXDuration(const Interval &sec);\n\n      /** Returns @c true if the alarm channel is the selected channel. */\n      virtual bool channelIsSelected() const;\n      /** Returns the channel index. */\n      virtual unsigned channelIndex() const;\n      /** Sets the channel index. */\n      virtual void setChannelIndex(unsigned idx);\n      /** Sets the alarm channel to the selected channel. */\n      virtual void setChannelSelected();\n\n      /** Returns @c true if the alarm is repeated continuously. */\n      virtual bool repeatContinuously() const;\n      /** Returns the number of alarm repetitions. */\n      virtual unsigned repetitions() const;\n      /** Sets the number of alarm repetitions. */\n      virtual void setRepetitions(unsigned num);\n      /** Sets the alarm to be repeated continuously. */\n      virtual void setRepatContinuously();\n\n    protected:\n      /** Some internal offsets within element. */\n      struct Offset: public Element::Offset {\n        /// @cond DO_NOT_DOCUMENT\n        static constexpr unsigned int action()            { return 0x0000; }\n        static constexpr unsigned int encodingType()      { return 0x0001; }\n        static constexpr unsigned int emergencyIndex()    { return 0x0002; }\n        static constexpr unsigned int duration()          { return 0x0003; }\n        static constexpr unsigned int txDuration()        { return 0x0004; }\n        static constexpr unsigned int rxDuration()        { return 0x0005; }\n        static constexpr unsigned int channelIndex()      { return 0x0006; }\n        static constexpr unsigned int channelIsSelected() { return 0x0008; }\n        static constexpr unsigned int repetitions()       { return 0x0009; }\n        /// @endcond\n      };\n    };\n\n\n    /** Represents the base class of an digital alarm setting for all AnyTone codeplugs.\n     *\n     * Memory representation of a digital alarm setting (size 0x000c bytes):\n     * @verbinclude anytone_digitalalarm.txt */\n    class DigitalAlarm: public Element\n    {\n    public:\n      /** Possible alarm types. */\n      enum class Action {\n        None = 0,           ///< No alarm at all.\n        Background = 1,     ///< Transmit and background.\n        NonLocal = 2,       ///< Transmit and non-local alarm.\n        Local = 3,          ///< Transmit and local alarm.\n      };\n\n    protected:\n      /** Hidden constructor. */\n      DigitalAlarm(uint8_t *ptr, unsigned size);\n\n    public:\n      /** Constructor. */\n      explicit DigitalAlarm(uint8_t *ptr);\n\n      /** Returns the size of the element. */\n      static constexpr unsigned int size() { return 0x000c; }\n\n      /** Resets the digital alarm settings. */\n      void clear();\n\n      /** Returns the alarm action. */\n      virtual Action action() const;\n      /** Sets the alarm action. */\n      virtual void setAction(Action action);\n\n      /** Returns the alarm duration in seconds. */\n      virtual Interval duration() const;\n      /** Sets the alarm duration in seconds. */\n      virtual void setDuration(const Interval &sec);\n      /** Returns the TX duration in seconds. */\n      virtual Interval txDuration() const;\n      /** Sets the TX duration in seconds. */\n      virtual void setTXDuration(const Interval &sec);\n      /** Returns the RX duration in seconds. */\n      virtual Interval rxDuration() const;\n      /** Sets the RX duration in seconds. */\n      virtual void setRXDuration(const Interval &sec);\n\n      /** Returns @c true if the alarm channel is the selected channel. */\n      virtual bool channelIsSelected() const;\n      /** Returns the channel index. */\n      virtual unsigned channelIndex() const;\n      /** Sets the channel index. */\n      virtual void setChannelIndex(unsigned idx);\n      /** Sets the alarm channel to the selected channel. */\n      virtual void setChannelSelected();\n\n      /** Returns @c true if the alarm is repeated continuously. */\n      virtual bool repeatContinuously() const;\n      /** Returns the number of alarm repetitions. */\n      virtual unsigned repetitions() const;\n      /** Sets the number of alarm repetitions. */\n      virtual void setRepetitions(unsigned num);\n      /** Sets the alarm to be repeated continuously. */\n      virtual void setRepatContinuously();\n\n      /** Returns voice broadcast duration in minutes. */\n      virtual Interval voiceBroadcastDuration() const;\n      /** Sets voice broadcast duration in minutes. */\n      virtual void setVoiceBroadcastDuration(const Interval &min);\n      /** Returns area broadcast duration in minutes. */\n      virtual Interval areaBroadcastDuration() const;\n      /** Sets area broadcast duration in minutes. */\n      virtual void setAreaBroadcastDuration(const Interval &min);\n\n      /** Returns @c true if the VOX gets enabled. */\n      virtual bool vox() const;\n      /** Enables/disables the VOX for alarms. */\n      virtual void enableVOX(bool enable);\n      /** Returns @c true if alarms gets received enabled. */\n      virtual bool rxAlarm() const;\n      /** Enables/disables the reception of alarms. */\n      virtual void enableRXAlarm(bool enable);\n\n    protected:\n      /** Internal offsets within element. */\n      struct Offset: public Element::Offset {\n        /// @cond DO_NOT_DOCUMENT\n        static constexpr unsigned int action()                 { return 0x0000; }\n        static constexpr unsigned int duration()               { return 0x0001; }\n        static constexpr unsigned int txDuration()             { return 0x0002; }\n        static constexpr unsigned int rxDuration()             { return 0x0003; }\n        static constexpr unsigned int channelIndex()           { return 0x0004; }\n        static constexpr unsigned int channelIsSelected()      { return 0x0006; }\n        static constexpr unsigned int repetitions()            { return 0x0007; }\n        static constexpr unsigned int voiceBroadcastDuration() { return 0x0008; }\n        static constexpr unsigned int areaBroadcastDuration()  { return 0x0009; }\n        static constexpr unsigned int vox()                    { return 0x000a; }\n        static constexpr unsigned int rxAlarm()                { return 0x000b; }\n        /// @endcond\n      };\n    };\n\n  protected:\n    /** Hidden constructor. */\n    AlarmSettingElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    AlarmSettingElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Clears the alarm settings. */\n    void clear();\n\n    /** Returns a pointer to the analog alarm settings. */\n    virtual uint8_t *analog() const;\n    /** Returns a pointer to the digital alarm settings. */\n    virtual uint8_t *digital() const;\n\n  protected:\n    /** Internal offsets within the element */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int analog()  { return 0x0000; }\n      static constexpr unsigned int digital() { return 0x000a; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class of digital alarm setting extension for all AnyTone codeplugs.\n   *\n   * Memory encoding of a digital alarm setting extension (size 0x0030 bytes):\n   * @verbinclude anytone_digitalalarmextension.txt */\n  class DigitalAlarmExtensionElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DigitalAlarmExtensionElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    DigitalAlarmExtensionElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Clears the settings. */\n    void clear();\n\n    /** Returns the call type. */\n    virtual DMRContact::Type callType() const;\n    /** Sets the call type. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns the destination DMR number. */\n    virtual unsigned destination() const;\n    /** Sets the destination DMR number. */\n    virtual void setDestination(unsigned number);\n\n  protected:\n    /** Internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int callType()    { return 0x0000; }\n      static constexpr unsigned int destination() { return 0x0023; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base-class for 5Tone IDs for all AnyTone codeplugs.\n   *\n   * Memory encoding of the ID (size 0x0020 bytes):\n   * @verbinclude anytone_5toneid.txt */\n  class FiveToneIDElement: public Element\n  {\n  public:\n    /** Possible 5-tone encoding standards. */\n    enum class Standard {\n      ZVEI1 = 0, ZVEI2, ZVEI3, PZVEI, DZVEI, PDZVEI, CCIR1, CCIR2, PCCIR, EEA, EuroSignal, NATEL,\n      MODAT, CCITT, EIA\n    };\n\n  protected:\n    /** Hidden constructor. */\n    FiveToneIDElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    FiveToneIDElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Clears the ID. */\n    void clear();\n\n    /** Returns the 5Tone encoding standard. */\n    virtual Standard standard() const;\n    /** Sets the encoding standard. */\n    virtual void setStandard(Standard std);\n\n    /** Returns the tone duration in ms. */\n    virtual Interval toneDuration() const;\n    /** Sets the tone duration in ms. */\n    virtual void setToneDuration(const Interval &ms);\n\n    /** Returns the ID. */\n    virtual QString id() const;\n    /** Sets the ID. */\n    virtual void setID(const QString &id);\n\n    /** Returns the name. */\n    virtual QString name() const;\n    /** Sets the name. */\n    virtual void setName(const QString &name);\n\n  public:\n    /** Some limits of the element. */\n    struct Limit: public Element::Limit {\n      /** Maximum ID length. */\n      static constexpr unsigned int idLength() { return 80; }\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 7; }\n    };\n\n  protected:\n    /** Some internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int standard()     { return 0x0001; }\n      static constexpr unsigned int idLength()     { return 0x0002; }\n      static constexpr unsigned int toneDuration() { return 0x0003; }\n      static constexpr unsigned int id()           { return 0x0004; }\n      static constexpr unsigned int name()         { return 0x0018; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which five-tone IDs are valid. */\n  class FiveToneIDBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    FiveToneIDBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    FiveToneIDBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n  };\n\n  /** Represents the list of five-tone IDs.\n   *\n   * Memory encoding of the ID list (size 0x0c80 bytes):\n   * @verbinclude anytone_5toneidlist.txt */\n  class FiveToneIDListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    FiveToneIDListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    FiveToneIDListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0c80; }\n\n    void clear();\n\n    /** Returns a pointer to the n-th five-tone ID. */\n    virtual uint8_t *member(unsigned int n) const;\n\n  public:\n    /** Some limits for the list. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 100; }   ///< Maximum number of entries.\n    };\n  };\n\n  /** Represents the base-class for 5Tone function for all AnyTone codeplugs.\n   *\n   * Memory encoding of the function (size 0x0020 bytes):\n   * @verbinclude anytone_5tonefunction.txt */\n  class FiveToneFunctionElement: public Element\n  {\n  public:\n    /** Possible function being performed on 5-tone decoding. */\n    enum class Function {\n      OpenSquelch=0, CallAll, EmergencyAlarm, RemoteKill, RemoteStun, RemoteWakeup,\n      GroupCall\n    };\n\n    /** Possible responses to 5-tone decoding. */\n    enum class Response {\n      None=0, Tone, ToneRespond\n    };\n\n  protected:\n    /** Hidden constructor. */\n    FiveToneFunctionElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit FiveToneFunctionElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Clears the function settings. */\n    void clear();\n\n    /** Returns the function. */\n    virtual Function function() const;\n    /** Sets the function. */\n    virtual void setFunction(Function function);\n    /** Returns the response. */\n    virtual Response response() const;\n    /** Sets the response. */\n    virtual void setResponse(Response response);\n\n    /** Returns the ID. */\n    virtual QString id() const;\n    /** Sets the ID. */\n    virtual void setID(const QString &id);\n\n    /** Returns the name. */\n    virtual QString name() const;\n    /** Sets the name. */\n    virtual void setName(const QString &name);\n\n  public:\n    /** Some limits for the function element. */\n    struct Limit: public Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int idLength()   { return 24; }\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 7; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int function() { return 0x0000; }\n      static constexpr unsigned int response() { return 0x0001; }\n      static constexpr unsigned int idLength() { return 0x0002; }\n      static constexpr unsigned int id()       { return 0x0003; }\n      static constexpr unsigned int name()     { return 0x000f; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the list of five-tone functions for all AnyTone codeplugs.\n   *\n   * Memory representation of the function list (size 0x0200 bytes):\n   * @verbinclude anytone_5tonefunctionlist.txt */\n  class FiveToneFunctionListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    FiveToneFunctionListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    FiveToneFunctionListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0200; }\n\n    void clear();\n\n    /** Returns the pointer to the n-th function setting. */\n    virtual uint8_t *function(unsigned int n) const;\n\n  public:\n    /** Some limits for the list. */\n    struct Limit {\n      static constexpr unsigned int numFunctions() { return 16; }     ///< The max number of functions.\n    };\n  };\n\n\n  /** Represents the base-class for 5Tone settings for all AnyTone codeplugs.\n   *\n   * Memory encoding of the settings (size 0x0080 bytes):\n   * @verbinclude anytone_5tonesettings.txt */\n  class FiveToneSettingsElement: public Element\n  {\n  public:\n    /** Possible responses to decoded 5-tone codes. */\n    enum class Response {\n      None = 0, Tone, ToneRespond\n    };\n    /** Possible 5-tone encoding standards. */\n    typedef enum FiveToneIDElement::Standard Standard;\n\n  protected:\n    /** Hidden constructor. */\n    FiveToneSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    FiveToneSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0080; }\n\n    /** Resets the 5tone settings. */\n    void clear();\n\n    /** Returns the decoding response. */\n    virtual Response decodingResponse() const;\n    /** Sets the decoding response. */\n    virtual void setDecodingResponse(Response response);\n\n    /** Returns the decoding standard. */\n    virtual Standard decodingStandard() const;\n    /** Sets the decoding standard. */\n    virtual void setDecodingStandard(Standard standard);\n\n    /** Returns the decoding tone duration in ms. */\n    virtual Interval decodingToneDuration() const;\n    /** Sets the decoding tone duration in ms. */\n    virtual void setDecodingToneDuration(const Interval &ms);\n\n    /** Returns the 5tone radio ID. */\n    virtual QString id() const;\n    /** Sets the 5tone radio ID. */\n    virtual void setID(const QString &id);\n\n    /** Returns the post-encode delay in ms. */\n    virtual Interval postEncodeDelay() const;\n    /** Sets the post-encode delay in ms. */\n    virtual void setPostEncodeDelay(const Interval &ms);\n\n    /** Returns @c true if the PTT ID is set. */\n    virtual bool hasPTTID() const;\n    /** Returns the PTT ID. */\n    virtual unsigned pttID() const;\n    /** Sets the PTT ID [5,75]. */\n    virtual void setPTTID(unsigned id);\n    /** Clears the PTT ID. */\n    virtual void clearPTTID();\n\n    /** Returns the auto-reset time in seconds. */\n    virtual Interval autoResetTime() const;\n    /** Sets the auto-reset time in seconds. */\n    virtual void setAutoResetTime(const Interval &s);\n\n    /** Returns the first delay in ms. */\n    virtual Interval firstDelay() const;\n    /** Sets the first delay in ms. */\n    virtual void setFirstDelay(const Interval &ms);\n\n    /** Returns @c true if the sidetone is enabled. */\n    virtual bool sidetoneEnabled() const;\n    /** Enables/disables side tone. */\n    virtual void enableSidetone(bool enable);\n    /** Returns the stop code [0,15]. */\n    virtual unsigned stopCode() const;\n    /** Sets the stop code. */\n    virtual void setStopCode(unsigned code);\n    /** Returns the stop time in ms. */\n    virtual Interval stopTime() const;\n    /** Sets the stop time in ms. */\n    virtual void setStopTime(const Interval &ms);\n    /** Returns the decode time in ms. */\n    virtual Interval decodeTime() const;\n    /** Sets the decode time in ms. */\n    virtual void setDecodeTime(const Interval &ms);\n    /** Returns the delay after stop in ms. */\n    virtual Interval delayAfterStop() const;\n    /** Sets the delay after stop in ms. */\n    virtual void setDelayAfterStop(const Interval &ms);\n    /** Returns the pre time in ms. */\n    virtual Interval preTime() const;\n    /** Sets the pre time in ms. */\n    virtual void setPreTime(const Interval &ms);\n\n    /** Returns the BOT standard. */\n    virtual Standard botStandard() const;\n    /** Sets the BOT standard. */\n    virtual void setBOTStandard(Standard standard);\n    /** Returns the BOT tone duration in ms. */\n    virtual Interval botToneDuration() const;\n    /** Sets the BOT tone duration in ms. */\n    virtual void setBOTToneDuration(const Interval &ms);\n    /** Returns the 5tone BOT ID. */\n    virtual QString botID() const;\n    /** Sets the 5tone BOT ID. */\n    virtual void setBOTID(const QString &id);\n\n    /** Returns the EOT standard. */\n    virtual Standard eotStandard() const;\n    /** Sets the EOT standard. */\n    virtual void setEOTStandard(Standard standard);\n    /** Returns the EOT tone duration in ms. */\n    virtual Interval eotToneDuration() const;\n    /** Sets the EOT tone duration in ms. */\n    virtual void setEOTToneDuration(const Interval &ms);\n    /** Returns the 5tone EOT ID. */\n    virtual QString eotID() const;\n    /** Sets the 5tone EOT ID. */\n    virtual void setEOTID(const QString &id);\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: public Element::Limit {\n      /** Maximum ID length. */\n      static constexpr unsigned int idLength() { return 14; }\n      /** Maximum BOT ID length. */\n      static constexpr unsigned int botIdLength() { return 24; }\n      /** Maximum EOT ID length. */\n      static constexpr unsigned int eotIdLength() { return 24; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int decodingResponse()     { return 0x0021; }\n      static constexpr unsigned int decodingStandard()     { return 0x0022; }\n      static constexpr unsigned int idLength()             { return 0x0023; }\n      static constexpr unsigned int decodingToneDuration() { return 0x0024; }\n      static constexpr unsigned int id()                   { return 0x0025; }\n      static constexpr unsigned int postDecodeDelay()      { return 0x002c; }\n      static constexpr unsigned int pttId()                { return 0x002d; }\n      static constexpr unsigned int autoResetTime()        { return 0x002e; }\n      static constexpr unsigned int firstDelay()           { return 0x002f; }\n      static constexpr unsigned int sidetoneEnabled()      { return 0x0030; }\n      static constexpr unsigned int stopCode()             { return 0x0032; }\n      static constexpr unsigned int stopTime()             { return 0x0033; }\n      static constexpr unsigned int decodeTime()           { return 0x0034; }\n      static constexpr unsigned int delayAfterStop()       { return 0x0035; }\n      static constexpr unsigned int preTime()              { return 0x0036; }\n      static constexpr unsigned int botStandard()          { return 0x0041; }\n      static constexpr unsigned int botIdLength()          { return 0x0042; }\n      static constexpr unsigned int botToneDuration()      { return 0x0043; }\n      static constexpr unsigned int botId()                { return 0x0044; }\n      static constexpr unsigned int eotStandard()          { return 0x0061; }\n      static constexpr unsigned int eotIdLength()          { return 0x0062; }\n      static constexpr unsigned int eotToneDuration()      { return 0x0063; }\n      static constexpr unsigned int eotId()                { return 0x0064; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base-class for a 2-tone ID for all AnyTone codeplugs.\n   *\n   * Memory encoding of the ID (size 0x0020 bytes):\n   * @verbinclude anytone_2toneid.txt */\n  class TwoToneIDElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    TwoToneIDElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    TwoToneIDElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Resets the ID. */\n    void clear();\n\n    /** Returns the first tone of the sequence. */\n    virtual double firstTone() const;\n    /** Sets the first tone of the sequence. */\n    virtual void setFirstTone(double f);\n\n    /** Returns the second tone of the sequence. */\n    virtual double secondTone() const;\n    /** Sets the second tone of the sequence. */\n    virtual void setSecondTone(double f);\n\n    /** Returns the name of the function. */\n    virtual QString name() const;\n    /** Sets the name of the function. */\n    virtual void setName(const QString &name);\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      static constexpr unsigned int nameLength() { return 7; }          ///< Maximum name length.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int firstTone()  { return 0x0000; }\n      static constexpr unsigned int secondTone() { return 0x0002; }\n      static constexpr unsigned int name()       { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the two-tone ID bitmap, indicating the which two-tone IDs are valid. */\n  class TwoToneIDBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    TwoToneIDBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    TwoToneIDBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n  };\n\n  /** Represents the base-class for a 2-tone function for all AnyTone codeplugs.\n   *\n   * Memory encoding of the function (size 0x0020 bytes):\n   * @verbinclude anytone_2tonefunction.txt */\n  class TwoToneFunctionElement: public Element\n  {\n  public:\n    /** Possible responses to a decode. */\n    enum class Response {\n      None = 0, Tone, ToneRespond\n    };\n\n  protected:\n    /** Hidden constructor. */\n    TwoToneFunctionElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    TwoToneFunctionElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the function. */\n    void clear();\n\n    /** Returns the first tone of the sequence. */\n    virtual double firstTone() const;\n    /** Sets the first tone of the sequence. */\n    virtual void setFirstTone(double f);\n\n    /** Returns the second tone of the sequence. */\n    virtual double secondTone() const;\n    /** Sets the second tone of the sequence. */\n    virtual void setSecondTone(double f);\n\n    /** Returns the response. */\n    virtual Response response() const;\n    /** Sets the response. */\n    virtual void setResponse(Response resp);\n\n    /** Returns the name of the function. */\n    virtual QString name() const;\n    /** Sets the name of the function. */\n    virtual void setName(const QString &name);\n\n  public:\n    /** Some limits of the element. */\n    struct Limit {\n      static constexpr unsigned int nameLength() { return 7; }      ///< Maximum name length.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int firstTone()  { return 0x0000; }\n      static constexpr unsigned int secondTone() { return 0x0002; }\n      static constexpr unsigned int response()   { return 0x0004; }\n      static constexpr unsigned int name()       { return 0x0005; }\n      /// @endcond\n    };\n  };\n\n  /** Rerpesents the two-tone function bitmap, indicating which two-tone functions are valid. */\n  class TwoToneFunctionBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    TwoToneFunctionBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    TwoToneFunctionBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n  };\n\n  /** Represents the base class of 2-tone settings for all AnyTone codeplugs.\n   *\n   * Memory encoding of the settings (size 0x0010 bytes):\n   * @verbinclude anytone_2tonesettings.txt */\n  class TwoToneSettingsElement : public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    TwoToneSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    TwoToneSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns the first tone duration in ms. */\n    virtual Interval firstToneDuration() const;\n    /** Sets the first tone duration in ms. */\n    virtual void setFirstToneDuration(const Interval &ms);\n\n    /** Returns the second tone duration in ms. */\n    virtual Interval secondToneDuration() const;\n    /** Sets the second tone duration in ms. */\n    virtual void setSecondToneDuration(const Interval &ms);\n\n    /** Returns the long tone duration in ms. */\n    virtual Interval longToneDuration() const;\n    /** Sets the long tone duration in ms. */\n    virtual void setLongToneDuration(const Interval &ms);\n\n    /** Returns the gap duration in ms. */\n    virtual Interval gapDuration() const;\n    /** Sets the gap duration in ms. */\n    virtual void setGapDuration(const Interval &ms);\n\n    /** Returns the auto-reset time in seconds. */\n    virtual Interval autoResetTime() const;\n    /** Sets the auto-reset time in seconds. */\n    virtual void setAutoResetTime(const Interval &sec);\n\n    /** Returns @c true if the sidetone is enabled. */\n    virtual bool sidetone() const;\n    /** Enables/disables the sidetone. */\n    virtual void enableSidetone(bool enable);\n\n  protected:\n    /** Internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int firstToneDuration()  { return 0x0009; }\n      static constexpr unsigned int secondToneDuration() { return 0x000a; }\n      static constexpr unsigned int longToneDuration()   { return 0x000b; }\n      static constexpr unsigned int gapDuration()        { return 0x000c; }\n      static constexpr unsigned int autoResetTime()      { return 0x000d; }\n      static constexpr unsigned int sidetone()           { return 0x000e; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class of DTMF settings for all AnyTone codeplugs.\n   *\n   * Memory representation of the settings (size 0x0050):\n   * @verbinclude anytone_dtmfsettings.txt */\n  class DTMFSettingsElement: public Element\n  {\n  public:\n    /** Possible responses to a DTMF decode. */\n    enum Response {\n      None=0, Tone, ToneRespond\n    };\n\n  protected:\n    /** Hidden constructor. */\n    DTMFSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit DTMFSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0050; }\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns the interval/repeat symbol [0,15]. */\n    virtual unsigned intervalSymbol() const;\n    /** Sets the interval/repeat symbol [0,15]. */\n    virtual void setIntervalSymbol(unsigned symb);\n\n    /** Returns the group code [0,15]. */\n    virtual unsigned groupCode() const;\n    /** Sets the group code [0,15]. */\n    virtual void setGroupCode(unsigned symb);\n\n    /** Returns the response to a DMTF decode. */\n    virtual Response response() const;\n    /** Sets the response to a DTMF decode. */\n    virtual void setResponse(Response resp);\n\n    /** Returns the pre time in ms. */\n    virtual Interval preTime() const;\n    /** Sets the pre time in ms. */\n    virtual void setPreTime(const Interval &ms);\n\n    /** Returns the first digit duration in ms. */\n    virtual Interval firstDigitDuration() const;\n    /** Sets the first digit duration in ms. */\n    virtual void setFirstDigitDuration(const Interval &ms);\n\n    /** Returns the auto reset time in seconds. */\n    virtual Interval autoResetTime() const;\n    /** Sets the auto reset time in seconds. */\n    virtual void setAutoResetTime(const Interval &sec);\n\n    /** Returns the radio ID. */\n    virtual QString id() const;\n    /** Sets the radio ID. */\n    virtual void setID(const QString &id);\n\n    /** Returns the post encoding delay in ms. */\n    virtual Interval postEncodingDelay() const;\n    /** Sets the post encoding delay in ms. */\n    virtual void setPostEncodingDelay(const Interval &ms);\n\n    /** Returns the PTT ID pause in seconds. */\n    virtual Interval pttIDPause() const;\n    /** Sets the PTT ID pause in seconds. */\n    virtual void setPTTIDPause(const Interval &sec);\n\n    /** Returns @c true if the PTT ID is enabled. */\n    virtual bool pttIDEnabled() const;\n    /** Enables/disables the PTT ID. */\n    virtual void enablePTTID(bool enable);\n\n    /** Returns the D-code pause in seconds. */\n    virtual Interval dCodePause() const;\n    /** Sets the D-code pause in seconds. */\n    virtual void setDCodePause(const Interval &sec);\n\n    /** Returns @c true if the sidetone is enabled. */\n    virtual bool sidetone() const;\n    /** Enables/disables the sidetone. */\n    virtual void enableSidetone(bool enable);\n\n    /** Returns the BOT ID. */\n    virtual QString botID() const;\n    /** Sets the BOT ID. */\n    virtual void setBOTID(const QString &id);\n\n    /** Returns the EOT ID. */\n    virtual QString eotID() const;\n    /** Sets the EOT ID. */\n    virtual void setEOTID(const QString &id);\n\n    /** Returns the remote kill ID. */\n    virtual QString remoteKillID() const;\n    /** Sets the remote kill ID. */\n    virtual void setRemoteKillID(const QString &id);\n\n    /** Returns the remote stun ID. */\n    virtual QString remoteStunID() const;\n    /** Sets the remote stun ID. */\n    virtual void setRemoteStunID(const QString &id);\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: public Element::Limit {\n      /** Maximum ID length. */\n      static constexpr unsigned int idLength() { return 3; }\n      /** Maximum BOT ID length. */\n      static constexpr unsigned int botIdLength() { return 16; }\n      /** Maximum EOT ID length. */\n      static constexpr unsigned int eotIdLength() { return 16; }\n      /** Maximum remote kill ID length. */\n      static constexpr unsigned int remoteKillIdLength() { return 16; }\n      /** Maximum remote stun ID length. */\n      static constexpr unsigned int remteStunIdLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int intervalSymbol() { return 0x0000; }\n      static constexpr unsigned int groupCode() { return 0x0001; }\n      static constexpr unsigned int response() { return 0x0002; }\n      static constexpr unsigned int preTime() { return 0x0003; }\n      static constexpr unsigned int firstDigitDuration() { return 0x0004; }\n      static constexpr unsigned int autoResetTime() { return 0x0005; }\n      static constexpr unsigned int id() { return 0x0006; }\n      static constexpr unsigned int postEncodingDelay() { return 0x0009; }\n      static constexpr unsigned int pttIDPause() { return 0x000a; }\n      static constexpr unsigned int pttIDEnabled() { return 0x000b; }\n      static constexpr unsigned int dCodePause() { return 0x000c; }\n      static constexpr unsigned int sidetone() { return 0x000d; }\n      static constexpr unsigned int botID() { return 0x0010; }\n      static constexpr unsigned int eotID() { return 0x0020; }\n      static constexpr unsigned int remoteKillID() { return 0x0030; }\n      static constexpr unsigned int remoteStunID() { return 0x0040; }\n      /// @endcond\n    };\n  };\n\n  /** Represents a list of DTMF IDs to be send.\n   *\n   * Memory encoding of the DTMF IDs (size 0x0100 bytes):\n   * @verbinclude anytone_dtmfidlist.txt */\n  class DTMFIDListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFIDListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFIDListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    void clear();\n\n    /** Returns @c true, if the n-th number is set. */\n    virtual bool hasNumber(unsigned int n) const;\n    /** Returns the n-th number. */\n    virtual QString number(unsigned int n) const;\n    /** Sets the n-th number. */\n    virtual void setNumber(unsigned int n, const QString &number);\n    /** Clears the n-th number. */\n    virtual void clearNumber(unsigned int n);\n\n  public:\n    /** Some limits of the list. */\n    struct Limit {\n      static constexpr unsigned int numEntries()   { return 16; }      ///< The maximum number of entries in the list.\n      static constexpr unsigned int numberLength() { return 16; }      ///< The maximum length of the numbers.\n    };\n  };\n\n\n  /** Represents a list of 100 FM broad cast channels.\n   *\n   * Memory representation of the channel list (size 0x0200 bytes):\n   * @verbinclude anytone_wfmchannellist.txt */\n  class WFMChannelListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    WFMChannelListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit WFMChannelListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0200; }\n\n    void clear();\n\n    /** Returns @c true, if the n-th channel is set. */\n    virtual bool hasChannel(unsigned int n) const;\n    /** Returns the n-th channel frequency. */\n    virtual Frequency channel(unsigned int n) const;\n    /** Sets the n-th channel frequency. */\n    virtual void setChannel(unsigned int n, Frequency freq);\n    /** Clears the n-th channel frequency. */\n    virtual void clearChannel(unsigned int n);\n\n  public:\n    /** Some limits for the channel list. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 100; }     ///< Maximum number of channels in the list.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int betweenChannels() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap, indicating which WFM (FM broadcast) channels are valid. */\n  class WFMChannelBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    WFMChannelBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    WFMChannelBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n  /** Represents the WFM (FM broadcast) VFO frequency. */\n  class WFMVFOElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    WFMVFOElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    WFMVFOElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    void clear();\n\n    /** Returns the VFO frequency. */\n    virtual Frequency frequency() const;\n    /** Sets the VFO frequency. */\n    virtual void setFrequency(Frequency freq);\n  };\n\n\n  /** Represents a list of DMR encryption keys. */\n  class DMREncryptionKeyListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DMREncryptionKeyListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DMREncryptionKeyListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    void clear();\n\n    /** Returns @c true if the n-th id is set. */\n    virtual bool hasKey(unsigned int n) const;\n    /** Returns the ID of the encryption key. */\n    virtual QByteArray key(unsigned int n) const;\n    /** Sets the ID of the encryption key. */\n    virtual void setKey(unsigned int n, const BasicEncryptionKey &key);\n    /** Clears the n-th id. */\n    virtual void clearKey(unsigned int n);\n\n  public:\n    /** Some limits for the list. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 32; }      ///< Maximum number of DMR encryption key IDs.\n    };\n\n  protected:\n    /** Some internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int betweenKeys() { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents a list of 'enhanced' DMR encryption keys.\n   * Important, there is no enhancement in this encryption, the\n   * used key is still derived from a 16bit seed. The effective encryption is still only 16bit. */\n  class EnhancedEncryptionKeyListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EnhancedEncryptionKeyListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    EnhancedEncryptionKeyListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0500; }\n\n    void clear();\n\n    /** Returns the n-th key. */\n    QByteArray key(unsigned int n) const;\n    /** Sets the n-th key. */\n    void setKey(unsigned int n, const QByteArray &key);\n\n  public:\n    /** Some limits of the list. */\n    struct Limit {\n      static constexpr unsigned numEntries() { return DMREncryptionKeyListElement::Limit::numEntries(); } ///< Maximum number of keys.\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int keys() { return 0x0010; }\n      static constexpr unsigned int betweenKeys() { return 0x0028; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the base class for entries to the contact indices in all AnyTone codeplugs.\n   *\n   * Memory representation of the entry (size 0x0008):\n   * @verbinclude anytone_contactmapentry.txt */\n  class ContactMapElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactMapElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ContactMapElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0008; }\n\n    /** Clears the entry. */\n    void clear();\n    /** Returns @c true if the contact map is valid. */\n    bool isValid() const;\n\n    /** Returns @c true if the entry is a group call. */\n    virtual bool isGroup() const;\n    /** Returns the id. */\n    virtual unsigned id() const;\n    /** Encodes ID and group call flag. */\n    virtual void setID(unsigned id, bool group=false);\n    /** Returns the index. */\n    virtual unsigned index() const;\n    /** Sets the index. */\n    virtual void setIndex(unsigned idx);\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int id()    { return 0x0000; }\n      static constexpr unsigned int index() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\nprotected:\n  /** Hidden constructor. */\n  AnytoneCodeplug(const QString &label, QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~AnytoneCodeplug();\n\n  /** Clears and resets the complete codeplug to some default values. */\n  virtual void clear();\n\n  Config *preprocess(Config *config, const ErrorStack &err) const;\n  bool encode(Config *config, const Flags &flags, const ErrorStack &err);\n\n  bool decode(Config *config, const ErrorStack &err);\n  bool postprocess(Config *config, const ErrorStack &err) const;\n\nprotected:\n  virtual bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  /** Allocates the bitmaps. This is also performed during a clear. */\n  virtual bool allocateBitmaps() = 0;\n  /** Sets all bitmaps for the given config. */\n  virtual void setBitmaps(Context &ctx) = 0;\n\n  /** Allocate all code-plug elements that must be written back to the device to maintain a working\n   * codeplug. These elements might be updated during encoding. */\n  virtual void allocateUpdated() = 0;\n  /** Allocate all code-plug elements that must be downloaded for decoding. All code-plug elements\n   * within the radio that are not represented within the common Config are omitted. */\n  virtual void allocateForDecoding() = 0;\n  /** Allocate all code-plug elements that are defined through the common Config. */\n  virtual void allocateForEncoding() = 0;\n\n  /** Encodes the given config (via context) to the binary codeplug. */\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the downloaded codeplug.\n   *\n   * Decoding consists of two steps: First, creation of all config objects and in a second step\n   * resolving all references within the codeplug. The latter step is called linking. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Creates all config objects from the downloaded codeplug. */\n  virtual bool createElements(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all previously created config objects. */\n  virtual bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\nprotected:\n  /** Holds the image label. */\n  QString _label;\n\n  // Allow access to protected allocation methods.\n  friend class AnytoneRadio;\n};\n\n#endif // ANYTONECODEPLUG_HH\n"
  },
  {
    "path": "lib/anytone_extension.cc",
    "content": "#include \"anytone_extension.hh\"\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAPRSFrequency\n * ********************************************************************************************* */\nAnytoneAPRSFrequency::AnytoneAPRSFrequency(QObject *parent)\n  : ConfigObject(parent), _frequency(Frequency::fromHz(0))\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneAPRSFrequency::clone() const {\n  AnytoneAPRSFrequency *obj = new AnytoneAPRSFrequency();\n  if (! obj->copy(*this)) {\n    obj->deleteLater();\n    return nullptr;\n  }\n  return obj;\n}\n\nFrequency\nAnytoneAPRSFrequency::frequency() const {\n  return _frequency;\n}\nvoid\nAnytoneAPRSFrequency::setFrequency(Frequency freq) {\n  if (_frequency == freq)\n    return;\n  _frequency = freq;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAPRSFrequencyRef\n * ********************************************************************************************* */\nAnytoneAPRSFrequencyRef::AnytoneAPRSFrequencyRef(QObject *parent)\n  : ConfigObjectReference(AnytoneAPRSFrequency::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAPRSFrequencyList\n * ********************************************************************************************* */\nAnytoneAPRSFrequencyList::AnytoneAPRSFrequencyList(QObject *parent)\n  : ConfigObjectList(AnytoneAPRSFrequency::staticMetaObject, parent)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneAPRSFrequencyList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err);\n  return new AnytoneAPRSFrequency();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneChannelExtension\n * ********************************************************************************************* */\nAnytoneChannelExtension::AnytoneChannelExtension(QObject *parent)\n  : ConfigExtension(parent), _frequencyCorrection(0), _handsFree(false),\n    _fmAPRSFrequency(new AnytoneAPRSFrequencyRef(this)), _aprsPTT(APRSPTT::Off)\n{\n  // pass...\n}\n\nint\nAnytoneChannelExtension::frequencyCorrection() const {\n  return _frequencyCorrection;\n}\nvoid\nAnytoneChannelExtension::setFrequencyCorrection(int corr) {\n  if (corr == _frequencyCorrection)\n    return;\n  _frequencyCorrection = corr;\n  emit modified(this);\n}\n\nbool\nAnytoneChannelExtension::handsFree() const {\n  return _handsFree;\n}\nvoid\nAnytoneChannelExtension::enableHandsFree(bool enable) {\n  if (enable == _handsFree)\n    return;\n  _handsFree = enable;\n  emit modified(this);\n}\n\nAnytoneAPRSFrequencyRef *\nAnytoneChannelExtension::fmAPRSFrequency() const {\n  return _fmAPRSFrequency;\n}\n\nAnytoneChannelExtension::APRSPTT\nAnytoneChannelExtension::aprsPTT() const {\n  return _aprsPTT;\n}\nvoid\nAnytoneChannelExtension::setAPRSPTT(APRSPTT mode) {\n  if (_aprsPTT == mode)\n    return;\n  _aprsPTT = mode;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAnalogChannelExtension\n * ********************************************************************************************* */\nAnytoneFMChannelExtension::AnytoneFMChannelExtension(QObject *parent)\n  : AnytoneChannelExtension(parent), _rxCustomCTCSS(false),\n    _txCustomCTCSS(false), _customCTCSS(0), _squelchMode(SquelchMode::Carrier),\n    _scramblerFrequency()\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneFMChannelExtension::clone() const {\n  AnytoneFMChannelExtension *ext = new AnytoneFMChannelExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nAnytoneFMChannelExtension::rxCustomCTCSS() const {\n  return _rxCustomCTCSS;\n}\nvoid\nAnytoneFMChannelExtension::enableRXCustomCTCSS(bool enable) {\n  if (enable == _rxCustomCTCSS)\n    return;\n  _rxCustomCTCSS = enable;\n  emit modified(this);\n}\nbool\nAnytoneFMChannelExtension::txCustomCTCSS() const {\n  return _txCustomCTCSS;\n}\nvoid\nAnytoneFMChannelExtension::enableTXCustomCTCSS(bool enable) {\n  if (enable == _txCustomCTCSS)\n    return;\n  _txCustomCTCSS = enable;\n  emit modified(this);\n}\ndouble\nAnytoneFMChannelExtension::customCTCSS() const {\n  return _customCTCSS;\n}\nvoid\nAnytoneFMChannelExtension::setCustomCTCSS(double freq) {\n  if (freq == _customCTCSS)\n    return;\n  _customCTCSS = freq;\n  emit modified(this);\n}\n\nAnytoneFMChannelExtension::SquelchMode\nAnytoneFMChannelExtension::squelchMode() const {\n  return _squelchMode;\n}\nvoid\nAnytoneFMChannelExtension::setSquelchMode(SquelchMode mode) {\n  if (mode == _squelchMode)\n    return;\n  _squelchMode = mode;\n  emit modified(this);\n}\n\nFrequency\nAnytoneFMChannelExtension::scramblerFrequency() const {\n  return _scramblerFrequency;\n}\nvoid\nAnytoneFMChannelExtension::setScramblerFrequency(const Frequency &f) {\n  if (f == _scramblerFrequency)\n    return;\n  _scramblerFrequency = f;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneDigitalChannelExtension\n * ********************************************************************************************* */\nAnytoneDMRChannelExtension::AnytoneDMRChannelExtension(QObject *parent)\n  : AnytoneChannelExtension(parent),  _adaptiveTDMA(false), _throughMode(false), _crc(true)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneDMRChannelExtension::clone() const {\n  AnytoneDMRChannelExtension *ext = new AnytoneDMRChannelExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nbool\nAnytoneDMRChannelExtension::adaptiveTDMA() const {\n  return _adaptiveTDMA;\n}\nvoid\nAnytoneDMRChannelExtension::enableAdaptiveTDMA(bool enable) {\n  if (enable == _adaptiveTDMA)\n    return;\n  _adaptiveTDMA = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDMRChannelExtension::throughMode() const {\n  return _throughMode;\n}\nvoid\nAnytoneDMRChannelExtension::enableThroughMode(bool enable) {\n  if (enable == _throughMode)\n    return;\n  _throughMode = enable;\n  emit modified(this);\n}\n\n\nbool AnytoneDMRChannelExtension::crcEnabled() const {\n  return _crc;\n}\n\nvoid AnytoneDMRChannelExtension::enableCRC(bool enable) {\n  if (enable == _crc)\n    return;\n  _crc = enable;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneZoneExtension\n * ********************************************************************************************* */\nAnytoneZoneExtension::AnytoneZoneExtension(QObject *parent)\n  : ConfigExtension(parent), _hidden(false)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneZoneExtension::clone() const {\n  AnytoneZoneExtension *obj = new AnytoneZoneExtension();\n  if (! obj->copy(*this)) {\n    obj->deleteLater();\n    return nullptr;\n  }\n  return obj;\n}\n\nbool\nAnytoneZoneExtension::hidden() const {\n  return _hidden;\n}\nvoid\nAnytoneZoneExtension::enableHidden(bool enable) {\n  if (_hidden == enable)\n    return;\n  _hidden = enable;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneContactExtension\n * ********************************************************************************************* */\nAnytoneContactExtension::AnytoneContactExtension(QObject *parent)\n  : ConfigExtension(parent), _alertType(AlertType::None)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneContactExtension::clone() const {\n  AnytoneContactExtension *obj = new AnytoneContactExtension();\n  if (! obj->copy(*this)) {\n    obj->deleteLater();\n    return nullptr;\n  }\n  return obj;\n}\n\nAnytoneContactExtension::AlertType\nAnytoneContactExtension::alertType() const {\n  return _alertType;\n}\nvoid\nAnytoneContactExtension::setAlertType(AlertType type) {\n  _alertType = type;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneFMAPRSSettingsExtension\n * ********************************************************************************************* */\nAnytoneFMAPRSSettingsExtension::AnytoneFMAPRSSettingsExtension(QObject *parent)\n  : ConfigExtension(parent), _txDelay(Interval::fromMilliseconds(60)),\n    _preWaveDelay(Interval::fromMilliseconds(0)), _passAll(false), _reportPosition(false),\n    _reportMicE(false), _reportObject(false), _reportItem(false), _reportMessage(false),\n    _reportWeather(false), _reportNMEA(false), _reportStatus(false), _reportOther(false),\n    _frequencies(new AnytoneAPRSFrequencyList(this))\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneFMAPRSSettingsExtension::clone() const {\n  AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nInterval\nAnytoneFMAPRSSettingsExtension::txDelay() const {\n  return _txDelay;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::setTXDelay(Interval intv) {\n  if (_txDelay == intv)\n    return;\n  _txDelay = intv;\n  emit modified(this);\n}\n\nInterval\nAnytoneFMAPRSSettingsExtension::preWaveDelay() const {\n  return _preWaveDelay;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::setPreWaveDelay(Interval intv) {\n  if (_preWaveDelay == intv)\n    return;\n  _preWaveDelay = intv;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::passAll() const {\n  return _passAll;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enablePassAll(bool enable) {\n  if (_passAll == enable)\n    return;\n  _passAll = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportPosition() const {\n  return _reportPosition;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportPosition(bool enable) {\n  if (_reportPosition == enable)\n    return;\n  _reportPosition = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportMicE() const {\n  return _reportMicE;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportMicE(bool enable) {\n  if (_reportMicE == enable)\n    return;\n  _reportMicE = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportObject() const {\n  return _reportObject;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportObject(bool enable) {\n  if (_reportObject == enable)\n    return;\n  _reportObject = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportItem() const {\n  return _reportItem;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportItem(bool enable) {\n  if (_reportItem == enable)\n    return;\n  _reportItem = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportMessage() const {\n  return _reportMessage;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportMessage(bool enable) {\n  if (_reportMessage == enable)\n    return;\n  _reportMessage = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportWeather() const {\n  return _reportWeather;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportWeather(bool enable) {\n  if (_reportWeather == enable)\n    return;\n  _reportWeather = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportNMEA() const {\n  return _reportNMEA;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportNMEA(bool enable) {\n  if (_reportNMEA == enable)\n    return;\n  _reportNMEA = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportStatus() const {\n  return _reportStatus;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportStatus(bool enable) {\n  if (_reportStatus == enable)\n    return;\n  _reportStatus = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneFMAPRSSettingsExtension::reportOther() const {\n  return _reportOther;\n}\nvoid\nAnytoneFMAPRSSettingsExtension::enableReportOther(bool enable) {\n  if (_reportOther == enable)\n    return;\n  _reportOther = enable;\n  emit modified(this);\n}\n\nAnytoneAPRSFrequencyList *\nAnytoneFMAPRSSettingsExtension::frequencies() const {\n  return _frequencies;\n}\n"
  },
  {
    "path": "lib/anytone_extension.hh",
    "content": "#ifndef ANYTONEEXTENSION_HH\n#define ANYTONEEXTENSION_HH\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"frequency.hh\"\n#include \"interval.hh\"\n\n#include <QTimeZone>\n\n\n\n/** Implements the config representation of an FM APRS frequency.\n * @ingroup anytone */\nclass AnytoneAPRSFrequency: public ConfigObject\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"IdPrefix\", \"af\")\n\n  Q_CLASSINFO(\"frequencyDecription\",\n              \"Transmit-frequency.\")\n\n  /** The frequency. */\n  Q_PROPERTY(Frequency frequency READ frequency WRITE setFrequency)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit AnytoneAPRSFrequency(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the transmit frequency. */\n  Frequency frequency() const;\n  /** Sets the transmit frequency. */\n  void setFrequency(Frequency freq);\n\nprotected:\n  /** The transmit frequency. */\n  Frequency _frequency;\n};\n\n\n/** Represents a reference to an APRS frequency.\n * @ingroup anytone */\nclass AnytoneAPRSFrequencyRef: public ConfigObjectReference\n{\n  Q_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneAPRSFrequencyRef(QObject *parent=nullptr);\n};\n\n\n/** Represents a list of APRS transmit frequencies.\n * @ingroup anytone */\nclass AnytoneAPRSFrequencyList: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit AnytoneAPRSFrequencyList(QObject *parent=nullptr);\n\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err);\n};\n\n\n/** Implements the common properties for analog and digital AnyTone channels.\n * This class cannot be instantiated directly, use one of the derived classes.\n * @ingroup anytone */\nclass AnytoneChannelExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** Holds the frequency correction in some unknown units. */\n  Q_PROPERTY(int frequencyCorrection READ frequencyCorrection WRITE setFrequencyCorrection)\n  /** If @c true, the hands-free feature is enabled for this channel. */\n  Q_PROPERTY(bool handsFree READ handsFree WRITE enableHandsFree)\n  /** A reference to the FM APRS frequency. If not set, the default will be used. */\n  Q_PROPERTY(AnytoneAPRSFrequencyRef *fmAPRSFrequency READ fmAPRSFrequency())\n  /** Specifies if and when the position is send via the associated APRS system, once the PTT is\n   * pressed. */\n  Q_PROPERTY(APRSPTT aprsPTT READ aprsPTT WRITE setAPRSPTT)\n\npublic:\n  /** Possible APRS PTT modes. */\n  enum class APRSPTT{\n    Off, Start, End\n  };\n  Q_ENUM(APRSPTT)\n\nprotected:\n  /** Hidden constructor. */\n  explicit AnytoneChannelExtension(QObject *parent=nullptr);\n\npublic:\n  /** Returns the frequency correction in some unknown units. */\n  int frequencyCorrection() const;\n  /** Sets the frequency correction. */\n  void setFrequencyCorrection(int corr);\n\n  /** Returns @c true if the hands-free feature is enabled. */\n  bool handsFree() const;\n  /** Enables/disables the hands-free feature for this channel. */\n  void enableHandsFree(bool enable);\n\n  /** Holds a reference to the FM APRS frequency to be used if FM APRS is enabled on the channel. */\n  AnytoneAPRSFrequencyRef *fmAPRSFrequency() const;\n\n  /** Holds the APRS PTT mode. That his, if and when the APRS information is send via the\n   * associated APRS system. */\n  APRSPTT aprsPTT() const;\n  /** Sets the APRS PTT mode. */\n  void setAPRSPTT(APRSPTT mode);\n\nprotected:\n  /** The frequency correction. */\n  int _frequencyCorrection;\n  /** If @c true, the hands-free feature is enabled for this channel. */\n  bool _handsFree;\n  /** A reference to the FM APRS frequency. */\n  AnytoneAPRSFrequencyRef *_fmAPRSFrequency;\n  /** Holds the APRS PTT mode. */\n  APRSPTT _aprsPTT;\n};\n\n\n/** Implements the settings extension for FM channels on AnyTone devices.\n * @ingroup anytone */\nclass AnytoneFMChannelExtension: public AnytoneChannelExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the custom CTCSS tone is used for RX (open squelch). */\n  Q_PROPERTY(bool rxCustomCTCSS READ rxCustomCTCSS WRITE enableRXCustomCTCSS)\n  /** If @c true, the custom CTCSS tone is transmitted. */\n  Q_PROPERTY(bool txCustomCTCSS READ txCustomCTCSS WRITE enableTXCustomCTCSS)\n  /** Holds the custom CTCSS tone frequency in Hz. Resolution is 0.1Hz */\n  Q_PROPERTY(double customCTCSS READ customCTCSS WRITE setCustomCTCSS)\n  /** Holds the squelch mode. */\n  Q_PROPERTY(SquelchMode squelchMode READ squelchMode WRITE setSquelchMode)\n  /** If @c true, the analog scrabler is enabled. */\n  Q_PROPERTY(Frequency scramblerFrequency READ scramblerFrequency WRITE setScramblerFrequency)\n\npublic:\n  /** Possible squelch mode settings. */\n  enum class SquelchMode {\n    Carrier = 0,\n    SubTone = 1,\n    OptSig  = 2,\n    SubToneAndOptSig = 3,\n    SubToneOrOptSig = 4\n  };\n  Q_ENUM(SquelchMode)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit AnytoneFMChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true, if the custom CTCSS frequency is used for RX (open squelch). */\n  bool rxCustomCTCSS() const;\n  /** Enables/disables usage of custom CTCSS frequency for RX. */\n  void enableRXCustomCTCSS(bool enable);\n  /** Returns @c true, if the custom CTCSS frequency is used for TX (open squelch). */\n  bool txCustomCTCSS() const;\n  /** Enables/disables usage of custom CTCSS frequency for TX. */\n  void enableTXCustomCTCSS(bool enable);\n  /** Returns the custom CTCSS frequency in Hz. Resolution is 0.1Hz. */\n  double customCTCSS() const;\n  /** Sets the custom CTCSS frequency in Hz. Resolution is 0.1Hz. */\n  void setCustomCTCSS(double freq);\n\n  /** Returns the squelch mode. */\n  SquelchMode squelchMode() const;\n  /** Sets the squelch mode. */\n  void setSquelchMode(SquelchMode mode);\n\n  /** Reuturns the FM scrabler carrier frequency. */\n  Frequency scramblerFrequency() const;\n  /** Sets the FM scrambler carrier frequency. */\n  void setScramblerFrequency(const Frequency &freq);\n\nprotected:\n  /** If @c true, the custom CTCSS tone is used for RX (open squelch). */\n  bool _rxCustomCTCSS;\n  /** If @c true, the custom CTCSS tone is transmitted. */\n  bool _txCustomCTCSS;\n  /** Holds the custom CTCSS tone frequency in Hz. Resolution is 0.1Hz */\n  double _customCTCSS;\n  /** Holds the squelch mode. */\n  SquelchMode _squelchMode;\n  /** Sets the FM scrambler frequency. */\n  Frequency _scramblerFrequency;\n};\n\n\n/** Implements the settings extension for DMR channels on AnyTone devices.\n * @ingroup anytone */\nclass AnytoneDMRChannelExtension: public AnytoneChannelExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the adaptive TDMA mode is enabled. This makes only sense, if @c simplexTDMA is\n   * enabled too. In this case, the radio is able to receive both simplex TDMA as well as \"normal\"\n   * simplex DMR on the channel. */\n  Q_PROPERTY(bool adaptiveTDMA READ adaptiveTDMA WRITE enableAdaptiveTDMA)\n  /** If @c true, the through mode is enabled (what ever that means). */\n  Q_PROPERTY(bool throughMode READ throughMode WRITE enableThroughMode)\n  /** If @c true, the DMR CRC check is enabled (default). */\n  Q_PROPERTY(bool crc READ crcEnabled WRITE enableCRC);\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit AnytoneDMRChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the adaptive TDMA mode is enabled. */\n  bool adaptiveTDMA() const;\n  /** Enables/disables the adaptive TDMA mode. */\n  void enableAdaptiveTDMA(bool enable);\n  /** Returns @c true if the through mode is enabled. */\n  bool throughMode() const;\n  /** Enables/disables the through mode. */\n  void enableThroughMode(bool enable);\n  /** Returns @c true if the DMR CRC check is enabled (default). */\n  bool crcEnabled() const;\n  /** Enables DMR CRC check. */\n  void enableCRC(bool enable);\n\nprotected:\n  /** If @c true, the adaptive TDMA mode is enabled. */\n  bool _adaptiveTDMA;\n  /** If @c true the through mode is enabled. */\n  bool _throughMode;\n  /** If @c true, DMR CRC check is enabled. */\n  bool _crc;\n};\n\n\n/** Implements the AnyTone extensions for zones.\n * @ingroup anytone */\nclass AnytoneZoneExtension : public ConfigExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the zone is hidden in the menu. */\n  Q_PROPERTY(bool hidden READ hidden WRITE enableHidden)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit AnytoneZoneExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the zone is hidden. */\n  bool hidden() const;\n  /** Enables/disables hidden zone. */\n  void enableHidden(bool enable);\n\nprotected:\n  /** If @c true, the zone is hidden in the menu. */\n  bool _hidden;\n};\n\n\n/** Implements the AnyTone contact extension.\n * @ingroup anytone */\nclass AnytoneContactExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** Overrides the ring flag, allows to set None, Ring and Online. */\n  Q_PROPERTY(AlertType alertType READ alertType WRITE setAlertType)\n\npublic:\n  /** Possible ring-tone types. */\n  enum class AlertType {\n    None = 0,                   ///< Alert disabled.\n    Ring = 1,                   ///< Ring tone.\n    Online = 2                  ///< WTF?\n  };\n  Q_ENUM(AlertType)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit AnytoneContactExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the alert type for the contact. */\n  AlertType alertType() const;\n  /** Sets the alert type for the contact. */\n  void setAlertType(AlertType type);\n\nprotected:\n  /** Holds the alert type for the contact. */\n  AlertType _alertType;\n};\n\n\n/** Implements some additional settings for the FM APRS system.\n * This extension gets attached to a @c APRSSystem instance.\n */\nclass AnytoneFMAPRSSettingsExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The transmit delay in milliseconds. */\n  Q_PROPERTY(Interval txDelay READ txDelay WRITE setTXDelay)\n  /** The transmit pre-wave delay in milliseconds. */\n  Q_PROPERTY(Interval preWaveDelay READ preWaveDelay WRITE setPreWaveDelay)\n  /** If @c true, all APRS messages are processed, including those with invalid CRC. */\n  Q_PROPERTY(bool passAll READ passAll WRITE enablePassAll)\n  /** If @c true, the report position flag is set. */\n  Q_PROPERTY(bool reportPosition READ reportPosition WRITE enableReportPosition)\n  /** If @c true, the report Mic-E flag is set. */\n  Q_PROPERTY(bool reportMicE READ reportMicE WRITE enableReportMicE)\n  /** If @c true, the report object flag is set. */\n  Q_PROPERTY(bool reportObject READ reportObject WRITE enableReportObject)\n  /** If @c true, the report item flag is set. */\n  Q_PROPERTY(bool reportItem READ reportItem WRITE enableReportItem)\n  /** If @c true, the report message flag is set. */\n  Q_PROPERTY(bool reportMessage READ reportMessage WRITE enableReportMessage)\n  /** If @c true, the report weather flag is set. */\n  Q_PROPERTY(bool reportWeather READ reportWeather WRITE enableReportWeather)\n  /** If @c true, the report NMEA flag is set. */\n  Q_PROPERTY(bool reportNMEA READ reportNMEA WRITE enableReportNMEA)\n  /** If @c true, the report status flag is set. */\n  Q_PROPERTY(bool reportStatus READ reportStatus WRITE enableReportStatus)\n  /** If @c true, the report other flag is set. */\n  Q_PROPERTY(bool reportOther READ reportOther WRITE enableReportOther)\n\n  /** The list of additional APRS frequencies. */\n  Q_PROPERTY(AnytoneAPRSFrequencyList *frequencies READ frequencies)\n\npublic:\n  /** Possible bandwidth settings. */\n  enum class Bandwidth {\n    Narrow = 0, Wide = 1\n  };\n  Q_ENUM(Bandwidth)\n\npublic:\n  /** Default constructor. */\n  explicit Q_INVOKABLE AnytoneFMAPRSSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the transmit delay. */\n  Interval txDelay() const;\n  /** Sets the transmit delay. */\n  void setTXDelay(Interval intv);\n\n  /** Returns the pre-wave delay in ms. */\n  Interval preWaveDelay() const;\n  /** Sets the pre-wave delay in ms. */\n  void setPreWaveDelay(Interval ms);\n\n  /** Returns @c true if all received APRS messages are processed, even those with invalid CRC. */\n  bool passAll() const;\n  /** Enables processing of all received APRS messages, including those with invalid CRC. */\n  void enablePassAll(bool enable);\n\n  /** Returns @c true if the report position flag is set. */\n  bool reportPosition() const;\n  /** Enables/disables report position flag. */\n  void enableReportPosition(bool enable);\n  /** Returns @c true if the report Mic-E flag is set. */\n  bool reportMicE() const;\n  /** Enables/disables report Mic-E flag. */\n  void enableReportMicE(bool enable);\n  /** Returns @c true if the report object flag is set. */\n  bool reportObject() const;\n  /** Enables/disables report object flag. */\n  void enableReportObject(bool enable);\n  /** Returns @c true if the report item flag is set. */\n  bool reportItem() const;\n  /** Enables/disables report item flag. */\n  void enableReportItem(bool enable);\n  /** Returns @c true if the report message flag is set. */\n  bool reportMessage() const;\n  /** Enables/disables report message flag. */\n  void enableReportMessage(bool enable);\n  /** Returns @c true if the report weather flag is set. */\n  bool reportWeather() const;\n  /** Enables/disables report weather flag. */\n  void enableReportWeather(bool enable);\n  /** Returns @c true if the report NMEA flag is set. */\n  bool reportNMEA() const;\n  /** Enables/disables report NMEA flag. */\n  void enableReportNMEA(bool enable);\n  /** Returns @c true if the report status flag is set. */\n  bool reportStatus() const;\n  /** Enables/disables report status flag. */\n  void enableReportStatus(bool enable);\n  /** Returns @c true if the report other flag is set. */\n  bool reportOther() const;\n  /** Enables/disables report other flag. */\n  void enableReportOther(bool enable);\n\n  /** Returns the list of additional FM APRS frequencies. */\n  AnytoneAPRSFrequencyList *frequencies() const;\n\nprotected:\n  /** The transmit delay. */\n  Interval _txDelay;\n  /** The pre-wave delay. */\n  Interval _preWaveDelay;\n  /** If @c true, all APRS messages are processed. */\n  bool _passAll;\n  /** If @c true the report position flag is set. */\n  bool _reportPosition;\n  /** The report Mic-E flag. */\n  bool _reportMicE;\n  /** The report object flag. */\n  bool _reportObject;\n  /** The report item flag. */\n  bool _reportItem;\n  /** The report message flag. */\n  bool _reportMessage;\n  /** The report weather flag. */\n  bool _reportWeather;\n  /** The report NMEA flag. */\n  bool _reportNMEA;\n  /** The report status flag. */\n  bool _reportStatus;\n  /** The report other flag. */\n  bool _reportOther;\n  /** The list of additional FM APRS frequencies. */\n  AnytoneAPRSFrequencyList *_frequencies;\n};\n\n#endif // ANYTONEEXTENSION_HH\n"
  },
  {
    "path": "lib/anytone_filereader.cc",
    "content": "#include \"anytone_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n#include <QtEndian>\n\n#include \"d868uv_filereader.hh\"\n#include \"d878uv_filereader.hh\"\n\n/** Internal used struct to read a CPS file header. */\nstruct __attribute__((packed)) file_header {\n  char version[5];         ///< The version number.\n  uint32_t payload_size;   ///< The content size.\n  char modelname[7];       ///< The model name.\n  uint8_t _unused0010[3];  ///< Unused/reserved.\n  char hw_version[4];      ///< The hardware version.\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneFileReader::Element\n * ********************************************************************************************* */\nAnytoneFileReader::Element::Element(const uint8_t *ptr)\n  : _data(ptr)\n{\n  // pass...\n}\n\nAnytoneFileReader::Element::~Element() {\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneFileReader\n * ********************************************************************************************* */\nAnytoneFileReader::AnytoneFileReader(Config *config, const uint8_t *data, size_t size, QString &message)\n  : _context(config), _start(data), _data(data), _size(size), _message(message)\n{\n  // pass...\n}\n\nAnytoneFileReader::~AnytoneFileReader() {\n  // pass...\n}\n\nbool\nAnytoneFileReader::read() {\n  // Set pointer to start\n  _data = _start;\n\n  if (! this->readHeader())\n    return false;\n  if (! this->readChannels())\n    return false;\n  if (! this->readRadioIDs())\n    return false;\n  if (! this->readZones())\n    return false;\n  if (! this->readScanLists())\n    return false;\n  if (! this->readAnalogContacts())\n    return false;\n\n  // 2nd pass, link.\n  _data = _start;\n  if (! this->linkHeader())\n    return false;\n  if (! this->linkChannels())\n    return false;\n  if (! this->linkRadioIDs())\n    return false;\n  if (! this->linkZones())\n    return false;\n  if (! this->linkScanLists())\n    return false;\n  if (! this->linkAnalogContacts())\n    return false;\n\n  return true;\n}\n\n\nbool\nAnytoneFileReader::read(const QString &filename, Config *config, QString &message)\n{\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    message = QObject::tr(\"Cannot open file '%1': File does not exist.\").arg(filename);\n    return false;\n  }\n\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    message = QObject::tr(\"Cannot open file '%1': %2.\").arg(filename).arg(file.errorString());\n    return false;\n  }\n\n  file_header head;\n  if (sizeof(file_header) != file.read((char *)&head, sizeof(file_header))) {\n    message = QObject::tr(\"Cannot read header from file '%1': %2.\")\n        .arg(filename).arg(file.errorString());\n    file.close();\n    return false;\n  }\n\n  size_t size = qFromLittleEndian(head.payload_size)+14;\n  if (size != (size_t)info.size()) {\n    message = QObject::tr(\"Malformed header in file '%1': Mismatching content size. Expected %2, got %3.\")\n        .arg(filename).arg(info.size()-14).arg(size-14);\n    file.close();\n    return false;\n  }\n\n  uint8_t *data = file.map(0, size);\n  if (nullptr == data) {\n    message = QObject::tr(\"Cannot mmap file '%1': %2.\")\n        .arg(filename).arg(file.errorString());\n    file.close();\n    return false;\n  }\n\n  QString cps_version = QString::fromLocal8Bit(head.version, strnlen(head.version,5));\n  QString model = QString::fromLocal8Bit(head.modelname, strnlen(head.modelname,7));\n\n  // Dispatch by model name\n  AnytoneFileReader *reader = nullptr;\n  if (\"D868UVE\" == model) {\n    reader = new D868UVFileReader(config, data, size, message);\n  } else if (\"D878UV\" == model) {\n    reader = new D878UVFileReader(config, data, size, message);\n  } else if (\"D878UV2\" == model) {\n\n  } else if (\"D578UV\" == model) {\n\n  } else {\n    message = QObject::tr(\"Cannot read codeplug file '%1': Unknown model '%2'.\")\n        .arg(filename).arg(model);\n    file.unmap(data);\n    file.close();\n    return false;\n  }\n\n  if (nullptr == reader) {\n    message = QObject::tr(\"Cannot read codeplug file '%1': Model '%2' not implemented yet.\")\n        .arg(filename).arg(model);\n    file.unmap(data);\n    file.close();\n    return false;\n  }\n\n  // Clear config\n  config->reset();\n\n  if (! reader->read()) {\n    message = QObject::tr(\"Cannot read codeplug file '%1': %2\").arg(filename).arg(message);\n    file.unmap(data);\n    file.close();\n    return false;\n  }\n\n  file.unmap(data);\n  file.close();\n  return true;\n}\n"
  },
  {
    "path": "lib/anytone_filereader.hh",
    "content": "#ifndef ANYTONEFILEREADER_HH\n#define ANYTONEFILEREADER_HH\n\n#include <QFile>\n#include \"config.hh\"\n\n\n/** This class implements a reader of AnyTone codeplug files.\n * @warning This is mostly incomplete.\n * @ingroup anytone */\nclass AnytoneFileReader\n{\npublic:\n  /** Base class for all elements in the codeplug file. */\n  class Element\n  {\n  protected:\n    /** Hidden constructor. */\n    Element(const uint8_t *ptr);\n\n  public:\n    /** Destructor. */\n    virtual ~Element();\n    /** Returns the storage size of the element. */\n    virtual size_t size() const = 0;\n\n  protected:\n    /** Pointer to the actual element. */\n    const uint8_t *_data;\n  };\n\nprotected:\n  /** Constructs a configuration from the given codeplug file in memory (@c data, @c size). */\n  AnytoneFileReader(Config *config, const uint8_t *data, size_t size, QString &message);\n\npublic:\n  /** Destructor. */\n  virtual ~AnytoneFileReader();\n\nprotected:\n  /** Read all elements. */\n  virtual bool read();\n\n  /** Read header of file. */\n  virtual bool readHeader() = 0;\n  /** Link elements. */\n  virtual bool linkHeader() = 0;\n  /** Read all channels. */\n  virtual bool readChannels() = 0;\n  /** Link elements. */\n  virtual bool linkChannels() = 0;\n  /** Read all radio IDs. */\n  virtual bool readRadioIDs() = 0;\n  /** Link elements. */\n  virtual bool linkRadioIDs() = 0;\n  /** Read all zones. */\n  virtual bool readZones() = 0;\n  /** Link elements. */\n  virtual bool linkZones() = 0;\n  /** Read all scal lists. */\n  virtual bool readScanLists() = 0;\n  /** Link elements. */\n  virtual bool linkScanLists() = 0;\n  /** Read all DTMF contacts. */\n  virtual bool readAnalogContacts() = 0;\n  /** Link elements. */\n  virtual bool linkAnalogContacts() = 0;\n\npublic:\n  /** Use this static function to read a codeplug from the manufacturer CPS file. */\n  static bool read(const QString &filename, Config *config, QString &message);\n\nprotected:\n  /** Pointer to the start. */\n  const uint8_t * const _start;\n  /** Pointer to the entire data. */\n  const uint8_t *_data;\n  /** Size of the entire blob. */\n  size_t _size;\n  /** Error message. */\n  QString &_message;\n};\n\n#endif // ANYTONEFILEREADER_HH\n"
  },
  {
    "path": "lib/anytone_interface.cc",
    "content": "#include \"anytone_interface.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n#define USB_VID_GD32  0x28e9\n#define USB_PID_GD32  0x018a\n#define USB_VID_STM32 0x2e3c\n#define USB_PID_STM32 0x5740\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneInterface::ReadRequest\n * ********************************************************************************************* */\nAnytoneInterface::ReadRequest::ReadRequest(uint32_t addr) {\n  cmd = 'R';\n  this->addr = qToBigEndian(addr);\n  size = 16;\n}\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneInterface::ReadResponse\n * ********************************************************************************************* */\nbool\nAnytoneInterface::ReadResponse::check(uint32_t addr, QString &msg) const {\n  if ('W' != cmd) {\n    msg = QObject::tr(\"Invalid read response: Expected command 'W' got '%1'\").arg(cmd);\n    return false;\n  }\n  if (qFromBigEndian(this->addr) != addr) {\n    msg = QObject::tr(\"Invalid read response: Expected address '%1' got '%2'\")\n        .arg(addr, 8, 16, QChar('0')).arg(qFromLittleEndian(this->addr), 8, 16, QChar('0'));\n    return false;\n  }\n  if (16 != size) {\n    msg = QObject::tr(\"Invalid read response: Expected size 16 got %1\").arg((int)size);\n    return false;\n  }\n  // Compute checksum\n  uint8_t crc=((const uint8_t *)this)[1];\n  for (uint8_t i=2; i<(size+6); i++)\n    crc += ((const uint8_t *)this)[i];\n  // compare\n  if (crc != sum) {\n    msg = QObject::tr(\"Invalid read response: Expected check-sum %1 got %2\")\n        .arg((int)crc).arg((int)sum);\n    return false;\n  }\n  // check ACK flag.\n  if (6 != ack) {\n    msg = QObject::tr(\"Invalid read response: Expected ACK 6 got %1\").arg((int)ack);\n    return false;\n  }\n  // ok\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneInterface::WriteRequest\n * ********************************************************************************************* */\nAnytoneInterface::WriteRequest::WriteRequest(uint32_t addr, const char *data) {\n  cmd = 'W';\n  this->addr = qToBigEndian(addr);\n  size = 16;\n   memcpy(this->data, data, size);\n  sum = 0;\n  uint8_t *b=(uint8_t *)this;\n  for (uint8_t i=1; i<(size+6); i++)\n    sum += b[i];\n  ack = 6;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneInterface::RadioInfo\n * ********************************************************************************************* */\nAnytoneInterface::RadioVariant::RadioVariant()\n  : name(\"\"), bands(0x00), version(\"\")\n{\n  // pass...\n}\n\nbool\nAnytoneInterface::RadioVariant::isValid() const {\n  return ! name.isEmpty();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneInterface\n * ********************************************************************************************* */\nAnytoneInterface::AnytoneInterface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : USBSerial(descriptor, QSerialPort::Baud115200, err, parent), _state(STATE_INITIALIZED), _info()\n{\n  if (isOpen()) {\n    _state = STATE_OPEN;\n  } else {\n    _state = STATE_ERROR;\n    return;\n  }\n\n  // enter program mode\n  if (! this->enter_program_mode())\n    return;\n  // identify device\n  if (! this->request_identifier(_info)) {\n    _info = RadioVariant();\n    _state = STATE_ERROR;\n  }\n}\n\nAnytoneInterface::~AnytoneInterface() {\n  if (isOpen())\n    AnytoneInterface::close();\n}\n\n\nvoid\nAnytoneInterface::close() {\n  switch (_state) {\n  case STATE_INITIALIZED:\n  case STATE_OPEN:\n    USBSerial::close();\n    break;\n  case STATE_PROGRAM:\n    this->reboot();\n    break;\n  case STATE_CLOSED:\n  case STATE_ERROR:\n    break;\n  }\n}\n\nbool\nAnytoneInterface::getInfo(RadioVariant &info) {\n  if (_info.isValid()) {\n    info = _info;\n    return true;\n  }\n  return false;\n}\n\nbool\nAnytoneInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err)\n{\n  Q_UNUSED(bank); Q_UNUSED(addr)\n  if ((STATE_PROGRAM != _state) && (! enter_program_mode(err)))\n    return false;\n\n  return true;\n}\n\nbool\nAnytoneInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err)\n{\n  if (0 != bank) {\n    errMsg(err) << \"Anytone: Cannot write to bank \" << bank << \". There is only one (idx=0).\";\n    return false;\n  }\n\n  if (STATE_PROGRAM != _state) {\n    errMsg(err) << \"Anytone: Cannot write data to device: Not in programming mode.\";\n    return false;\n  }\n\n  //logDebug() << \"Anytone: Write \" << nbytes << \"b to addr 0x\" << QString::number(addr, 16) << \"...\";\n\n  for (int i=0; i<nbytes; i+=16) {\n    uint8_t ack;\n    WriteRequest req(addr+i, (const char *)(data+i));\n    if (! send_receive((const char *)&req, sizeof(WriteRequest),(char *)&ack, 1, err)) {\n      errMsg(err) << \"Anytone: Cannot write data to device.\";\n      return false;\n    }\n    if (0x06 != ack) {\n      errMsg(err) << \"Anytone: Cannot write data to device: Unexpected response \"\n                  << (int)ack << \", expected 6.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nAnytoneInterface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err)\n  return true;\n}\n\nbool\nAnytoneInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr);\n\n  if ((STATE_PROGRAM != _state) && (! enter_program_mode(err)))\n    return false;\n\n  return true;\n}\n\nbool\nAnytoneInterface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  if (0 != bank) {\n    errMsg(err) << \"Anytone: Cannot read from bank \" << bank << \". There is only one (idx=0).\";\n    return false;\n  }\n\n  if (STATE_PROGRAM != _state) {\n    errMsg(err) << \"Anytone: Cannot read data from device: Not in programming mode.\";\n    return false;\n  }\n\n  //logDebug() << \"Anytone: Read \" << nbytes << \"b from addr 0x\" << QString::number(addr, 16) << \"...\";\n\n  for (int i=0; i<nbytes; i+=16) {\n    ReadRequest req(addr + i);\n    ReadResponse resp;\n    if (! send_receive((const char *)&req, sizeof(ReadRequest),\n                       (char *)&resp, sizeof(ReadResponse), err)) {\n      errMsg(err) << \"Anytone: Cannot read data from device.\";\n      return false;\n    }\n    QString error_message;\n    if (! resp.check(addr+i, error_message)) {\n      errMsg(err) << \"Anytone: Cannot read data from device: \" << error_message << \".\";\n      return false;\n    }\n    memcpy(data+i, resp.data, 16);\n  }\n\n  return true;\n}\n\nbool\nAnytoneInterface::read_finish(const ErrorStack &err) {\n  Q_UNUSED(err)\n  return true;\n}\n\nbool\nAnytoneInterface::reboot(const ErrorStack &err) {\n  if (STATE_PROGRAM == _state) {\n    if (! leave_program_mode(err))\n      return false;\n    _state = STATE_OPEN;\n  }\n  if (STATE_OPEN == _state) {\n    this->close();\n    _state = STATE_CLOSED;\n  }\n  return true;\n}\n\nbool\nAnytoneInterface::enter_program_mode(const ErrorStack &err) {\n  if (STATE_PROGRAM == _state) {\n    logDebug() << \"Already in program mode. Skip.\";\n    return true;\n  } else if (STATE_OPEN != _state) {\n    errMsg(err) << \"Anytone: Cannot enter program mode. Device is in state \" << _state << \".\";\n    return false;\n  }\n\n  char ack[3];\n  // send \"enter program mode\" command\n  if (! send_receive(\"PROGRAM\", 7, ack, 3, err)) {\n    errMsg(err) << \"Anytone: Cannot enter program mode.\";\n    return false;\n  }\n  // check response\n  if (0 != memcmp(ack, \"QX\\6\", 3)) {\n    errMsg(err) << \"Anytone: Cannot enter program mode: Unexpected response. \"\n                << \"Expected 515806 got \" << QString::number(ack[0], 16)\n                << QString::number(ack[1], 16)\n                << QString::number(ack[1], 16) << \".\";\n    close();\n    _state = STATE_ERROR;\n    return false;\n  }\n  logDebug() << \"Anytone: In program-mode now.\";\n  _state = STATE_PROGRAM;\n  return true;\n}\n\nbool\nAnytoneInterface::request_identifier(RadioVariant &info, const ErrorStack &err) {\n  if (STATE_PROGRAM != _state) {\n      errMsg(err) << \"Anytone: Cannot request identifier. Device not in program mode, is in state \"\n               << _state << \".\";\n      return false;\n    }\n\n  RadioInfoResponse resp;\n  // send \"identify\" command (in program mode)\n  if (! send_receive(\"\\2\", 1, (char *)&resp, sizeof(RadioInfoResponse))) {\n    errMsg(err) << \"Anytone: Cannot request identifier.\";\n    return false;\n  }\n  // check response\n  if (('I'!=resp.prefix) || (0x06 != resp.eot)) {\n    errMsg(err) << \"Anytone: Cannot request identifier: Unexpected response.\";\n    close();\n    _state = STATE_ERROR;\n    return false;\n  }\n  info.name = QString::fromLocal8Bit(resp.model, strnlen(resp.model, sizeof(resp.model))).simplified();\n  info.bands = resp.bands;\n  info.version = QString::fromLocal8Bit(resp.version, strnlen(resp.version, sizeof(resp.version))).simplified();\n  logDebug() << \"Found radio '\" << info.name << \"', version '\" << info.version << \"'.\";\n  return true;\n}\n\nbool\nAnytoneInterface::leave_program_mode(const ErrorStack &err) {\n  if (STATE_OPEN == _state) {\n    logDebug() << \"Device in open mode -> no need to leave program mode.\";\n    return true;\n  } else if (STATE_PROGRAM != _state) {\n    errMsg(err) << \"Anytone: Cannot leave program mode. Device in state \" << _state << \".\";\n    return false;\n  }\n\n  char ack[1];\n  if (! send_receive(\"END\", 3, ack, 1)) {\n    errMsg(err) << \"Anytone: Cannot leave program mode.\";\n    return false;\n  }\n\n  logDebug() << \"Anytone: Left program-mode.\";\n  _state = STATE_OPEN;\n  return true;\n}\n\nbool\nAnytoneInterface::send_receive(const char *cmd, int clen, char *resp, int rlen, const ErrorStack &err) {\n  // Try to write command to device\n  if (clen != QSerialPort::write(cmd, clen)) {\n    errMsg(err) << \"Cannot send command to device.\";\n    close();\n    _state = STATE_ERROR;\n    return false;\n  }\n\n  // Read from device until complete response has been read\n  char *p = resp;\n  int len = rlen;\n  while (len > 0) {\n    if (! waitForReadyRead(1000)) {\n      errMsg(err) << \"No response from device: Timeout.\";\n      close();\n      _state = STATE_ERROR;\n      return false;\n    }\n\n    int r = QSerialPort::read(p, len);\n    if (r < 0) {\n      errMsg(err) << \"Cannot read response from device.\";\n      close();\n      _state = STATE_ERROR;\n      return false;\n    }\n    p += r;\n    len-=r;\n  }\n\n  // done\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneGD32Interface\n * ********************************************************************************************* */\nAnytoneGD32Interface::AnytoneGD32Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : AnytoneInterface(descriptor, err, parent)\n{\n  // pass...\n}\n\n\nRadioInfo\nAnytoneGD32Interface::identifier(const ErrorStack &err) {\n  if (! _info.isValid())\n    return RadioInfo();\n  if (\"D868UVE\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D868UVE);\n  } else if (\"D6X2UV\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::DMR6X2UV);\n  } else if (\"D6X2UV2\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::DMR6X2UV2);\n  } else if (\"D878UV\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D878UV);\n  } else if (\"D878UV2\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D878UVII);\n  } else if (\"D578UV\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D578UV);\n  }  else if (\"D578UV2\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D578UVII);\n  }\n\n  errMsg(err) << tr(\"Unsupported AnyTone radio '%1', HW rev. '%2'.\")\n                 .arg(_info.name).arg(_info.version);\n  return RadioInfo();\n}\n\n\nUSBDeviceInfo\nAnytoneGD32Interface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID_GD32, USB_PID_GD32);\n}\n\nQList<USBDeviceDescriptor>\nAnytoneGD32Interface::detect(bool saveOnly) {\n  Q_UNUSED(saveOnly);\n  return USBSerial::detect(USB_VID_GD32, USB_PID_GD32, true);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSTM32Interface\n * ********************************************************************************************* */\nAnytoneSTM32Interface::AnytoneSTM32Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : AnytoneInterface(descriptor, err, parent)\n{\n  // pass...\n}\n\nRadioInfo\nAnytoneSTM32Interface::identifier(const ErrorStack &err) {\n  if (! _info.isValid())\n    return RadioInfo();\n  if (\"D578UV\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D578UV);\n  } else if (\"D578UV2\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D578UVII);\n  } else if (\"D168UV\" == _info.name) {\n    return RadioInfo::byID(RadioInfo::D168UV);\n  }\n\n\n  errMsg(err) << tr(\"Unsupported AnyTone radio '%1', HW rev. '%2'.\")\n                 .arg(_info.name).arg(_info.version);\n  return RadioInfo();\n}\n\nUSBDeviceInfo\nAnytoneSTM32Interface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID_STM32, USB_PID_STM32);\n}\n\nQList<USBDeviceDescriptor>\nAnytoneSTM32Interface::detect(bool saveOnly) {\n  Q_UNUSED(saveOnly);\n  return USBSerial::detect(USB_VID_STM32, USB_PID_STM32, true);\n}\n"
  },
  {
    "path": "lib/anytone_interface.hh",
    "content": "#ifndef ANYTONEINTERFACE_HH\n#define ANYTONEINTERFACE_HH\n\n#include \"usbserial.hh\"\n\n/** Implements the interface to Anytone D868UV, D878UV, etc radios.\n *\n * This interface uses a USB serial-port to communicate with the device. To find the corresponding\n * port, the device-specific VID @c 0x28e9 and PID @c 0x018a are used. Hence no udev rules are\n * needed to access these devices. The user, however, should be a member of the @c dialout group\n * to get access to the serial interfaces.\n *\n * @ingroup anytone */\nclass AnytoneInterface : public USBSerial\n{\n  Q_OBJECT\n\npublic:\n  /** Collects information about the particular radio being accessed. */\n  struct RadioVariant {\n    /** The name of the radio. */\n    QString name;\n    /** A code for which bands are supported. */\n    char bands;\n    /** The (firmware/hardware) version. */\n    QString version;\n\n    /** Empty constructor. */\n    RadioVariant();\n    /** Returns @c true if the radio info is valid. */\n    bool isValid() const;\n  };\n\npublic:\n  /** Constructs a new interface to Anytone radios. If a matching device was found, @c isOpen\n   * returns @c true. */\n  explicit AnytoneInterface(const USBDeviceDescriptor &descriptor,\n                            const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~AnytoneInterface();\n\n  /** Closes the interface to the device. */\n  void close();\n\n  /** Reads the radio info from the device and returns it.\n   * The information is only read once. */\n  bool getInfo(RadioVariant &info);\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\n  bool reboot(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Send command message to radio to ender program state. */\n  bool enter_program_mode(const ErrorStack &err=ErrorStack());\n  /** Sends a request to radio to identify itself. */\n  bool request_identifier(RadioVariant &info, const ErrorStack &err=ErrorStack());\n  /** Sends a command message to radio to leave program state and reboot. */\n  bool leave_program_mode(const ErrorStack &err=ErrorStack());\n  /** Internal used method to send messages to and receive responses from radio. */\n  bool send_receive(const char *cmd, int clen, char *resp, int rlen, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Binary representation of a read request to the radio. */\n  struct __attribute__((packed)) ReadRequest {\n    char cmd;      ///< Fixed to 'R'.\n    uint32_t addr; ///< Memory address in little-endian.\n    uint8_t size;  ///< Fixed to 16.\n    /// Constructs a read request for the specified address.\n    ReadRequest(uint32_t addr);\n  };\n\n  /** Binary representation of a read response from the radio. */\n  struct __attribute__((packed)) ReadResponse {\n    char cmd;      ///< Fixed to 'W'.\n    uint32_t addr; ///< Memory address in big-endian.\n    uint8_t size;  ///< Fixed to 16.\n    char data[16]; ///< The actual data.\n    uint8_t sum;   ///< Sum over address, size and data.\n    uint8_t ack;   ///< Fixed to 0x06.\n    /** Check the response, returns @c true if read request was successful.\n     * @param addr The read address to verify.\n     * @param msg On error, contains a message describing the issue. */\n    bool check(uint32_t addr, QString &msg) const;\n  };\n\n  /** Binary representation of a write request to the radio. */\n  struct __attribute__((packed)) WriteRequest {\n    char cmd;      ///< Fixed to 'W'\n    uint32_t addr; ///< Memory address in big-endian.\n    uint8_t size;  ///< Fixed to 16\n    char data[16]; ///< The actual data.\n    uint8_t sum;   ///< Sum over addr, size and data.\n    uint8_t ack;   ///< Fixed to 0x06;\n\n    /** Assembles a write request message to the given address with the given data.\n     * @param addr Specifies the address to write to.\n     * @param data 16 bytes of payload. */\n    WriteRequest(uint32_t addr, const char *data);\n  };\n\n  /** Structure of radio information response. */\n  struct __attribute__((packed)) RadioInfoResponse {\n    char prefix;       ///< Fixed prefix. Set to 'I' on success.\n    char model[7];     ///< Model name.\n    uint8_t bands;     ///< Frequency bands supported by radio.\n    char version[6];   ///< Version number. Either hardware or 'radio' version.\n    uint8_t eot;       ///< Fixed 0x06 on success.\n  };\n\n  /** Possible states of the radio interface. */\n  enum State {\n    STATE_INITIALIZED, ///< Initial state.\n    STATE_OPEN,        ///< Interface to radio is open.\n    STATE_PROGRAM,     ///< Radio is in program mode.\n    STATE_CLOSED,      ///< Interface to radio is closed (captive final state).\n    STATE_ERROR        ///< An error occurred (captive final state),\n                       ///  use @c errorMessage() to get an error message.\n  };\n\n  /** Holds the state of the interface. */\n  State _state;\n  /** Holds the radio info. */\n  RadioVariant _info;\n};\n\n\n\n/** Implements the interface to Anytone GD32 revisions of D578UV radios.\n *\n * This interface is identical to the previous ones, except it uses a different VID/PID combo.\n * That is VID @c 0x2e3c and PID @c 0x5740 are used.\n *\n * @ingroup anytone */\nclass AnytoneGD32Interface: public AnytoneInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new interface to Anytone radios. If a matching device was found, @c isOpen\n   * returns @c true. */\n  explicit AnytoneGD32Interface(const USBDeviceDescriptor &descriptor,\n                               const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  /** Returns an identifier of the radio. */\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n};\n\n\n\n/** Implements the interface to Anytone SMT32 revisions of D578UV radios.\n *\n * This interface is identical to the previous ones, except it uses a different VID/PID combo.\n * That is VID @c 0x2e3c and PID @c 0x5740 are used.\n *\n * @ingroup anytone */\nclass AnytoneSTM32Interface: public AnytoneInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new interface to Anytone radios. If a matching device was found, @c isOpen\n   * returns @c true. */\n  explicit AnytoneSTM32Interface(const USBDeviceDescriptor &descriptor,\n                               const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  /** Returns an identifier of the radio. */\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n};\n\n#endif // ANYTONEINTERFACE_HH\n"
  },
  {
    "path": "lib/anytone_limits.cc",
    "content": "#include \"anytone_limits.hh\"\n\nAnytoneLimits::AnytoneLimits(const QString &hardwareRevision, const QString &supportedRevision, bool betaWarning, QObject *parent)\n  : RadioLimits(betaWarning, parent), _hardwareRevision(hardwareRevision), _supportedRevision(supportedRevision)\n{\n  // pass...\n}\n\nbool\nAnytoneLimits::verifyConfig(const Config *config, RadioLimitContext &context) const {\n  bool success = RadioLimits::verifyConfig(config, context);\n\n  if (_hardwareRevision.isEmpty())\n    return success;\n\n  if (_supportedRevision < _hardwareRevision) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg = tr(\"You are likely using a newer radio reversion (%1) than supported (%2) by qdmr. \"\n             \"The codeplug might be incompatible. \"\n             \"Notify the developers of qdmr about the new reversion.\").arg(_hardwareRevision, _supportedRevision);\n  } else if (_supportedRevision > _hardwareRevision) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg = tr(\"You are likely using an older hardware reversion (%1) than supported (%2) by qdmr. \"\n             \"The codeplug might be incompatible.\").arg(_hardwareRevision, _supportedRevision);\n  }\n\n  return success;\n}\n"
  },
  {
    "path": "lib/anytone_limits.hh",
    "content": "#ifndef ANYTONELIMITS_HH\n#define ANYTONELIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Base class of limits for all AnyTone radios.\n * This class extends @c RadioLimits and implements the hardware revision check.\n * @ingroup anytone */\nclass AnytoneLimits: public RadioLimits\n{\n  Q_OBJECT\n\nprotected:\n  /** Constructor.\n   * @param hardwareRevision Specifies the hardware revision as reported by the radio. If empty,\n   *        no check is perfored.\n   * @param supportedRevision Specifies the supported hardware revision.\n   * @param betaWarning If @c true, a warning is issued that the radio is still under development.\n   * @param parent Specifies the QObject parent. */\n  AnytoneLimits(const QString &hardwareRevision, const QString &supportedRevision, bool betaWarning,\n                QObject *parent=nullptr);\n\npublic:\n  bool verifyConfig(const Config *config, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the hardware revision of the radio. */\n  QString _hardwareRevision;\n  /** Holds the supported hardware revision of the radio. */\n  QString _supportedRevision;\n};\n\n#endif // ANYTONELIMITS_HH\n"
  },
  {
    "path": "lib/anytone_radio.cc",
    "content": "#include \"anytone_radio.hh\"\n\n#include \"d868uv.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"configcopyvisitor.hh\"\n\n#define RBSIZE 16\n#define WBSIZE 16\n\n\nAnytoneRadio::AnytoneRadio(const QString &name, AnytoneInterface *device, QObject *parent)\n  : Radio(parent), _name(name), _dev(device), _codeplugFlags(), _config(nullptr),\n    _codeplug(nullptr), _callsigns(nullptr), _satellites(nullptr)\n{\n  // Check if device is open\n  if ((nullptr==_dev) || (! _dev->isOpen())) {\n    _task = StatusError;\n    return;\n  }  \n}\n\nAnytoneRadio::~AnytoneRadio() {\n  if (_dev && _dev->isOpen()) {\n    _dev->reboot();\n    _dev->close();\n  }\n  if (_dev) {\n    _dev->deleteLater();\n    _dev = nullptr;\n  }\n}\n\nconst QString &\nAnytoneRadio::name() const {\n  return _name;\n}\n\nconst Codeplug &\nAnytoneRadio::codeplug() const {\n  return *_codeplug;\n}\n\nCodeplug &\nAnytoneRadio::codeplug() {\n  return *_codeplug;\n}\n\nbool\nAnytoneRadio::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  start();\n\n  return true;\n}\n\nbool\nAnytoneRadio::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (_config)\n    delete _config;\n\n  // Cannot upload null-pointer\n  if (nullptr == (_config = config))\n    return false;\n\n  _task = StatusUpload;\n  _codeplugFlags = flags;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n\n  // also, move config to thread\n  _config->moveToThread(this);\n\n  start();\n\n  return true;\n}\n\n\nbool\nAnytoneRadio::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  _callsigns->encode(db, selection);\n\n  _task = StatusUploadCallsigns;\n  _errorStack = err;\n\n  if (selection.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  start();\n\n  return true;\n}\n\n\nbool\nAnytoneRadio::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  if (! _satellites->encode(db, err)) {\n    errMsg(err) << \"Cannot encode satellite config for AnyTone device.\";\n    return false;\n  }\n\n  _task = StatusUploadSatellites;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  start();\n\n  return true;\n}\n\n\nvoid\nAnytoneRadio::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    emit downloadStarted();\n\n    if (! download()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _dev->close();\n    _task = StatusIdle;\n    emit downloadFinished(this, _codeplug);\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! upload()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! uploadCallsigns()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadSatellites == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! uploadSatellites()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  }\n}\n\nbool\nAnytoneRadio::download() {\n  if (nullptr == _codeplug) {\n    errMsg(_errorStack) << \"Cannot download codeplug: Object not created yet.\";\n    return false;\n  }\n\n  logDebug() << \"Download of \" << _codeplug->image(0).numElements() << \" bitmaps.\";\n\n  // Download bitmaps\n  for (int n=0; n<_codeplug->image(0).numElements(); n++) {\n    unsigned addr = _codeplug->image(0).element(n).address();\n    unsigned size = _codeplug->image(0).element(n).data().size();\n    if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot download codeplug.\";\n      return false;\n    }\n    emit downloadProgress(float(n*100)/_codeplug->image(0).numElements());\n  }\n\n  // Allocate remaining memory sections\n  unsigned nstart = _codeplug->image(0).numElements();\n  _codeplug->allocateForDecoding();\n\n  logDebug() << \"Download of \" << _codeplug->image(0).numElements()-nstart << \" discovered elements.\";\n\n  // Check every segment in the remaining codeplug\n  for (int n=nstart; n<_codeplug->image(0).numElements(); n++) {\n    if (! _codeplug->image(0).element(n).isAligned(RBSIZE)) {\n      errMsg(_errorStack) << \"Cannot download codeplug: Codeplug element \" << n\n                          << \" (addr=\" << _codeplug->image(0).element(n).address()\n                          << \", size=\" << _codeplug->image(0).element(n).data().size()\n                          << \") is not aligned with blocksize \" << RBSIZE << \".\";\n      return false;\n    }\n  }\n\n  // Download remaining memory sections\n  for (int n=nstart; n<_codeplug->image(0).numElements(); n++) {\n    unsigned addr = _codeplug->image(0).element(n).address();\n    unsigned size = _codeplug->image(0).element(n).data().size();\n    if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot download codeplug.\";\n      return false;\n    }\n    logDebug() << \"Read \" << Qt::hex << size <<\n                  \"h bytes from address \" << Qt::hex << addr << \".\";\n    emit downloadProgress(float(n*100)/_codeplug->image(0).numElements());\n  }\n\n  return true;\n}\n\nbool\nAnytoneRadio::upload() {\n  if (nullptr == _codeplug) {\n    errMsg(_errorStack) << \"Cannot write codeplug: Object not created yet.\";\n    return false;\n  }\n\n  // Download bitmaps first\n  size_t nbitmaps = _codeplug->numImages();\n  for (int n=0; n<_codeplug->image(0).numElements(); n++) {\n    unsigned addr = _codeplug->image(0).element(n).address();\n    unsigned size = _codeplug->image(0).element(n).data().size();\n    if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot read codeplug for update.\";\n      return false;\n    }\n    emit uploadProgress(float(n*25)/_codeplug->image(0).numElements());\n  }\n\n  // Allocate all memory sections that must be read first\n  // and written back to the device more or less untouched\n  _codeplug->allocateUpdated();\n\n  // Download new memory sections for update\n  for (int n=nbitmaps; n<_codeplug->image(0).numElements(); n++) {\n    unsigned addr = _codeplug->image(0).element(n).address();\n    unsigned size = _codeplug->image(0).element(n).data().size();\n    if (! _dev->read(0, addr, _codeplug->data(addr), size, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot read codeplug for update.\";\n      return false;\n    }\n    emit uploadProgress(25+float(n*25)/_codeplug->image(0).numElements());\n  }\n\n  // Update binary codeplug from config\n  if (! _codeplug->encode(_config, _codeplugFlags, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot encode codeplug.\";\n    return false;\n  }\n\n  // Sort all elements before uploading\n  _codeplug->image(0).sort();\n\n  // Upload all elements back to the device\n  for (int n=0; n<_codeplug->image(0).numElements(); n++) {\n    unsigned addr = _codeplug->image(0).element(n).address();\n    unsigned size = _codeplug->image(0).element(n).data().size();\n    if (! _dev->write(0, addr, _codeplug->data(addr), size, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot write codeplug.\";\n      return false;\n    }\n    emit uploadProgress(50+float(n*50)/_codeplug->image(0).numElements());\n  }\n\n  return true;\n}\n\n\nbool\nAnytoneRadio::uploadCallsigns() {\n  // Sort all elements before uploading\n  _callsigns->image(0).sort();\n\n  size_t totalBlocks = _callsigns->memSize()/WBSIZE;\n  size_t blkWritten  = 0;\n  // Upload all elements back to the device\n  for (int n=0; n<_callsigns->image(0).numElements(); n++) {\n    unsigned addr = _callsigns->image(0).element(n).address();\n    unsigned size = _callsigns->image(0).element(n).data().size();\n    unsigned nblks = size/WBSIZE;\n    for (unsigned i=0; i<nblks; i++) {\n      if (! _dev->write(0, addr+i*WBSIZE, _callsigns->data(addr)+i*WBSIZE, WBSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot write callsign db.\";\n        _task = StatusError;\n        return false;\n      }\n      blkWritten++;\n      emit uploadProgress(float(blkWritten*100)/totalBlocks);\n    }\n  }\n\n  return true;\n}\n\n\nbool\nAnytoneRadio::uploadSatellites() {\n  // Sort all elements before uploading\n  _satellites->image(0).sort();\n\n  size_t totalBlocks = _satellites->memSize()/WBSIZE;\n  size_t blkWritten  = 0;\n  // Upload all elements back to the device\n  for (int n=0; n<_satellites->image(0).numElements(); n++) {\n    unsigned addr = _satellites->image(0).element(n).address();\n    unsigned size = _satellites->image(0).element(n).data().size();\n    unsigned nblks = size/WBSIZE;\n    for (unsigned i=0; i<nblks; i++) {\n      if (! _dev->write(0, addr+i*WBSIZE, _satellites->data(addr)+i*WBSIZE, WBSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot write satellite config.\";\n        _task = StatusError;\n        return false;\n      }\n      blkWritten++;\n      emit uploadProgress(float(blkWritten*100)/totalBlocks);\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/anytone_radio.hh",
    "content": "/** @defgroup anytone Anytone Radios\n * Base classes for all anytone radios.\n *\n * Anytone radios share a lot of common formats and interfaces.\n * Consequently there are base-classes for radio interfaces (@c AnytoneInterface), codeplugs\n * (@c AnytoneCodeplug) and radios (@c AnytoneRadio). This helps to keep the burden of implementing\n * the support for each radio at a minimum.\n *\n * @ingroup dsc */\n#ifndef __ANYTONE_RADIO_HH__\n#define __ANYTONE_RADIO_HH__\n\n#include \"radio.hh\"\n#include \"anytone_interface.hh\"\n#include \"anytone_codeplug.hh\"\n#include \"anytone_satelliteconfig.hh\"\n\n/** Implements an interface to Anytone radios.\n *\n * The transfer of the codeplug to the device is performed in 4 steps.\n *\n * First only the bitmaps of all lists are downloaded from the device. Then all elements that are\n * not touched or only updated by the common codeplug config are downloaded. Then, the common\n * config gets applied to the binary codeplug. That is, all channels, contacts, zones, group-lists\n * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config\n * gets updated from the common codeplug settings. Finally, the resulting binary codeplug gets\n * written back to the device.\n *\n * This rather complex method of writing a codeplug to the device is needed to maintain all\n * settings within the radio that are not defined within the common codeplug config while keeping\n * the amount of data being read from and written to the device small.\n *\n * @ingroup anytone */\nclass AnytoneRadio: public Radio\n{\n  Q_OBJECT\n\nprotected:\n  /** Do not construct this class directly. */\n  explicit AnytoneRadio(const QString &name, AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~AnytoneRadio();\n\n  const QString &name() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack());\n\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(), const ErrorStack &err=ErrorStack());\n\n  bool startUploadSatelliteConfig(\n      SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n  void run();\n\nprivate:\n  /** Downloads the codeplug from the radio. This method block until the download is complete. */\n  virtual bool download();\n  /** Uploads the encoded codeplug to the radio. This method block until the upload is complete. */\n  virtual bool upload();\n  /** Uploads the encoded callsign database to the radio.\n   * This method block until the upload is complete. */\n  virtual bool uploadCallsigns();\n  /** Uploads the encoded satellite config to the radio.\n   * This method block until the upload is complete. */\n  virtual bool uploadSatellites();\n\nprotected:\n  /** The device identifier. */\n  QString _name;\n  /** The interface to the radio. */\n  AnytoneInterface *_dev;\n  /** If @c true, the codeplug on the radio gets updated upon upload. If @c false, it gets\n   * overridden. */\n  Codeplug::Flags _codeplugFlags;\n  /** Owns the generic configuration. */\n  Config *_config;\n  /** A weak reference to the user-database. */\n  UserDatabase *_userDB;\n  /** The actual binary codeplug representation. */\n  AnytoneCodeplug *_codeplug;\n  /** The actual binary callsign database representation. */\n  CallsignDB *_callsigns;\n  /** The actual binary callsign database representation. */\n  AnytoneSatelliteConfig *_satellites;\n};\n\n#endif // __D868UV_HH__\n"
  },
  {
    "path": "lib/anytone_satelliteconfig.cc",
    "content": "#include \"anytone_satelliteconfig.hh\"\n#include \"anytone_codeplug.hh\"\n#include \"satellitedatabase.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSatelliteConfig::Satellite\n * ********************************************************************************************* */\nAnytoneSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nAnytoneSatelliteConfig::SatelliteElement::SatelliteElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::clear() {\n  memset(_data, 0, size());\n  memset(_data, 0x20, 0x50);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::name(), 0);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setEpoch(const OrbitalElement::Epoch &epoch) {\n  double tmp = epoch.toEpoch();\n  int year = epoch.year;\n  int day  = tmp; tmp -= day; tmp *= 100000000;\n  int fday = tmp;\n\n  QString fmt_year = QString(\"%1\").arg(year%100, 2, 10, QChar('0'));\n  QString fmt_day = QString(\"%1.%2\")\n      .arg(day, 3, 10, QChar('0'))\n      .arg(fday, 8, 10, QChar('0'));\n\n  writeASCII(Offset::epochYear(), fmt_year, 2);\n  writeASCII(Offset::epochDay(), fmt_day, 12, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setMeanMotionDerivative(double dmm) {\n  QString fmt = QString(\"%1.%2\")\n      .arg(dmm < 0 ? '-' :  ' ')\n      .arg((int)(std::abs(dmm) * 100000000), 8, 10, QChar('0'));\n  writeASCII(Offset::meanMotionDerivative(), fmt, 10, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setInclination(double incl) {\n  int ddd = incl; incl -= ddd;\n  int ffff = incl*10000;\n\n  QString fmt = QString(\"%1.%2\")\n      .arg(ddd, 3, 10, QChar(' '))\n      .arg(ffff, 4, 10, QChar('0'));\n\n  writeASCII(Offset::inclination(), fmt, 8, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setAscension(double asc) {\n  int ddd = asc; asc -= ddd;\n  int ffff = asc*10000;\n\n  QString fmt = QString(\"%1.%2\")\n      .arg(ddd, 3, 10, QChar(' '))\n      .arg(ffff, 4, 10, QChar('0'));\n\n  writeASCII(Offset::ascension(), fmt, 8, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setEccentricity(double ecc) {\n  QString fmt = QString(\"%1\").arg((int)(ecc * 10000000), 7, 10, QChar('0'));\n  writeASCII(Offset::eccentricity(), fmt, 7, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setPerigee(double peri) {\n  int ddd = peri; peri -= ddd;\n  int ffff = peri*10000;\n\n  QString fmt = QString(\"%1.%2\")\n      .arg(ddd, 3, 10, QChar(' '))\n      .arg(ffff, 4, 10, QChar('0'));\n\n  writeASCII(Offset::perigee(), fmt, 8, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setAnomaly(double ma) {\n  int ddd = ma; ma -= ddd;\n  int ffff = ma*10000;\n\n  QString fmt = QString(\"%1.%2\")\n      .arg(ddd, 3, 10, QChar(' '))\n      .arg(ffff, 4, 10, QChar('0'));\n\n  writeASCII(Offset::anomaly(), fmt, 8, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setMeanMotion(double mm) {\n  int dd = mm; mm -= dd;\n  int ffffffff = mm*100000000;\n\n  QString fmt = QString(\"%1.%2\")\n      .arg(dd, 2, 10, QChar(' '))\n      .arg(ffffffff, 8, 10, QChar('0'));\n\n  writeASCII(Offset::meanMotion(), fmt, 11, 0x20);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setRevolution(unsigned int num) {\n  writeASCII(Offset::revolution(), QString(\"%1\").arg(num, 5, 10, QChar('0')), 5, 0x20);\n}\n\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setDownlink(const Frequency &f) {\n  setUInt32_le(Offset::downlinkFrequency(), f.inHz()/10);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setDownlinkTone(const SelectiveCall &tone) {\n  if (tone.isInvalid()) {\n    setUInt8(Offset::downlinkToneType(), (int)ToneType::None);\n  } else if (tone.isCTCSS()) {\n    setUInt8(Offset::downlinkToneType(), (int)ToneType::CTCSS);\n    setUInt8(Offset::downlinkCTCSS(), AnytoneCodeplug::CTCSS::encode(tone));\n  } else if (tone.isDCS()) {\n    uint16_t val = tone.binCode();\n    if (tone.isInverted()) val |= (1<<9);\n    setUInt8(Offset::downlinkToneType(), (int)ToneType::DCS);\n    setUInt16_le(Offset::downlinkDCS(), val);\n  }\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setUplink(const Frequency &f) {\n  setUInt32_le(Offset::uplinkFrequency(), f.inHz()/10);\n}\n\nvoid\nAnytoneSatelliteConfig::SatelliteElement::setUplinkTone(const SelectiveCall &tone) {\n  if (tone.isInvalid()) {\n    setUInt8(Offset::uplinkToneType(), (int)ToneType::None);\n  } else if (tone.isCTCSS()) {\n    setUInt8(Offset::uplinkToneType(), (int)ToneType::CTCSS);\n    setUInt8(Offset::uplinkCTCSS(), AnytoneCodeplug::CTCSS::encode(tone));\n  } else if (tone.isDCS()) {\n    uint16_t val = tone.binCode();\n    if (tone.isInverted()) val |= (1<<9);\n    setUInt8(Offset::uplinkToneType(), (int)ToneType::DCS);\n    setUInt16_le(Offset::uplinkDCS(), val);\n  }\n}\n\n\nbool\nAnytoneSatelliteConfig::SatelliteElement::encode(const Satellite &sat, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  setName(sat.name());\n  setEpoch(sat.epoch());\n  setMeanMotionDerivative(sat.meanMotionDerivative());\n  setInclination(sat.inclination());\n  setAscension(sat.ascension());\n  setEccentricity(sat.eccentricity());\n  setPerigee(sat.perigee());\n  setAnomaly(sat.meanAnomaly());\n  setMeanMotion(sat.meanMotion());\n  setRevolution(sat.revolutionNumber());\n\n  setDownlink(sat.fmDownlink());\n  setDownlinkTone(sat.fmDownlinkTone());\n  setUplink(sat.fmUplink());\n  setUplinkTone(sat.fmUplinkTone());\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSatelliteConfig\n * ********************************************************************************************* */\nAnytoneSatelliteConfig::AnytoneSatelliteConfig(QObject *parent)\n  : SatelliteConfig{parent}\n{\n  addImage(\"AnyTone Satellite Configuration\");\n}\n\n\nAnytoneSatelliteConfig::SatelliteElement\nAnytoneSatelliteConfig::satellite(unsigned int idx) {\n  return SatelliteElement(data(Offset::satellites() + idx*Offset::betweenSatellites()));\n}\n\n\nbool\nAnytoneSatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) {\n  unsigned int numSat = std::min(Limit::satellites(), db->count());\n  image(0).addElement(Offset::satellites(), numSat*SatelliteElement::size());\n\n  for (unsigned int i=0; i<numSat; i++) {\n    logDebug() << \"Encode sat '\" << db->getAt(i).name() << \"' at index \" << i << \".\";\n    if (! satellite(i).encode(db->getAt(i), err)) {\n      errMsg(err) << \"Cannot encode satellite '\" << db->getAt(i).name()\n                  << \"at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/anytone_satelliteconfig.hh",
    "content": "#ifndef ANYTONE_SATELLITECONFIG_HH\n#define ANYTONE_SATELLITECONFIG_HH\n\n#include \"satelliteconfig.hh\"\n#include \"frequency.hh\"\n#include \"signaling.hh\"\n#include \"codeplug.hh\"\n#include \"orbitalelementsdatabase.hh\"\n\n// Forward declarations\nclass Satellite;\n\n/** Implementation of satellite configuration for all AnyTone devices.\n *\n * For now only the D878UV, D878UV2 and the DMR6X2 support sat tracking.\n *\n * @ingroup anytone */\nclass AnytoneSatelliteConfig : public SatelliteConfig\n{\n  Q_OBJECT\n\npublic:\n  class SatelliteElement: Codeplug::Element\n  {\n  protected:\n    /** Internal encoding of sub tone type. */\n    enum class ToneType {\n      None = 0, CTCSS = 1, DCS = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    SatelliteElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit SatelliteElement(uint8_t *ptr);\n\n    static constexpr unsigned int size() { return 0x200; }\n\n    void clear();\n\n    /** Sets the satellite name. */\n    void setName(const QString &name);\n\n    /** Sets the epoch of the orbital element. */\n    void setEpoch(const OrbitalElement::Epoch &epoch);\n    /** Sets the derivative of the mean motion. */\n    void setMeanMotionDerivative(double dmm);\n    /** Sets the inclination. */\n    void setInclination(double incl);\n    /** Sets the right ascension of the ascending node. */\n    void setAscension(double asc);\n    /** Sets the eccentricity */\n    void setEccentricity(double ecc);\n    /** Sets the argument of perigee. */\n    void setPerigee(double peri);\n    /** Sets the mean anomaly. */\n    void setAnomaly(double ma);\n    /** Sets the mean motion. */\n    void setMeanMotion(double mm);\n    /** Sets the revolution number. */\n    void setRevolution(unsigned int num);\n\n    /** Sets the downlink frequency. */\n    void setDownlink(const Frequency &f);\n    /** Sets the downlink sub tone. */\n    void setDownlinkTone(const SelectiveCall &tone);\n    /** Sets the uplink frequency. */\n    void setUplink(const Frequency &f);\n    /** Sets the uplink sub tone. */\n    void setUplinkTone(const SelectiveCall &tone);\n\n    /** Encodes the given satellite. */\n    bool encode(const Satellite &sat, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the satellite. */\n    struct Limit: Element::Limit {\n      /** Maximum size of satellite name. */\n      static constexpr unsigned int name() { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int epochYear() { return 0x0008; }\n      static constexpr unsigned int epochDay() { return 0x000a; }\n      static constexpr unsigned int meanMotionDerivative() { return 0x0017; }\n      static constexpr unsigned int inclination() { return 0x0021; }\n      static constexpr unsigned int ascension() { return 0x002a; }\n      static constexpr unsigned int eccentricity() { return 0x0033; }\n      static constexpr unsigned int perigee() { return 0x003b; }\n      static constexpr unsigned int anomaly() { return 0x0044; }\n      static constexpr unsigned int meanMotion() { return 0x004d; }\n      static constexpr unsigned int revolution() { return 0x0058; }\n      static constexpr unsigned int downlinkFrequency() { return 0x0060; }\n      static constexpr unsigned int uplinkFrequency() { return 0x0064; }\n      static constexpr unsigned int uplinkToneType() { return 0x0068; }\n      static constexpr unsigned int downlinkToneType() { return 0x0069; }\n      static constexpr unsigned int uplinkCTCSS() { return 0x006a; }\n      static constexpr unsigned int downlinkCTCSS() { return 0x006b; }\n      static constexpr unsigned int uplinkDCS() { return 0x006c; }\n      static constexpr unsigned int downlinkDCS() { return 0x006e; }\n      /// @endcond\n    };\n  };\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneSatelliteConfig(QObject *parent = nullptr);\n\n  SatelliteElement satellite(unsigned int idx);\n  bool encode(SatelliteDatabase *db, const ErrorStack &err) override;\n\npublic:\n  /** Some limits for the satellite config. */\n  struct Limit {\n    static constexpr unsigned int satellites() { return 200; }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int satellites() { return 0x2ec0000; }\n    static constexpr unsigned int betweenSatellites() { return SatelliteElement::size(); }\n    /// @endcond\n  };\n};\n\n#endif // ANYTONE_SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/anytone_settingsextension.cc",
    "content": "#include \"anytone_settingsextension.hh\"\n#include <QTimeZone>\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneDMRSettingsExtension\n * ********************************************************************************************* */\nAnytoneDMRSettingsExtension::AnytoneDMRSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _manualGroupCallHangTime(Interval::fromSeconds(5)),\n    _manualPrivateCallHangTime(Interval::fromSeconds(7)),\n    _wakeHeadPeriod(Interval::fromMilliseconds(100)), _filterOwnID(true),\n    _monitorSlotMatch(SlotMatch::Off), _monitorColorCodeMatch(false), _monitorIDMatch(false),\n    _monitorTimeSlotHold(true), _talkerAliasSource(TalkerAliasSource::UserDB),\n    _encryption(EncryptionType::DMR)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneDMRSettingsExtension::clone() const {\n  AnytoneDMRSettingsExtension *ext = new AnytoneDMRSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nInterval\nAnytoneDMRSettingsExtension::manualGroupCallHangTime() const {\n  return _manualGroupCallHangTime;\n}\nvoid\nAnytoneDMRSettingsExtension::setManualGroupCallHangTime(Interval sec) {\n  if (_manualGroupCallHangTime == sec)\n    return;\n  _manualGroupCallHangTime = sec;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneDMRSettingsExtension::manualPrivateCallHangTime() const {\n  return _manualPrivateCallHangTime;\n}\nvoid\nAnytoneDMRSettingsExtension::setManualPrivateCallHangTime(Interval sec) {\n  if (_manualPrivateCallHangTime == sec)\n    return;\n  _manualPrivateCallHangTime = sec;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneDMRSettingsExtension::wakeHeadPeriod() const {\n  return _wakeHeadPeriod;\n}\nvoid\nAnytoneDMRSettingsExtension::setWakeHeadPeriod(Interval ms) {\n  if (_wakeHeadPeriod == ms)\n    return;\n  _wakeHeadPeriod = ms;\n  emit modified(this);\n}\n\nbool\nAnytoneDMRSettingsExtension::filterOwnIDEnabled() const {\n  return _filterOwnID;\n}\nvoid\nAnytoneDMRSettingsExtension::enableFilterOwnID(bool enable) {\n  if (_filterOwnID == enable)\n    return;\n  _filterOwnID = enable;\n  emit modified(this);\n}\n\nAnytoneDMRSettingsExtension::SlotMatch\nAnytoneDMRSettingsExtension::monitorSlotMatch() const {\n  return _monitorSlotMatch;\n}\nvoid\nAnytoneDMRSettingsExtension::setMonitorSlotMatch(SlotMatch match) {\n  if (_monitorSlotMatch == match)\n    return;\n  _monitorSlotMatch = match;\n  emit modified(this);\n}\n\nbool\nAnytoneDMRSettingsExtension::monitorColorCodeMatchEnabled() const {\n  return _monitorColorCodeMatch;\n}\nvoid\nAnytoneDMRSettingsExtension::enableMonitorColorCodeMatch(bool enable) {\n  if (_monitorColorCodeMatch == enable)\n    return;\n  _monitorColorCodeMatch = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDMRSettingsExtension::monitorIDMatchEnabled() const {\n  return _monitorIDMatch;\n}\nvoid\nAnytoneDMRSettingsExtension::enableMonitorIDMatch(bool enable) {\n  if (_monitorIDMatch == enable)\n    return;\n  _monitorIDMatch = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDMRSettingsExtension::monitorTimeSlotHoldEnabled() const {\n  return _monitorTimeSlotHold;\n}\nvoid\nAnytoneDMRSettingsExtension::enableMonitorTimeSlotHold(bool enable) {\n  if (_monitorTimeSlotHold == enable)\n    return;\n  _monitorTimeSlotHold = enable;\n  emit modified(this);\n}\n\n\nAnytoneDMRSettingsExtension::TalkerAliasSource\nAnytoneDMRSettingsExtension::talkerAliasSource() const {\n  return _talkerAliasSource;\n}\nvoid\nAnytoneDMRSettingsExtension::setTalkerAliasSource(TalkerAliasSource mode) {\n  if (mode == _talkerAliasSource)\n    return;\n  _talkerAliasSource = mode;\n  emit modified(this);\n}\n\n\nAnytoneDMRSettingsExtension::EncryptionType\nAnytoneDMRSettingsExtension::encryption() const {\n  return _encryption;\n}\nvoid\nAnytoneDMRSettingsExtension::setEncryption(EncryptionType type) {\n  if (type == _encryption)\n    return;\n  _encryption = type;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneGPSSettingsExtension\n * ********************************************************************************************* */\nAnytoneGPSSettingsExtension::AnytoneGPSSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _timeZone(QTimeZone::utc()),\n    _gpsRangeReporting(false), _gpsRangingInterval(Interval::fromSeconds(300))\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneGPSSettingsExtension::clone() const {\n  auto *ext = new AnytoneGPSSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nQTimeZone\nAnytoneGPSSettingsExtension::timeZone() const {\n  return _timeZone;\n}\n\nQString\nAnytoneGPSSettingsExtension::ianaTimeZone() const {\n  return QString::fromLocal8Bit(_timeZone.id());\n}\n\nvoid\nAnytoneGPSSettingsExtension::setTimeZone(const QTimeZone &zone) {\n  if (_timeZone == zone)\n    return;\n  _timeZone = zone;\n  emit modified(this);\n}\n\nvoid\nAnytoneGPSSettingsExtension::setIANATimeZone(const QString &id) {\n  setTimeZone(QTimeZone(id.toLocal8Bit()));\n}\n\n\nbool\nAnytoneGPSSettingsExtension::positionReportingEnabled() const {\n  return _gpsRangeReporting;\n}\n\nvoid\nAnytoneGPSSettingsExtension::enablePositionReporting(bool enable) {\n  if (_gpsRangeReporting == enable)\n    return;\n  _gpsRangeReporting = enable;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneGPSSettingsExtension::updatePeriod() const {\n  return _gpsRangingInterval;\n}\n\nvoid\nAnytoneGPSSettingsExtension::setUpdatePeriod(Interval sec) {\n  if (_gpsRangingInterval == sec)\n    return;\n  _gpsRangingInterval = sec;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneRangingSettingsExtension\n * ********************************************************************************************* */\nAnytoneRoamingSettingsExtension::AnytoneRoamingSettingsExtension(QObject *parent)\n  : ConfigItem(parent),\n    _autoRoam(false), _autoRoamPeriod(Interval::fromMinutes(1)), _autoRoamDelay(),\n    _repeaterRangeCheck(false), _repeaterCheckInterval(Interval::fromSeconds(5)), _repeaterRangeCheckCount(3),\n    _outOfRangeAlert(OutOfRangeAlert::None),\n    _roamingStartCondition(RoamStart::Periodic), _roamingReturnCondition(RoamStart::Periodic),\n    _notification(false), _notificationCount(1),\n    _gpsRoaming(false), _defaultRoamingZone(new RoamingZoneReference(this))\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneRoamingSettingsExtension::clone() const {\n  AnytoneRoamingSettingsExtension *ext = new AnytoneRoamingSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nAnytoneRoamingSettingsExtension::autoRoam() const {\n  return _autoRoam;\n}\nvoid\nAnytoneRoamingSettingsExtension::enableAutoRoam(bool enable) {\n  if (enable == _autoRoam)\n    return;\n  _autoRoam = enable;\n  emit modified(this);\n}\n\nInterval\nAnytoneRoamingSettingsExtension::autoRoamPeriod() const {\n  return _autoRoamPeriod;\n}\nvoid\nAnytoneRoamingSettingsExtension::setAutoRoamPeriod(Interval min) {\n  if (_autoRoamPeriod == min)\n    return;\n  _autoRoamPeriod = min;\n  emit modified(this);\n}\n\nInterval\nAnytoneRoamingSettingsExtension::autoRoamDelay() const {\n  return _autoRoamDelay;\n}\nvoid\nAnytoneRoamingSettingsExtension::setAutoRoamDelay(Interval min) {\n  if (_autoRoamDelay == min)\n    return;\n  _autoRoamDelay = min;\n  emit modified(this);\n}\n\nbool\nAnytoneRoamingSettingsExtension::repeaterRangeCheckEnabled() const {\n  return _repeaterRangeCheck;\n}\nvoid\nAnytoneRoamingSettingsExtension::enableRepeaterRangeCheck(bool enable) {\n  if (_repeaterRangeCheck == enable)\n    return;\n  _repeaterRangeCheck = enable;\n  emit modified(this);\n}\nInterval\nAnytoneRoamingSettingsExtension::repeaterCheckInterval() const {\n  return _repeaterCheckInterval;\n}\nvoid\nAnytoneRoamingSettingsExtension::setRepeaterCheckInterval(Interval sec) {\n  if (_repeaterCheckInterval == sec)\n    return;\n  _repeaterCheckInterval = sec;\n  emit modified(this);\n}\n\nunsigned int\nAnytoneRoamingSettingsExtension::repeaterRangeCheckCount() const {\n  return _repeaterRangeCheckCount;\n}\nvoid\nAnytoneRoamingSettingsExtension::setRepeaterRangeCheckCount(unsigned int sec) {\n  if (_repeaterRangeCheckCount == sec)\n    return;\n  _repeaterRangeCheckCount = sec;\n  emit modified(this);\n}\n\nAnytoneRoamingSettingsExtension::OutOfRangeAlert\nAnytoneRoamingSettingsExtension::outOfRangeAlert() const {\n  return _outOfRangeAlert;\n}\nvoid\nAnytoneRoamingSettingsExtension::setOutOfRangeAlert(OutOfRangeAlert type) {\n  if (type == _outOfRangeAlert)\n    return;\n  _outOfRangeAlert = type;\n  emit modified(this);\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nAnytoneRoamingSettingsExtension::roamingStartCondition() const {\n  return _roamingStartCondition;\n}\nvoid\nAnytoneRoamingSettingsExtension::setRoamingStartCondition(RoamStart start) {\n  if (_roamingStartCondition == start)\n    return;\n  _roamingStartCondition = start;\n  emit modified(this);\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nAnytoneRoamingSettingsExtension::roamingReturnCondition() const {\n  return _roamingReturnCondition;\n}\nvoid\nAnytoneRoamingSettingsExtension::setRoamingReturnCondition(RoamStart end) {\n  if (_roamingReturnCondition == end)\n    return;\n  _roamingReturnCondition = end;\n  emit modified(this);\n}\n\nbool\nAnytoneRoamingSettingsExtension::notificationEnabled() const {\n  return _notification;\n}\nvoid\nAnytoneRoamingSettingsExtension::enableNotification(bool enable) {\n  if (_notification == enable)\n    return;\n  _notification = enable;\n  emit modified(this);\n}\nunsigned int\nAnytoneRoamingSettingsExtension::notificationCount() const {\n  return _notificationCount;\n}\nvoid\nAnytoneRoamingSettingsExtension::setNotificationCount(unsigned int n) {\n  if (_notificationCount == n)\n    return;\n  _notificationCount = n;\n  emit modified(this);\n}\n\nbool\nAnytoneRoamingSettingsExtension::gpsRoaming() const {\n  return _gpsRoaming;\n}\nvoid\nAnytoneRoamingSettingsExtension::enableGPSRoaming(bool enable) {\n  if (enable == _gpsRoaming)\n    return;\n  _gpsRoaming = enable;\n  emit modified(this);\n}\n\nRoamingZoneReference *\nAnytoneRoamingSettingsExtension::defaultZone() const {\n  return _defaultRoamingZone;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSettingsExtension\n * ********************************************************************************************* */\nAnytoneSettingsExtension::AnytoneSettingsExtension(QObject *parent)\n  : ConfigExtension(parent),\n    _bootSettings(new AnytoneBootSettingsExtension(this)),\n    _powerSaveSettings(new AnytonePowerSaveSettingsExtension(this)),\n    _keySettings(new AnytoneKeySettingsExtension(this)),\n    _toneSettings(new AnytoneToneSettingsExtension(this)),\n    _displaySettings(new AnytoneDisplaySettingsExtension(this)),\n    _audioSettings(new AnytoneAudioSettingsExtension(this)),\n    _menuSettings(new AnytoneMenuSettingsExtension(this)),\n    _autoRepeaterSettings(new AnytoneAutoRepeaterSettingsExtension(this)),\n    _dmrSettings(new AnytoneDMRSettingsExtension(this)),\n    _gpsSettings(new AnytoneGPSSettingsExtension(this)),\n    _roamingSettings(new AnytoneRoamingSettingsExtension(this)),\n    _bluetoothSettings(new AnytoneBluetoothSettingsExtension(this)),\n    _repeaterSettings(new AnytoneRepeaterSettingsExtension(this)),\n    _satelliteSettings(new AnytoneSatelliteSettingsExtension(this)),\n    _vfoScanType(VFOScanType::Time), _modeA(VFOMode::Memory), _modeB(VFOMode::Memory),\n    _zoneA(), _zoneB(), _selectedVFO(VFO::A), _subChannel(true),\n    _minVFOScanFrequencyUHF(Frequency::fromMHz(430)), _maxVFOScanFrequencyUHF(Frequency::fromMHz(440)),\n    _minVFOScanFrequencyVHF(Frequency::fromMHz(144)), _maxVFOScanFrequencyVHF(Frequency::fromMHz(146)),\n    _keepLastCaller(false), _vfoStep(Frequency::fromkHz(5)), _steType(STEType::Off), _steFrequency(0),\n    _steDuration(Interval::fromMilliseconds(300)), _tbstFrequency(Frequency::fromHz(1750)),\n    _proMode(false), _maintainCallChannel(false), _fan(FanControl::Temperature)\n{\n  connect(_bootSettings, &AnytoneBootSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_keySettings, &AnytoneKeySettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_toneSettings, &AnytoneToneSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_displaySettings, &AnytoneDisplaySettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_audioSettings, &AnytoneAudioSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_menuSettings, &AnytoneMenuSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_autoRepeaterSettings, &AnytoneAutoRepeaterSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_dmrSettings, &AnytoneDMRSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_roamingSettings, &AnytoneRoamingSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n  connect(_repeaterSettings, &AnytoneRepeaterSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n    connect(_satelliteSettings, &AnytoneSatelliteSettingsExtension::modified,\n          this, &AnytoneSettingsExtension::modified);\n}\n\nConfigItem *\nAnytoneSettingsExtension::clone() const {\n  AnytoneSettingsExtension *ext = new AnytoneSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n\n  return ext;\n}\n\nAnytoneBootSettingsExtension *\nAnytoneSettingsExtension::bootSettings() const {\n  return _bootSettings;\n}\n\nAnytonePowerSaveSettingsExtension *\nAnytoneSettingsExtension::powerSaveSettings() const {\n  return _powerSaveSettings;\n}\n\nAnytoneKeySettingsExtension *\nAnytoneSettingsExtension::keySettings() const {\n  return _keySettings;\n}\n\nAnytoneToneSettingsExtension *\nAnytoneSettingsExtension::toneSettings() const {\n  return _toneSettings;\n}\n\nAnytoneDisplaySettingsExtension *\nAnytoneSettingsExtension::displaySettings() const {\n  return _displaySettings;\n}\n\nAnytoneAudioSettingsExtension *\nAnytoneSettingsExtension::audioSettings() const {\n  return _audioSettings;\n}\n\nAnytoneMenuSettingsExtension *\nAnytoneSettingsExtension::menuSettings() const {\n  return _menuSettings;\n}\n\nAnytoneAutoRepeaterSettingsExtension *\nAnytoneSettingsExtension::autoRepeaterSettings() const {\n  return _autoRepeaterSettings;\n}\n\nAnytoneDMRSettingsExtension *\nAnytoneSettingsExtension::dmrSettings() const {\n  return _dmrSettings;\n}\n\nAnytoneGPSSettingsExtension *\nAnytoneSettingsExtension::gpsSettings() const {\n  return _gpsSettings;\n}\n\nAnytoneRoamingSettingsExtension *\nAnytoneSettingsExtension::roamingSettings() const {\n  return _roamingSettings;\n}\n\nAnytoneBluetoothSettingsExtension *\nAnytoneSettingsExtension::bluetoothSettings() const {\n  return _bluetoothSettings;\n}\n\nAnytoneRepeaterSettingsExtension *\nAnytoneSettingsExtension::repeaterSettings() const {\n  return _repeaterSettings;\n}\n\nAnytoneSatelliteSettingsExtension *\nAnytoneSettingsExtension::satelliteSettings() const {\n  return _satelliteSettings;\n}\n\n\nAnytoneSettingsExtension::VFOScanType\nAnytoneSettingsExtension::vfoScanType() const {\n  return _vfoScanType;\n}\nvoid\nAnytoneSettingsExtension::setVFOScanType(VFOScanType type) {\n  if (_vfoScanType == type)\n    return;\n  _vfoScanType = type;\n  emit modified(this);\n}\n\nAnytoneSettingsExtension::VFOMode\nAnytoneSettingsExtension::modeA() const {\n  return _modeA;\n}\nvoid\nAnytoneSettingsExtension::setModeA(VFOMode mode) {\n  if (_modeA == mode)\n    return;\n  _modeA = mode;\n  emit modified(this);\n}\n\nAnytoneSettingsExtension::VFOMode\nAnytoneSettingsExtension::modeB() const {\n  return _modeB;\n}\nvoid\nAnytoneSettingsExtension::setModeB(VFOMode mode) {\n  if (_modeB == mode)\n    return;\n  _modeB = mode;\n  emit modified(this);\n}\n\nZoneReference *\nAnytoneSettingsExtension::zoneA() {\n  return &_zoneA;\n}\nconst ZoneReference *\nAnytoneSettingsExtension::zoneA() const {\n  return &_zoneA;\n}\nZoneReference *\nAnytoneSettingsExtension::zoneB() {\n  return &_zoneB;\n}\nconst ZoneReference *\nAnytoneSettingsExtension::zoneB() const {\n  return &_zoneB;\n}\n\nAnytoneSettingsExtension::VFO\nAnytoneSettingsExtension::selectedVFO() const {\n  return _selectedVFO;\n}\nvoid\nAnytoneSettingsExtension::setSelectedVFO(VFO vfo) {\n  if (_selectedVFO == vfo)\n    return;\n  _selectedVFO = vfo;\n  emit modified(this);\n}\n\nbool\nAnytoneSettingsExtension::subChannelEnabled() const {\n  return _subChannel;\n}\nvoid\nAnytoneSettingsExtension::enableSubChannel(bool enable) {\n  if (_subChannel == enable)\n    return;\n  _subChannel = enable;\n  emit modified(this);\n}\n\nFrequency\nAnytoneSettingsExtension::minVFOScanFrequencyUHF() const {\n  return _minVFOScanFrequencyUHF;\n}\nvoid\nAnytoneSettingsExtension::setMinVFOScanFrequencyUHF(Frequency hz) {\n  if (_minVFOScanFrequencyUHF == hz)\n    return;\n  _minVFOScanFrequencyUHF = hz;\n  emit modified(this);\n}\nFrequency\nAnytoneSettingsExtension::maxVFOScanFrequencyUHF() const {\n  return _maxVFOScanFrequencyUHF;\n}\nvoid\nAnytoneSettingsExtension::setMaxVFOScanFrequencyUHF(Frequency hz) {\n  if (_maxVFOScanFrequencyUHF == hz)\n    return;\n  _maxVFOScanFrequencyUHF = hz;\n  emit modified(this);\n}\n\nFrequency\nAnytoneSettingsExtension::minVFOScanFrequencyVHF() const {\n  return _minVFOScanFrequencyVHF;\n}\nvoid\nAnytoneSettingsExtension::setMinVFOScanFrequencyVHF(Frequency hz) {\n  if (_minVFOScanFrequencyVHF == hz)\n    return;\n  _minVFOScanFrequencyVHF = hz;\n  emit modified(this);\n}\nFrequency\nAnytoneSettingsExtension::maxVFOScanFrequencyVHF() const {\n  return _maxVFOScanFrequencyVHF;\n}\nvoid\nAnytoneSettingsExtension::setMaxVFOScanFrequencyVHF(Frequency hz) {\n  if (_maxVFOScanFrequencyVHF == hz)\n    return;\n  _maxVFOScanFrequencyVHF = hz;\n  emit modified(this);\n}\n\nbool\nAnytoneSettingsExtension::keepLastCallerEnabled() const {\n  return _keepLastCaller;\n}\nvoid\nAnytoneSettingsExtension::enableKeepLastCaller(bool enable) {\n  if (_keepLastCaller == enable)\n    return;\n  _keepLastCaller = enable;\n  emit modified(this);\n}\n\nFrequency\nAnytoneSettingsExtension::vfoStep() const {\n  return _vfoStep;\n}\nvoid\nAnytoneSettingsExtension::setVFOStep(Frequency step) {\n  if (_vfoStep == step)\n    return;\n  _vfoStep = step;\n  emit modified(this);\n}\n\nAnytoneSettingsExtension::STEType\nAnytoneSettingsExtension::steType() const {\n  return _steType;\n}\nvoid\nAnytoneSettingsExtension::setSTEType(STEType type) {\n  if (_steType == type)\n    return;\n  _steType = type;\n  emit modified(this);\n}\ndouble\nAnytoneSettingsExtension::steFrequency() const {\n  return _steFrequency;\n}\nvoid\nAnytoneSettingsExtension::setSTEFrequency(double freq) {\n  if (_steFrequency == freq)\n    return;\n  _steFrequency = freq;\n  emit modified(this);\n}\n\nInterval\nAnytoneSettingsExtension::steDuration() const {\n  return _steDuration;\n}\nvoid\nAnytoneSettingsExtension::setSTEDuration(Interval intv) {\n  if (intv == _steDuration)\n    return;\n  _steDuration = intv;\n  emit modified(this);\n}\n\nFrequency\nAnytoneSettingsExtension::tbstFrequency() const {\n  return _tbstFrequency;\n}\nvoid\nAnytoneSettingsExtension::setTBSTFrequency(Frequency Hz) {\n  if (_tbstFrequency == Hz)\n    return;\n  _tbstFrequency = Hz;\n  emit modified(this);\n}\n\n\nbool\nAnytoneSettingsExtension::proModeEnabled() const {\n  return _proMode;\n}\n\nvoid\nAnytoneSettingsExtension::enableProMode(bool enable) {\n  if (_proMode == enable)\n    return;\n  _proMode = enable;\n  emit modified(this);\n}\n\n\nbool\nAnytoneSettingsExtension::maintainCallChannelEnabled() const {\n  return _maintainCallChannel;\n}\n\nvoid\nAnytoneSettingsExtension::enableMaintainCallChannel(bool enable) {\n  if (_maintainCallChannel == enable)\n    return;\n  _maintainCallChannel = enable;\n  emit modified(this);\n}\n\n\nAnytoneSettingsExtension::FanControl\nAnytoneSettingsExtension::fan() const {\n  return _fan;\n}\n\nvoid\nAnytoneSettingsExtension::setFan(FanControl ctrl) {\n  if (_fan == ctrl)\n    return;\n  _fan = ctrl;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneBootSettingsExtension\n * ********************************************************************************************* */\nAnytoneBootSettingsExtension::AnytoneBootSettingsExtension(QObject *parent)\n  : ConfigItem(parent),\n    _priorityZoneA(new ZoneReference(this)), _priorityZoneB(new ZoneReference(this)),\n    _gpsCheck(false)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneBootSettingsExtension::clone() const {\n  AnytoneBootSettingsExtension *ext = new AnytoneBootSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nZoneReference *\nAnytoneBootSettingsExtension::priorityZoneA() const {\n  return _priorityZoneA;\n}\nZoneReference *\nAnytoneBootSettingsExtension::priorityZoneB() const {\n  return _priorityZoneB;\n}\n\nbool\nAnytoneBootSettingsExtension::gpsCheckEnabled() const {\n  return _gpsCheck;\n}\nvoid\nAnytoneBootSettingsExtension::enableGPSCheck(bool enable) {\n  if (_gpsCheck == enable)\n    return;\n  _gpsCheck = enable;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytonePowerSaveSettingsExtension\n * ********************************************************************************************* */\nAnytonePowerSaveSettingsExtension::AnytonePowerSaveSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _autoShutDownDelay(), _resetAutoShutdownOnCall(true),\n    _powerSave(PowerSave::Save50), _atpc(false)\n{\n  // pass...\n}\n\nConfigItem *\nAnytonePowerSaveSettingsExtension::clone() const {\n  AnytonePowerSaveSettingsExtension *ext = new AnytonePowerSaveSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nInterval\nAnytonePowerSaveSettingsExtension::autoShutdown() const {\n  return _autoShutDownDelay;\n}\nvoid\nAnytonePowerSaveSettingsExtension::setAutoShutdown(Interval intv) {\n  if (_autoShutDownDelay == intv)\n    return;\n  _autoShutDownDelay = intv;\n  emit modified(this);\n}\n\nbool\nAnytonePowerSaveSettingsExtension::resetAutoShutdownOnCall() const {\n  return _resetAutoShutdownOnCall;\n}\nvoid\nAnytonePowerSaveSettingsExtension::enableResetAutoShutdownOnCall(bool enable) {\n  if (enable == _resetAutoShutdownOnCall)\n    return;\n  _resetAutoShutdownOnCall = enable;\n  emit modified(this);\n}\n\nAnytonePowerSaveSettingsExtension::PowerSave\nAnytonePowerSaveSettingsExtension::powerSave() const {\n  return _powerSave;\n}\nvoid\nAnytonePowerSaveSettingsExtension::setPowerSave(PowerSave save) {\n  if (_powerSave == save)\n    return;\n  _powerSave = save;\n  emit modified(this);\n}\n\nbool\nAnytonePowerSaveSettingsExtension::atpc() const {\n  return _atpc;\n}\nvoid\nAnytonePowerSaveSettingsExtension::enableATPC(bool enable) {\n  if (enable == _atpc)\n    return;\n  _atpc = enable;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneKeySettingsExtension\n * ********************************************************************************************* */\nAnytoneKeySettingsExtension::AnytoneKeySettingsExtension(QObject *parent)\n  : ConfigItem(parent),\n    _funcKey1Short(KeyFunction::VOX), _funcKey1Long(KeyFunction::ToggleVFO),\n    _funcKey2Short(KeyFunction::Reverse), _funcKey2Long(KeyFunction::Off),\n    _funcKey3Short(KeyFunction::Power), _funcKey3Long(KeyFunction::Record),\n    _funcKey4Short(KeyFunction::Repeater), _funcKey4Long(KeyFunction::SMS),\n    _funcKey5Short(KeyFunction::Reverse), _funcKey5Long(KeyFunction::Dial),\n    _funcKey6Short(KeyFunction::Encryption), _funcKey6Long(KeyFunction::Off),\n    _funcKeyAShort(KeyFunction::Off), _funcKeyALong(KeyFunction::Encryption),\n    _funcKeyBShort(KeyFunction::Voltage), _funcKeyBLong(KeyFunction::Call),\n    _funcKeyCShort(KeyFunction::Power), _funcKeyCLong(KeyFunction::VOX),\n    _funcKeyDShort(KeyFunction::Off), _funcKeyDLong(KeyFunction::Off),\n    _funcKnobShort(KeyFunction::Off), _funcKnobLong(KeyFunction::Off),\n    _longPressDuration(Interval::fromSeconds(1)), _upDownFunction(UpDownKeyFunction::Channel),\n    _autoKeyLock(false), _knobLock(false), _keypadLock(false),\n    _sideKeysLock(false), _forcedKeyLock(false)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneKeySettingsExtension::clone() const {\n  AnytoneKeySettingsExtension *ext = new AnytoneKeySettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey1Short() const {\n  return _funcKey1Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey1Short(KeyFunction func) {\n  if (_funcKey1Short == func)\n    return;\n  _funcKey1Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey1Long() const {\n  return _funcKey1Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey1Long(KeyFunction func) {\n  if (_funcKey1Long == func)\n    return;\n  _funcKey1Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey2Short() const {\n  return _funcKey2Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey2Short(KeyFunction func) {\n  if (_funcKey2Short == func)\n    return;\n  _funcKey2Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey2Long() const {\n  return _funcKey2Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey2Long(KeyFunction func) {\n  if (_funcKey2Long == func)\n    return;\n  _funcKey2Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey3Short() const {\n  return _funcKey3Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey3Short(KeyFunction func) {\n  if (_funcKey3Short == func)\n    return;\n  _funcKey3Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey3Long() const {\n  return _funcKey3Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey3Long(KeyFunction func) {\n  if (_funcKey3Long == func)\n    return;\n  _funcKey3Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey4Short() const {\n  return _funcKey4Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey4Short(KeyFunction func) {\n  if (_funcKey4Short == func)\n    return;\n  _funcKey4Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey4Long() const {\n  return _funcKey4Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey4Long(KeyFunction func) {\n  if (_funcKey4Long == func)\n    return;\n  _funcKey4Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey5Short() const {\n  return _funcKey5Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey5Short(KeyFunction func) {\n  if (_funcKey5Short == func)\n    return;\n  _funcKey5Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey5Long() const {\n  return _funcKey5Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey5Long(KeyFunction func) {\n  if (_funcKey5Long == func)\n    return;\n  _funcKey5Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey6Short() const {\n  return _funcKey6Short;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey6Short(KeyFunction func) {\n  if (_funcKey6Short == func)\n    return;\n  _funcKey6Short = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKey6Long() const {\n  return _funcKey6Long;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKey6Long(KeyFunction func) {\n  if (_funcKey6Long == func)\n    return;\n  _funcKey6Long = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyAShort() const {\n  return _funcKeyAShort;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyAShort(KeyFunction func) {\n  if (_funcKeyAShort == func)\n    return;\n  _funcKeyAShort = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyALong() const {\n  return _funcKeyALong;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyALong(KeyFunction func) {\n  if (_funcKeyALong == func)\n    return;\n  _funcKeyALong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyBShort() const {\n  return _funcKeyBShort;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyBShort(KeyFunction func) {\n  if (_funcKeyBShort == func)\n    return;\n  _funcKeyBShort = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyBLong() const {\n  return _funcKeyBLong;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyBLong(KeyFunction func) {\n  if (_funcKeyBLong == func)\n    return;\n  _funcKeyBLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyCShort() const {\n  return _funcKeyCShort;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyCShort(KeyFunction func) {\n  if (_funcKeyCShort == func)\n    return;\n  _funcKeyCShort = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyCLong() const {\n  return _funcKeyCLong;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyCLong(KeyFunction func) {\n  if (_funcKeyCLong == func)\n    return;\n  _funcKeyCLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyDShort() const {\n  return _funcKeyDShort;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyDShort(KeyFunction func) {\n  if (_funcKeyDShort == func)\n    return;\n  _funcKeyDShort = func;\n  emit modified(this);\n}\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKeyDLong() const {\n  return _funcKeyDLong;\n}\nvoid\nAnytoneKeySettingsExtension::setFuncKeyDLong(KeyFunction func) {\n  if (_funcKeyDLong == func)\n    return;\n  _funcKeyDLong = func;\n  emit modified(this);\n}\n\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKnobShort() const {\n  return _funcKnobShort;\n}\n\nvoid\nAnytoneKeySettingsExtension::setFuncKnobShort(KeyFunction func) {\n  if (_funcKnobShort == func)\n    return;\n  _funcKnobShort = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneKeySettingsExtension::funcKnobLong() const {\n  return _funcKnobLong;\n}\n\nvoid\nAnytoneKeySettingsExtension::setFuncKnobLong(KeyFunction func) {\n  if (_funcKnobLong == func)\n    return;\n  _funcKnobLong = func;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneKeySettingsExtension::longPressDuration() const {\n  return _longPressDuration;\n}\nvoid\nAnytoneKeySettingsExtension::setLongPressDuration(Interval ms) {\n  if (_longPressDuration == ms)\n    return;\n  _longPressDuration = ms;\n  emit modified(this);\n}\n\n\nAnytoneKeySettingsExtension::UpDownKeyFunction\nAnytoneKeySettingsExtension::upDownKeyFunction() const {\n  return _upDownFunction;\n}\n\nvoid\nAnytoneKeySettingsExtension::setUpDownKeyFunction(UpDownKeyFunction func) {\n  if (_upDownFunction == func)\n    return;\n  _upDownFunction = func;\n  emit modified(this);\n}\n\n\nbool\nAnytoneKeySettingsExtension::autoKeyLockEnabled() const {\n  return _autoKeyLock;\n}\nvoid\nAnytoneKeySettingsExtension::enableAutoKeyLock(bool enabled) {\n  if (_autoKeyLock==enabled)\n    return;\n  _autoKeyLock = enabled;\n  emit modified(this);\n}\n\nbool\nAnytoneKeySettingsExtension::knobLockEnabled() const {\n  return _knobLock;\n}\nvoid\nAnytoneKeySettingsExtension::enableKnobLock(bool enable) {\n  if (_knobLock == enable)\n    return;\n  _knobLock = enable;\n  emit modified(this);\n}\nbool\nAnytoneKeySettingsExtension::keypadLockEnabled() const {\n  return _keypadLock;\n}\nvoid\nAnytoneKeySettingsExtension::enableKeypadLock(bool enable) {\n  if (_keypadLock == enable)\n    return;\n  _keypadLock = enable;\n  emit modified(this);\n}\nbool\nAnytoneKeySettingsExtension::sideKeysLockEnabled() const {\n  return _sideKeysLock;\n}\nvoid\nAnytoneKeySettingsExtension::enableSideKeysLock(bool enable) {\n  if (_sideKeysLock == enable)\n    return;\n  _sideKeysLock = enable;\n  emit modified(this);\n}\nbool\nAnytoneKeySettingsExtension::forcedKeyLockEnabled() const {\n  return _forcedKeyLock;\n}\nvoid\nAnytoneKeySettingsExtension::enableForcedKeyLock(bool enable) {\n  if (_forcedKeyLock == enable)\n    return;\n  _forcedKeyLock = enable;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneToneSettingsExtension\n * ********************************************************************************************* */\nAnytoneToneSettingsExtension::AnytoneToneSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _totNotification(false), _wxAlarm(false)\n{\n}\n\nConfigItem *\nAnytoneToneSettingsExtension::clone() const {\n  AnytoneToneSettingsExtension *ext = new AnytoneToneSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nbool\nAnytoneToneSettingsExtension::totNotification() const {\n  return _totNotification;\n}\n\nvoid\nAnytoneToneSettingsExtension::enableTOTNotification(bool enable) {\n  if (enable == _totNotification)\n    return;\n  _totNotification = enable;\n  emit modified(this);\n}\n\n\nbool\nAnytoneToneSettingsExtension::wxAlarm() const {\n  return _wxAlarm;\n}\nvoid\nAnytoneToneSettingsExtension::enableWXAlarm(bool enable) {\n  if (enable == _wxAlarm)\n    return;\n  _wxAlarm = enable;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneDisplaySettingsExtension\n * ********************************************************************************************* */\nAnytoneDisplaySettingsExtension::AnytoneDisplaySettingsExtension(QObject *parent)\n  : ConfigItem(parent), _displayFrequency(false), _brightness(5),\n  _volumeChangePrompt(true), _callEndPrompt(true),\n  _lastCallerDisplay(LastCallerDisplayMode::Both), _showClock(true), _showCall(true),\n  _callColor(Color::Orange), _language(Language::English), _dateFormat(DateFormat::DayFirst),\n  _showChannelNumber(true), _showGlobalChannelNumber(false), _showColorCode(true),\n  _showTimeSlot(true), _showChannelType(true), _showContact(true), _standbyTextColor(Color::White),\n  _standbyBackgroundColor(Color::Black), _showLastHeard(false),\n  _backlightDuration(Interval::infinity()), _backlightDurationTX(),\n  _backlightDurationRX(Interval::infinity()),\n  _customChannelBackground(false), _channelNameColor(Color::Orange),\n  _channelBNameColor(Color::Orange), _zoneNameColor(Color::White), _zoneBNameColor(Color::White)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneDisplaySettingsExtension::clone() const {\n  AnytoneDisplaySettingsExtension *ext = new AnytoneDisplaySettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nunsigned int\nAnytoneDisplaySettingsExtension::brightness() const {\n  return _brightness;\n}\nvoid\nAnytoneDisplaySettingsExtension::setBrightness(unsigned int level) {\n  if (_brightness == level)\n    return;\n  _brightness = level;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::displayFrequencyEnabled() const {\n  return _displayFrequency;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableDisplayFrequency(bool enable) {\n  if (_displayFrequency == enable)\n    return;\n  _displayFrequency = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::volumeChangePromptEnabled() const {\n  return _volumeChangePrompt;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableVolumeChangePrompt(bool enable) {\n  if (_volumeChangePrompt == enable)\n    return;\n  _volumeChangePrompt = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::callEndPromptEnabled() const {\n  return _callEndPrompt;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableCallEndPrompt(bool enable) {\n  if (_callEndPrompt == enable)\n    return;\n  _callEndPrompt = enable;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::LastCallerDisplayMode\nAnytoneDisplaySettingsExtension::lastCallerDisplay() const {\n  return _lastCallerDisplay;\n}\nvoid\nAnytoneDisplaySettingsExtension::setLastCallerDisplay(LastCallerDisplayMode mode) {\n  if (_lastCallerDisplay == mode)\n    return;\n  _lastCallerDisplay = mode;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showClockEnabled() const {\n  return _showClock;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowClock(bool enable) {\n  if (_showClock == enable)\n    return;\n  _showClock = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showCallEnabled() const {\n  return _showCall;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowCall(bool enable) {\n  if (_showCall == enable)\n    return;\n  _showCall = enable;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::callColor() const {\n  return _callColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setCallColor(Color color) {\n  if (_callColor == color)\n    return;\n  _callColor = color;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Language\nAnytoneDisplaySettingsExtension::language() const {\n  return _language;\n}\nvoid\nAnytoneDisplaySettingsExtension::setLanguage(Language lang) {\n  if (_language == lang)\n    return;\n  _language = lang;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::DateFormat\nAnytoneDisplaySettingsExtension::dateFormat() const {\n  return _dateFormat;\n}\nvoid\nAnytoneDisplaySettingsExtension::setDateFormat(DateFormat format) {\n  if (format == _dateFormat)\n    return;\n  _dateFormat = format;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showChannelNumberEnabled() const {\n  return _showChannelNumber;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowChannelNumber(bool enable) {\n  if (_showChannelNumber == enable)\n    return;\n  _showChannelNumber = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showGlobalChannelNumber() const {\n  return _showGlobalChannelNumber;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowGlobalChannelNumber(bool enable) {\n  if (_showGlobalChannelNumber == enable)\n    return;\n  _showGlobalChannelNumber = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showColorCode() const {\n  return _showColorCode;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowColorCode(bool enable) {\n  if (_showColorCode == enable)\n    return;\n  _showColorCode = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showTimeSlot() const {\n  return _showTimeSlot;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowTimeSlot(bool enable) {\n  if (_showTimeSlot == enable)\n    return;\n  _showTimeSlot = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showChannelType() const {\n  return _showChannelType;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowChannelType(bool enable) {\n  if (_showChannelType == enable)\n    return;\n  _showChannelType = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showContact() const {\n  return _showContact;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowContact(bool enable) {\n  if (_showContact == enable)\n    return;\n  _showContact = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::showLastHeardEnabled() const {\n  return _showLastHeard;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableShowLastHeard(bool enable) {\n  if (_showLastHeard == enable)\n    return;\n  _showLastHeard = enable;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::standbyTextColor() const {\n  return _standbyTextColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setStandbyTextColor(Color color) {\n  if (_standbyTextColor == color)\n    return;\n  _standbyTextColor = color;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::standbyBackgroundColor() const {\n  return _standbyBackgroundColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setStandbyBackgroundColor(Color color) {\n  if (_standbyBackgroundColor == color)\n    return;\n  _standbyBackgroundColor = color;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneDisplaySettingsExtension::backlightDuration() const {\n  return _backlightDuration;\n}\n\nvoid\nAnytoneDisplaySettingsExtension::setBacklightDuration(Interval sec) {\n  if (_backlightDuration == sec)\n    return;\n  _backlightDuration = sec;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneDisplaySettingsExtension::backlightDurationTX() const {\n  return _backlightDurationTX;\n}\n\nvoid\nAnytoneDisplaySettingsExtension::setBacklightDurationTX(Interval sec) {\n  if (_backlightDurationTX == sec)\n    return;\n  _backlightDurationTX = sec;\n  emit modified(this);\n}\n\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::channelNameColor() const {\n  return _channelNameColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setChannelNameColor(Color color) {\n  if (_channelNameColor == color)\n    return;\n  _channelNameColor = color;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::channelBNameColor() const {\n  return _channelBNameColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setChannelBNameColor(Color color) {\n  if (_channelBNameColor == color)\n    return;\n  _channelBNameColor = color;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::zoneNameColor() const {\n  return _zoneNameColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setZoneNameColor(Color color) {\n  if (_zoneNameColor == color)\n    return;\n  _zoneNameColor = color;\n  emit modified(this);\n}\n\nAnytoneDisplaySettingsExtension::Color\nAnytoneDisplaySettingsExtension::zoneBNameColor() const {\n  return _zoneBNameColor;\n}\nvoid\nAnytoneDisplaySettingsExtension::setZoneBNameColor(Color color) {\n  if (_zoneBNameColor == color)\n    return;\n  _zoneBNameColor = color;\n  emit modified(this);\n}\n\nInterval\nAnytoneDisplaySettingsExtension::backlightDurationRX() const {\n  return _backlightDurationRX;\n}\nvoid\nAnytoneDisplaySettingsExtension::setBacklightDurationRX(Interval sec) {\n  if (_backlightDurationRX == sec)\n    return;\n  _backlightDurationRX = sec;\n  emit modified(this);\n}\n\nbool\nAnytoneDisplaySettingsExtension::customChannelBackground() const {\n  return _customChannelBackground;\n}\nvoid\nAnytoneDisplaySettingsExtension::enableCustomChannelBackground(bool enable) {\n  if (enable == _customChannelBackground)\n    return;\n  _customChannelBackground = enable;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAudioSettingsExtension\n * ********************************************************************************************* */\nAnytoneAudioSettingsExtension::AnytoneAudioSettingsExtension(QObject *parent)\n  : ConfigItem(parent),  _voxDelay(), _recording(false), _voxSource(VoxSource::Both),\n  _enhanceAudio(true), _muteDelay(Interval::fromMinutes(1)),\n  _speaker(Speaker::Radio), _handsetSpeaker(HandsetSpeakerSource::MainChannel),\n  _handsetType(HandsetType::Anytone)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneAudioSettingsExtension::clone() const {\n  AnytoneAudioSettingsExtension *ext = new AnytoneAudioSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nAnytoneAudioSettingsExtension::recordingEnabled() const {\n  return _recording;\n}\nvoid\nAnytoneAudioSettingsExtension::enableRecording(bool enable) {\n  if (_recording == enable)\n    return;\n  _recording = enable;\n  emit modified(this);\n}\n\nAnytoneAudioSettingsExtension::VoxSource\nAnytoneAudioSettingsExtension::voxSource() const {\n  return _voxSource;\n}\nvoid\nAnytoneAudioSettingsExtension::setVOXSource(VoxSource source) {\n  if (_voxSource == source)\n    return;\n  _voxSource = source;\n  emit modified(this);\n}\n\nbool\nAnytoneAudioSettingsExtension::enhanceAudioEnabled() const {\n  return _enhanceAudio;\n}\nvoid\nAnytoneAudioSettingsExtension::enableEnhanceAudio(bool enable) {\n  if (_enhanceAudio == enable)\n    return;\n  _enhanceAudio = enable;\n  emit modified(this);\n}\n\nInterval\nAnytoneAudioSettingsExtension::muteDelay() const {\n  return _muteDelay;\n}\nvoid\nAnytoneAudioSettingsExtension::setMuteDelay(Interval intv) {\n  if (_muteDelay == intv)\n    return;\n  _muteDelay = intv;\n  emit modified(this);\n}\n\n\nAnytoneAudioSettingsExtension::Speaker\nAnytoneAudioSettingsExtension::speaker() const {\n  return _speaker;\n}\n\nvoid\nAnytoneAudioSettingsExtension::setSpeaker(Speaker speaker) {\n  if (_speaker == speaker)\n    return;\n  _speaker = speaker;\n  emit modified(this);\n}\n\n\nAnytoneAudioSettingsExtension::HandsetSpeakerSource\nAnytoneAudioSettingsExtension::handsetSpeaker() const {\n  return _handsetSpeaker;\n}\n\nvoid\nAnytoneAudioSettingsExtension::setHandsetSpeaker(HandsetSpeakerSource src) {\n  if (_handsetSpeaker == src)\n    return;\n  _handsetSpeaker = src;\n  emit modified(this);\n}\n\n\nAnytoneAudioSettingsExtension::HandsetType\nAnytoneAudioSettingsExtension::handsetType() const {\n  return _handsetType;\n}\n\nvoid\nAnytoneAudioSettingsExtension::setHandsetType(HandsetType type) {\n  if (_handsetType == type)\n    return;\n  _handsetType = type;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneMenuSettingsExtension\n * ********************************************************************************************* */\nAnytoneMenuSettingsExtension::AnytoneMenuSettingsExtension(QObject *parent)\n  : ConfigItem(parent),  _menuDuration(Interval::fromSeconds(15)), _showSeparator(false)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneMenuSettingsExtension::clone() const {\n  AnytoneMenuSettingsExtension *ext = new AnytoneMenuSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nInterval\nAnytoneMenuSettingsExtension::duration() const {\n  return _menuDuration;\n}\nvoid\nAnytoneMenuSettingsExtension::setDuration(Interval sec) {\n  if (_menuDuration == sec)\n    return;\n  _menuDuration = sec;\n  emit modified(this);\n}\n\nbool\nAnytoneMenuSettingsExtension::separatorEnabled() const {\n  return _showSeparator;\n}\nvoid\nAnytoneMenuSettingsExtension::enableSeparator(bool enable) {\n  if (_showSeparator == enable)\n    return;\n  _showSeparator = enable;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAutoRepeaterSettingsExtension\n * ********************************************************************************************* */\nAnytoneAutoRepeaterSettingsExtension::AnytoneAutoRepeaterSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _directionA(Direction::Off), _directionB(Direction::Off),\n    _minVHF(Frequency::fromMHz(136)), _maxVHF(Frequency::fromMHz(174)),\n    _minUHF(Frequency::fromMHz(400)), _maxUHF(Frequency::fromMHz(480)),\n    _vhfOffset(new AnytoneAutoRepeaterOffsetRef(this)),\n    _uhfOffset(new AnytoneAutoRepeaterOffsetRef(this)),\n    _minVHF2(Frequency::fromMHz(136)), _maxVHF2(Frequency::fromMHz(174)),\n    _minUHF2(Frequency::fromMHz(400)), _maxUHF2(Frequency::fromMHz(480)),\n    _vhf2Offset(new AnytoneAutoRepeaterOffsetRef(this)),\n    _uhf2Offset(new AnytoneAutoRepeaterOffsetRef(this)),\n    _offsets(new AnytoneAutoRepeaterOffsetList(this))\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneAutoRepeaterSettingsExtension::clone() const {\n  AnytoneAutoRepeaterSettingsExtension *ext = new AnytoneAutoRepeaterSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nAnytoneAutoRepeaterSettingsExtension::directionA() const {\n  return _directionA;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setDirectionA(Direction dir) {\n  if (_directionA == dir)\n    return;\n  _directionA = dir;\n  emit modified(this);\n}\nAnytoneAutoRepeaterSettingsExtension::Direction\nAnytoneAutoRepeaterSettingsExtension::directionB() const {\n  return _directionB;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setDirectionB(Direction dir) {\n  if (_directionB == dir)\n    return;\n  _directionB = dir;\n  emit modified(this);\n}\n\nFrequency\nAnytoneAutoRepeaterSettingsExtension::vhfMin() const {\n  return _minVHF;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setVHFMin(Frequency Hz) {\n  if (_minVHF == Hz)\n    return;\n  _minVHF = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::vhfMax() const {\n  return _maxVHF;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setVHFMax(Frequency Hz) {\n  if (_maxVHF == Hz)\n    return;\n  _maxVHF = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::uhfMin() const {\n  return _minUHF;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setUHFMin(Frequency Hz) {\n  if (_minUHF == Hz)\n    return;\n  _minUHF = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::uhfMax() const {\n  return _maxUHF;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setUHFMax(Frequency Hz) {\n  if (_maxUHF == Hz)\n    return;\n  _maxUHF = Hz;\n  emit modified(this);\n}\n\nAnytoneAutoRepeaterOffsetRef *\nAnytoneAutoRepeaterSettingsExtension::vhfRef() const {\n  return _vhfOffset;\n}\n\nAnytoneAutoRepeaterOffsetRef *\nAnytoneAutoRepeaterSettingsExtension::uhfRef() const {\n  return _uhfOffset;\n}\n\nFrequency\nAnytoneAutoRepeaterSettingsExtension::vhf2Min() const {\n  return _minVHF2;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setVHF2Min(Frequency Hz) {\n  if (_minVHF2 == Hz)\n    return;\n  _minVHF2 = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::vhf2Max() const {\n  return _maxVHF2;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setVHF2Max(Frequency Hz) {\n  if (_maxVHF2 == Hz)\n    return;\n  _maxVHF2 = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::uhf2Min() const {\n  return _minUHF2;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setUHF2Min(Frequency Hz) {\n  if (_minUHF2 == Hz)\n    return;\n  _minUHF2 = Hz;\n  emit modified(this);\n}\nFrequency\nAnytoneAutoRepeaterSettingsExtension::uhf2Max() const {\n  return _maxUHF2;\n}\nvoid\nAnytoneAutoRepeaterSettingsExtension::setUHF2Max(Frequency Hz) {\n  if (_maxUHF2 == Hz)\n    return;\n  _maxUHF2 = Hz;\n  emit modified(this);\n}\n\nAnytoneAutoRepeaterOffsetRef *\nAnytoneAutoRepeaterSettingsExtension::vhf2Ref() const {\n  return _vhf2Offset;\n}\n\nAnytoneAutoRepeaterOffsetRef *\nAnytoneAutoRepeaterSettingsExtension::uhf2Ref() const {\n  return _uhf2Offset;\n}\n\nAnytoneAutoRepeaterOffsetList *\nAnytoneAutoRepeaterSettingsExtension::offsets() const {\n  return _offsets;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAutoRepeaterOffset\n * ********************************************************************************************* */\nAnytoneAutoRepeaterOffset::AnytoneAutoRepeaterOffset(QObject *parent)\n  : ConfigObject(parent), _offset()\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneAutoRepeaterOffset::clone() const {\n  AnytoneAutoRepeaterOffset *off = new AnytoneAutoRepeaterOffset();\n  if (! off->copy(*this)) {\n    off->deleteLater();\n    return nullptr;\n  }\n  return off;\n}\n\nFrequency\nAnytoneAutoRepeaterOffset::offset() const {\n  return _offset;\n}\nvoid\nAnytoneAutoRepeaterOffset::setOffset(Frequency offset) {\n  if (_offset == offset)\n    return;\n  _offset = offset;\n  emit modified(this);\n}\n\nConfigItem *\nAnytoneAutoRepeaterOffsetList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err);\n  return new AnytoneAutoRepeaterOffset();\n}\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAutoRepeaterOffsetRef\n * ********************************************************************************************* */\nAnytoneAutoRepeaterOffsetRef::AnytoneAutoRepeaterOffsetRef(QObject *parent)\n  : ConfigObjectReference(AnytoneAutoRepeaterOffset::staticMetaObject, parent)\n{\n  // pass...\n}\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneAutoRepeaterOffsetList\n * ********************************************************************************************* */\nAnytoneAutoRepeaterOffsetList::AnytoneAutoRepeaterOffsetList(QObject *parent)\n  : ConfigObjectList(AnytoneAutoRepeaterOffset::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneBluetoothHandsetSettingsExtension\n * ********************************************************************************************* */\nAnytoneBluetoothHandsetSettingsExtension::AnytoneBluetoothHandsetSettingsExtension(QObject *parent)\n  : ConfigItem{parent}, _shutdown(true), _volumeLevelA(0), _volumeLevelB(0), _micGain(1),\n  _squelch(1), _txNoiseReduction(0), _voxLevel(0), _voxDelay(Interval::null()),\n  _funcKeyAShort(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyBShort(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyCShort(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyALong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyBLong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyCLong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyAVeryLong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyBVeryLong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _funcKeyCVeryLong(AnytoneKeySettingsExtension::KeyFunction::Off),\n  _backlight(Interval::fromSeconds(30))\n{\n  // pass...\n}\n\n\nConfigItem *\nAnytoneBluetoothHandsetSettingsExtension::clone() const {\n  auto obj = new AnytoneBluetoothHandsetSettingsExtension();\n  if (! obj->copy(*this)) {\n    delete obj;\n    return nullptr;\n  }\n  return obj;\n}\n\n\nbool\nAnytoneBluetoothHandsetSettingsExtension::shutdownEnabled() const {\n  return _shutdown;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::enableShutdown(bool enable) {\n  if (_shutdown == enable)\n    return;\n  _shutdown = enable;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::volumeLevelA() const {\n  return _volumeLevelA;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setVolumeLevelA(unsigned int level) {\n  level = std::min(10U, level);\n  if (_volumeLevelA == level)\n    return;\n  _volumeLevelA = level;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::volumeLevelB() const {\n  return _volumeLevelB;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setVolumeLevelB(unsigned int level) {\n  level = std::min(10U, level);\n  if (_volumeLevelB == level)\n    return;\n  _volumeLevelB = level;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::micGain() const {\n  return _micGain;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setMicGain(unsigned int gain) {\n  gain = std::max(1u, std::min(10U, gain));\n  if (_micGain == gain)\n    return;\n  _micGain = gain;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::squelch() const {\n  return _squelch;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setSquelch(unsigned int level) {\n  level = std::min(10U, level);\n  if (_squelch == level)\n    return;\n  _squelch = level;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::txNoiseReduction() const {\n  return _txNoiseReduction;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setTxNoiseReduction(unsigned int level) {\n  level = std::max(10U, level);\n  if (_txNoiseReduction == level)\n    return;\n  _txNoiseReduction = level;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneBluetoothHandsetSettingsExtension::voxLevel() const {\n  return _voxLevel;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setVoxLevel(unsigned int level) {\n  level = std::min(10U, level);\n  if (_voxLevel == level)\n    return;\n  _voxLevel = level;\n  emit modified(this);\n}\n\n\nInterval\nAnytoneBluetoothHandsetSettingsExtension::voxDelay() const {\n  return _voxDelay;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setVoxDelay(Interval delay) {\n  if (_voxDelay == delay)\n    return;\n  _voxDelay = delay;\n  emit modified(this);\n}\n\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyAShort() const {\n  return _funcKeyAShort;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyAShort == func)\n    return;\n  _funcKeyAShort = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyBShort() const {\n  return _funcKeyBShort;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyBShort == func)\n    return;\n  _funcKeyBShort = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyCShort() const {\n  return _funcKeyCShort;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyCShort == func)\n    return;\n  _funcKeyCShort = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyALong() const {\n  return _funcKeyALong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyALong == func)\n    return;\n  _funcKeyALong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyBLong() const {\n  return _funcKeyBLong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyBLong == func)\n    return;\n  _funcKeyBLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyCLong() const {\n  return _funcKeyCLong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyCLong == func)\n    return;\n  _funcKeyCLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyAVeryLong() const {\n  return _funcKeyAVeryLong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyAVeryLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyAVeryLong == func)\n    return;\n  _funcKeyAVeryLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyBVeryLong() const {\n  return _funcKeyBVeryLong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyBVeryLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyBVeryLong == func)\n    return;\n  _funcKeyBVeryLong = func;\n  emit modified(this);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nAnytoneBluetoothHandsetSettingsExtension::funcKeyCVeryLong() const {\n  return _funcKeyCVeryLong;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setFuncKeyCVeryLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  if (_funcKeyCVeryLong == func)\n    return;\n  _funcKeyCVeryLong = func;\n  emit modified(this);\n}\n\nInterval\nAnytoneBluetoothHandsetSettingsExtension::backlight() const {\n  return _backlight;\n}\n\nvoid\nAnytoneBluetoothHandsetSettingsExtension::setBacklight(Interval dur) {\n  if (_backlight == dur)\n    return;\n  _backlight = dur;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneBluetoothSettingsExtension\n * ********************************************************************************************* */\nAnytoneBluetoothSettingsExtension::AnytoneBluetoothSettingsExtension(QObject *parent)\n  : ConfigItem(parent),\n  _handset(new AnytoneBluetoothHandsetSettingsExtension(this)),\n  _bluetoothEnabled(false), _pttLatch(false), _pttSleep(Interval::fromMilliseconds(0)),\n  _internalMic(false), _internalSpeaker(false), _micGain(0), _speakerGain(0), _holdDuration(),\n  _holdDelay()\n{\n  connect(_handset, &AnytoneBluetoothHandsetSettingsExtension::modified,\n          this, &AnytoneBluetoothSettingsExtension::modified);\n}\n\nConfigItem *\nAnytoneBluetoothSettingsExtension::clone() const {\n  AnytoneBluetoothSettingsExtension *ext = new AnytoneBluetoothSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nAnytoneBluetoothSettingsExtension::pttLatch() const {\n  return _pttLatch;\n}\nvoid\nAnytoneBluetoothSettingsExtension::enablePTTLatch(bool enable) {\n  if (enable == _pttLatch)\n    return;\n  _pttLatch = enable;\n  emit modified(this);\n}\n\nAnytoneBluetoothHandsetSettingsExtension *\nAnytoneBluetoothSettingsExtension::handset() const {\n  return _handset;\n}\n\nbool\nAnytoneBluetoothSettingsExtension::bluetoothEnabled() const {\n  return _bluetoothEnabled;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::enableBluetooth(bool enable) {\n  if (enable == _bluetoothEnabled)\n    return;\n  _bluetoothEnabled = enable;\n  emit modified(this);\n}\n\nInterval\nAnytoneBluetoothSettingsExtension::pttSleepTimer() const {\n  return _pttSleep;\n}\nvoid\nAnytoneBluetoothSettingsExtension::setPTTSleepTimer(Interval intv) {\n  if (intv == _pttSleep)\n    return;\n  _pttSleep = intv;\n  emit modified(this);\n}\n\nbool\nAnytoneBluetoothSettingsExtension::internalMicEnabled() const {\n  return _internalMic;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::enableInternalMic(bool enable) {\n  if (enable == _internalMic)\n    return;\n  _internalMic = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneBluetoothSettingsExtension::internalSpeakerEnabled() const {\n  return _internalSpeaker;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::enableInternalSpeaker(bool enable) {\n  if (enable == _internalSpeaker)\n    return;\n  _internalSpeaker = enable;\n  emit modified(this);\n}\n\nunsigned int\nAnytoneBluetoothSettingsExtension::micGain() const {\n  return _micGain;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::setMicGain(unsigned int gain) {\n  if (_micGain == gain)\n    return;\n  _micGain = gain;\n  emit modified(this);\n}\n\nunsigned int\nAnytoneBluetoothSettingsExtension::speakerGain() const {\n  return _speakerGain;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::setSpeakerGain(unsigned int gain) {\n  if (_speakerGain == gain)\n    return;\n  _speakerGain = gain;\n  emit modified(this);\n}\n\nconst Interval &\nAnytoneBluetoothSettingsExtension::holdDuration() const {\n  return _holdDuration;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::setHoldDuration(const Interval &dur) {\n  if (dur == _holdDuration)\n    return;\n  _holdDuration = dur;\n  emit modified(this);\n}\n\nconst Interval &\nAnytoneBluetoothSettingsExtension::holdDelay() const {\n  return _holdDelay;\n}\n\nvoid\nAnytoneBluetoothSettingsExtension::setHoldDelay(const Interval &dur) {\n  if (dur == _holdDelay)\n    return;\n  _holdDelay = dur;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSimplexRepeaterSettingsExtension\n * ********************************************************************************************* */\nAnytoneRepeaterSettingsExtension::AnytoneRepeaterSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _enabled(false), _monitor(false), _timeSlot(TimeSlot::Channel),\n  _secTimeSlot(TimeSlot::Channel), _colorCode(ColorCode::Ignored)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneRepeaterSettingsExtension::clone() const {\n  AnytoneRepeaterSettingsExtension *ext = new AnytoneRepeaterSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nAnytoneRepeaterSettingsExtension::enabled() const {\n  return _enabled;\n}\nvoid\nAnytoneRepeaterSettingsExtension::enable(bool enable) {\n  if (_enabled == enable)\n    return;\n  _enabled = enable;\n  emit modified(this);\n}\n\nbool\nAnytoneRepeaterSettingsExtension::monitorEnabled() const {\n  return _monitor;\n}\nvoid\nAnytoneRepeaterSettingsExtension::enableMonitor(bool enable) {\n  if (_monitor == enable)\n    return;\n  _monitor = enable;\n  emit modified(this);\n}\n\nAnytoneRepeaterSettingsExtension::TimeSlot\nAnytoneRepeaterSettingsExtension::timeSlot() const {\n  return _timeSlot;\n}\nvoid\nAnytoneRepeaterSettingsExtension::setTimeSlot(TimeSlot ts) {\n  if (_timeSlot == ts)\n    return;\n  _timeSlot = ts;\n  emit modified(this);\n}\n\n\nAnytoneRepeaterSettingsExtension::TimeSlot\nAnytoneRepeaterSettingsExtension::secTimeSlot() const {\n  return _secTimeSlot;\n}\nvoid\nAnytoneRepeaterSettingsExtension::setSecTimeSlot(TimeSlot ts) {\n  if (_secTimeSlot == ts)\n    return;\n  _secTimeSlot = ts;\n  emit modified(this);\n}\n\n\nAnytoneRepeaterSettingsExtension::ColorCode\nAnytoneRepeaterSettingsExtension::colorCode() const {\n  return _colorCode;\n}\n\nvoid\nAnytoneRepeaterSettingsExtension::setColorCode(ColorCode code) {\n  if (_colorCode == code)\n    return;\n  _colorCode = code;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AnytoneSatelliteSettingsExtension\n * ********************************************************************************************* */\nAnytoneSatelliteSettingsExtension::AnytoneSatelliteSettingsExtension(QObject *parent)\n  : ConfigItem{parent}, _power(Channel::Power::High), _squelch(0)\n{\n  // pass...\n}\n\nConfigItem *\nAnytoneSatelliteSettingsExtension::clone() const {\n  auto ext = new AnytoneSatelliteSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nChannel::Power\nAnytoneSatelliteSettingsExtension::power() const {\n  return _power;\n}\n\nvoid\nAnytoneSatelliteSettingsExtension::setPower(Channel::Power power) {\n  if (_power == power)\n    return;\n  _power = power;\n  emit modified(this);\n}\n\n\nunsigned int\nAnytoneSatelliteSettingsExtension::squelch() const {\n  return _squelch;\n}\n\nvoid\nAnytoneSatelliteSettingsExtension::setSquelch(unsigned int squelch) {\n  if (_squelch == squelch)\n    return;\n  _squelch = squelch;\n  emit modified(this);\n}\n\n"
  },
  {
    "path": "lib/anytone_settingsextension.hh",
    "content": "#ifndef ANYTONE_SETTINGSEXTENSION_H\n#define ANYTONE_SETTINGSEXTENSION_H\n\n#include <QTimeZone>\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"interval.hh\"\n#include \"melody.hh\"\n#include \"frequency.hh\"\n#include \"channel.hh\"\n\n\n\n/** Implements the boot settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneBootSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The priority zone for VFO A. */\n  Q_PROPERTY(ZoneReference* priorityZoneA READ priorityZoneA)\n  /** The priority zone for VFO B. */\n  Q_PROPERTY(ZoneReference* priorityZoneB READ priorityZoneB)\n\n  /** Enables the GPS check. */\n  Q_PROPERTY(bool gpsCheck READ gpsCheckEnabled WRITE enableGPSCheck)\n\npublic:\n  /** Constructor. */\n  explicit AnytoneBootSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns a reference to the priority zone for VFO A. */\n  ZoneReference *priorityZoneA() const;\n  /** Returns a reference to the priority zone for VFO B. */\n  ZoneReference *priorityZoneB() const;\n\n  /** Returns @c true if the GPS check is enabled. */\n  bool gpsCheckEnabled() const;\n  /** Enables/disables the GPS check. */\n  void enableGPSCheck(bool enable);\n\nprotected:\n  ZoneReference *_priorityZoneA;   ///< Priority zone for VFO A.\n  ZoneReference *_priorityZoneB;   ///< Priority zone for VFO B.\n  bool _gpsCheck;                  ///< Enables GPS check.\n};\n\n\n/** Implements the power-save settings for AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytonePowerSaveSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"autoShutDownDelayDescription\", \"The auto shut-down delay in minutes.\")\n  /** The auto shut-down delay in minutes. */\n  Q_PROPERTY(Interval autoShutdown READ autoShutdown WRITE setAutoShutdown)\n\n  /** Resets the auto shut-down timer on every call. */\n  Q_PROPERTY(bool resetAutoShutdownOnCall READ resetAutoShutdownOnCall WRITE enableResetAutoShutdownOnCall)\n\n  Q_CLASSINFO(\"powerSaveDescription\", \"Specifies the power save mode. \"\n                                      \"D686UV, D878UV(2) and DMR-6X2UV only.\")\n  /** The power-save mode. */\n  Q_PROPERTY(PowerSave powerSave READ powerSave WRITE setPowerSave)\n\n  /** If @c true, the adaptive transmission power control is enabled. */\n  Q_PROPERTY(bool atpc READ atpc WRITE enableATPC)\n\npublic:\n  /** Possible power save modes. */\n  enum class PowerSave {\n    Off = 0, Save50 = 1, Save66 = 2\n  };\n  Q_ENUM(PowerSave)\n\npublic:\n  /** Default constructor. */\n  explicit AnytonePowerSaveSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the auto shut-down delay in minutes. */\n  Interval autoShutdown() const;\n  /** Sets the auto shut-down delay. */\n  void setAutoShutdown(Interval min);\n\n  /** Returns @c true, if the auto shut-down timer is reset on every call. */\n  bool resetAutoShutdownOnCall() const;\n  /** Enables/disables reset of auto shut-down timer on every call. */\n  void enableResetAutoShutdownOnCall(bool enable);\n\n  /** Returns the power-save mode. */\n  PowerSave powerSave() const;\n  /** Sets the power-save mode. */\n  void setPowerSave(PowerSave mode);\n\n  /** Returns @c true if the adaptive transmission power control is enabled. */\n  bool atpc() const;\n  /** Enables/disables the adaptive transmission power control. */\n  void enableATPC(bool enable);\n\nprotected:\n  Interval _autoShutDownDelay;     ///< The auto shut-down delay in minutes.\n  bool _resetAutoShutdownOnCall;   ///< Enables reset of auto shut-down timer on every call.\n  PowerSave _powerSave;            ///< Power save mode property.\n  bool _atpc;                      ///< Adaptive Transmission Power Control.\n};\n\n\n/** Implements the key settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneKeySettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** Function key 1 (P1 below or next to screen), short press function. */\n  Q_PROPERTY(KeyFunction funcKey1Short READ funcKey1Short WRITE setFuncKey1Short)\n  /** Function key 1 (P1 below or next to screen), long press function. */\n  Q_PROPERTY(KeyFunction funcKey1Long READ funcKey1Long WRITE setFuncKey1Long)\n  /** Function key 2 (P2 below or next to screen), short press function. */\n  Q_PROPERTY(KeyFunction funcKey2Short READ funcKey2Short WRITE setFuncKey2Short)\n  /** Function key 2 (P2 below or next to screen), long press function. */\n  Q_PROPERTY(KeyFunction funcKey2Long READ funcKey2Long WRITE setFuncKey2Long)\n  /** Function key 3 (P3 next to screen, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKey3Short READ funcKey3Short WRITE setFuncKey3Short)\n  /** Function key 3 (P3 next to screen, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKey3Long READ funcKey3Long WRITE setFuncKey3Long)\n  /** Function key 4 (P4 next to screen, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKey4Short READ funcKey4Short WRITE setFuncKey4Short)\n  /** Function key 4 (P4 next to screen, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKey4Long READ funcKey4Long WRITE setFuncKey4Long)\n  /** Function key 5 (P5 next to screen, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKey5Short READ funcKey5Short WRITE setFuncKey5Short)\n  /** Function key 5 (P5 next to screen, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKey5Long READ funcKey5Long WRITE setFuncKey5Long)\n  /** Function key 6 (P6 next to screen, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKey6Short READ funcKey6Short WRITE setFuncKey6Short)\n  /** Function key 6 (P6 next to screen, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKey6Long READ funcKey6Long WRITE setFuncKey6Long)\n  /** Function key A (PF1 below PTT or A on mic), short press function. */\n  Q_PROPERTY(KeyFunction funcKeyAShort READ funcKeyAShort WRITE setFuncKeyAShort)\n  /** Function key A (PF1 below PTT or A on mic), long press function. */\n  Q_PROPERTY(KeyFunction funcKeyALong READ funcKeyALong WRITE setFuncKeyALong)\n  /** Function key B (PF2 second below PTT or B on mic), short press function. */\n  Q_PROPERTY(KeyFunction funcKeyBShort READ funcKeyBShort WRITE setFuncKeyBShort)\n  /** Function key B (PF2 second below PTT or B on mic), long press function. */\n  Q_PROPERTY(KeyFunction funcKeyBLong READ funcKeyBLong WRITE setFuncKeyBLong)\n  /** Function key C (PF3 on top or C on mic), short press function. */\n  Q_PROPERTY(KeyFunction funcKeyCShort READ funcKeyCShort WRITE setFuncKeyCShort)\n  /** Function key C (PF3 on top or B on mic), long press function. */\n  Q_PROPERTY(KeyFunction funcKeyCLong READ funcKeyCLong WRITE setFuncKeyCLong)\n  /** Function key D (D on mic, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKeyDShort READ funcKeyDShort WRITE setFuncKeyDShort)\n  /** Function key D (D on mic, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKeyDLong READ funcKeyDLong WRITE setFuncKeyDLong)\n  /** Function knob (channel/frequency knob, D578UV only), short press function. */\n  Q_PROPERTY(KeyFunction funcKnobShort READ funcKnobShort WRITE setFuncKnobShort)\n  /** Function knob (channel/frequency knob, D578UV only), long press function. */\n  Q_PROPERTY(KeyFunction funcKnobLong READ funcKnobLong WRITE setFuncKnobLong)\n  /** The long press duration in ms. */\n  Q_PROPERTY(Interval longPressDuration READ longPressDuration WRITE setLongPressDuration)\n\n  /** Up/down key functions (D578UV only). */\n  Q_PROPERTY(UpDownKeyFunction upDownKeys READ upDownKeyFunction WRITE setUpDownKeyFunction);\n\n  /** The auto key-lock property. */\n  Q_PROPERTY(bool autoKeyLock READ autoKeyLockEnabled WRITE enableAutoKeyLock)\n\n  Q_CLASSINFO(\"knobLockDescription\", \"If enabled, the knob gets locked too.\")\n  /** If @c true, the knob gets locked too. */\n  Q_PROPERTY(bool knobLock READ knobLockEnabled WRITE enableKnobLock)\n\n  Q_CLASSINFO(\"keypadLockDescription\", \"If enabled, the key-pad gets locked.\")\n  /** If @c true, the key-pad gets locked too. */\n  Q_PROPERTY(bool keypadLock READ keypadLockEnabled WRITE enableKeypadLock)\n\n  Q_CLASSINFO(\"sideKeysLockDescription\", \"If enabled, the side-keys get locked.\")\n  /** If @c true, the side-keys get locked too. */\n  Q_PROPERTY(bool sideKeysLock READ sideKeysLockEnabled WRITE enableSideKeysLock)\n\n  Q_CLASSINFO(\"forcedKeyLockDescription\", \"If enabled, the key-lock is forced.\")\n  /** If @c true, the key-lock is forced. */\n  Q_PROPERTY(bool forcedKeyLock READ forcedKeyLockEnabled WRITE enableForcedKeyLock)\n\npublic:\n  /** All possible key functions. */\n  enum class KeyFunction {\n    Off, Voltage, Power, Repeater, Reverse, Encryption, Call, VOX, ToggleVFO, SubPTT,\n    Scan, WFM, Alarm, RecordSwitch, Record, SMS, Dial, GPSInformation, Monitor, ToggleMainChannel,\n    HotKey1, HotKey2, HotKey3, HotKey4, HotKey5, HotKey6, WorkAlone, SkipChannel, DMRMonitor,\n    SubChannel, PriorityZone, VFOScan, MICSoundQuality, LastCallReply, ChannelType, Ranging,\n    Roaming, ChannelRanging, MaxVolume, Slot, APRSTypeSwitch, Zone, ZoneUp, ZoneDown, Exit, Menu,\n    RoamingSet, APRSSet, Mute, MuteA, MuteB, CtcssDcsSet, TBSTSend, Bluetooth, GPS, ChannelName,\n    CDTScan, APRSSend, APRSInfo, Speaker, XBandRepeater, SimplexRepeater, GPSRoaming, Squelch,\n    NoiseReductionTX, DIMShut, SatPredict\n  };\n  Q_ENUM(KeyFunction)\n\n  /** Possible up/down key functions. */\n  enum class UpDownKeyFunction {\n    Channel, Volume\n  };\n  Q_ENUM(UpDownKeyFunction)\n\npublic:\n  /** Empty constructor. */\n  explicit AnytoneKeySettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the key function for a short press on the function key 1. */\n  KeyFunction funcKey1Short() const;\n  /** Sets the key function for a short press on the function key 1. */\n  void setFuncKey1Short(KeyFunction func);\n  /** Returns the key function for a long press on the function key 1. */\n  KeyFunction funcKey1Long() const;\n  /** Sets the key function for a long press on the function key 1. */\n  void setFuncKey1Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the programmable function key 2. */\n  KeyFunction funcKey2Short() const;\n  /** Sets the key function for a short press on the programmable function key 2. */\n  void setFuncKey2Short(KeyFunction func);\n  /** Returns the key function for a long press on the programmable function key 2. */\n  KeyFunction funcKey2Long() const;\n  /** Sets the key function for a long press on the programmable function key 2. */\n  void setFuncKey2Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the programmable function key 3. */\n  KeyFunction funcKey3Short() const;\n  /** Sets the key function for a short press on the programmable function key 3. */\n  void setFuncKey3Short(KeyFunction func);\n  /** Returns the key function for a long press on the programmable function key 3. */\n  KeyFunction funcKey3Long() const;\n  /** Sets the key function for a long press on the programmable function key 3. */\n  void setFuncKey3Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the programmable function key 4. */\n  KeyFunction funcKey4Short() const;\n  /** Sets the key function for a short press on the programmable function key 4. */\n  void setFuncKey4Short(KeyFunction func);\n  /** Returns the key function for a long press on the programmable function key 4. */\n  KeyFunction funcKey4Long() const;\n  /** Sets the key function for a long press on the programmable function key 4. */\n  void setFuncKey4Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the programmable function key 5. */\n  KeyFunction funcKey5Short() const;\n  /** Sets the key function for a short press on the programmable function key 5. */\n  void setFuncKey5Short(KeyFunction func);\n  /** Returns the key function for a long press on the programmable function key 5. */\n  KeyFunction funcKey5Long() const;\n  /** Sets the key function for a long press on the programmable function key 5. */\n  void setFuncKey5Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the programmable function key 6. */\n  KeyFunction funcKey6Short() const;\n  /** Sets the key function for a short press on the programmable function key 6. */\n  void setFuncKey6Short(KeyFunction func);\n  /** Returns the key function for a long press on the programmable function key 6. */\n  KeyFunction funcKey6Long() const;\n  /** Sets the key function for a long press on the programmable function key 6. */\n  void setFuncKey6Long(KeyFunction func);\n\n  /** Returns the key function for a short press on the function key A. */\n  KeyFunction funcKeyAShort() const;\n  /** Sets the key function for a short press on the function key A. */\n  void setFuncKeyAShort(KeyFunction func);\n  /** Returns the key function for a long press on the function key A. */\n  KeyFunction funcKeyALong() const;\n  /** Sets the key function for a long press on the function key A. */\n  void setFuncKeyALong(KeyFunction func);\n\n  /** Returns the key function for a short press on the function key B. */\n  KeyFunction funcKeyBShort() const;\n  /** Sets the key function for a short press on the function key B. */\n  void setFuncKeyBShort(KeyFunction func);\n  /** Returns the key function for a long press on the function key B. */\n  KeyFunction funcKeyBLong() const;\n  /** Sets the key function for a long press on the function key B. */\n  void setFuncKeyBLong(KeyFunction func);\n\n  /** Returns the key function for a short press on the function key C. */\n  KeyFunction funcKeyCShort() const;\n  /** Sets the key function for a short press on the function key C. */\n  void setFuncKeyCShort(KeyFunction func);\n  /** Returns the key function for a long press on the function key C. */\n  KeyFunction funcKeyCLong() const;\n  /** Sets the key function for a long press on the function key C. */\n  void setFuncKeyCLong(KeyFunction func);\n\n  /** Returns the key function for a short press on the function key D. */\n  KeyFunction funcKeyDShort() const;\n  /** Sets the key function for a short press on the function key D. */\n  void setFuncKeyDShort(KeyFunction func);\n  /** Returns the key function for a long press on the function key D. */\n  KeyFunction funcKeyDLong() const;\n  /** Sets the key function for a long press on the function key D. */\n  void setFuncKeyDLong(KeyFunction func);\n\n  /** Returns the key function for a short press on the knob. */\n  KeyFunction funcKnobShort() const;\n  /** Sets the key function for a short press on the knob. */\n  void setFuncKnobShort(KeyFunction func);\n  /** Returns the key function for a long press on the knob. */\n  KeyFunction funcKnobLong() const;\n  /** Sets the key function for a long press on the knob. */\n  void setFuncKnobLong(KeyFunction func);\n\n  /** Returns the long-press duration in ms. */\n  Interval longPressDuration() const;\n  /** Sets the long-press duration in ms. */\n  void setLongPressDuration(Interval ms);\n\n  /** Returns the up/down key function. */\n  UpDownKeyFunction upDownKeyFunction() const;\n  /** Sets the up/down key function. */\n  void setUpDownKeyFunction(UpDownKeyFunction func);\n\n  /** Returns @c true, if the automatic key-lock feature is enabled. */\n  bool autoKeyLockEnabled() const;\n  /** Enables/disables auto key-lock. */\n  void enableAutoKeyLock(bool enabled);\n\n  /** Returns @c true if the knob gets locked too. */\n  bool knobLockEnabled() const;\n  /** Enables/disables the knob lock. */\n  void enableKnobLock(bool enable);\n  /** Returns @c true if the key-pad gets locked too. */\n  bool keypadLockEnabled() const;\n  /** Enables/disables the key-pad lock. */\n  void enableKeypadLock(bool enable);\n  /** Returns @c true if the side-keys gets locked too. */\n  bool sideKeysLockEnabled() const;\n  /** Enables/disables the side-keys lock. */\n  void enableSideKeysLock(bool enable);\n  /** Returns @c true if the key-lock is forced. */\n  bool forcedKeyLockEnabled() const;\n  /** Enables/disables the forced key-lock. */\n  void enableForcedKeyLock(bool enable);\n\nprotected:\n  KeyFunction _funcKey1Short;          ///< Function of the function key 1, short press.\n  KeyFunction _funcKey1Long;           ///< Function of the function key 1, long press.\n  KeyFunction _funcKey2Short;          ///< Function of the function key 2, short press.\n  KeyFunction _funcKey2Long;           ///< Function of the function key 2, long press.\n  KeyFunction _funcKey3Short;          ///< Function of the function key 3, short press.\n  KeyFunction _funcKey3Long;           ///< Function of the function key 3, long press.\n  KeyFunction _funcKey4Short;          ///< Function of the function key 4, short press.\n  KeyFunction _funcKey4Long;           ///< Function of the function key 4, long press.\n  KeyFunction _funcKey5Short;          ///< Function of the function key 5, short press.\n  KeyFunction _funcKey5Long;           ///< Function of the function key 5, long press.\n  KeyFunction _funcKey6Short;          ///< Function of the function key 6, short press.\n  KeyFunction _funcKey6Long;           ///< Function of the function key 6, long press.\n  KeyFunction _funcKeyAShort;          ///< Function of the function key A, short press.\n  KeyFunction _funcKeyALong;           ///< Function of the function key A, long press.\n  KeyFunction _funcKeyBShort;          ///< Function of the function key B, short press.\n  KeyFunction _funcKeyBLong;           ///< Function of the function key B, long press.\n  KeyFunction _funcKeyCShort;          ///< Function of the function key C, short press.\n  KeyFunction _funcKeyCLong;           ///< Function of the function key C, long press.\n  KeyFunction _funcKeyDShort;          ///< Function of the function key D, short press.\n  KeyFunction _funcKeyDLong;           ///< Function of the function key D, long press.\n  KeyFunction _funcKnobShort;          ///< Function of the knob, short press.\n  KeyFunction _funcKnobLong;           ///< Function of the knob, long press.\n  Interval _longPressDuration;         ///< The long-press duration in ms.\n  UpDownKeyFunction _upDownFunction;   ///< The up/down key function.\n  bool _autoKeyLock;                   ///< Auto key-lock property.\n  bool _knobLock;                      ///< Knob locked too.\n  bool _keypadLock;                    ///< Key-pad is locked.\n  bool _sideKeysLock;                  ///< Side-keys are locked.\n  bool _forcedKeyLock;                 ///< Forced key-lock.\n};\n\n\n/** Implements the tone settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneToneSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"description\", \"Tone settings for AnyTone devices.\")\n\n  /** Enables transmit timeout notification (5s before TOT). */\n  Q_PROPERTY(bool tot READ totNotification WRITE enableTOTNotification)\n  /** Enables weather alarm tone. */\n  Q_PROPERTY(bool wxAlarm READ wxAlarm WRITE enableWXAlarm)\n\n\npublic:\n  /** Empty constructor. */\n  explicit AnytoneToneSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the transmit timeout notification is enabled (5s before TOT). */\n  bool totNotification() const;\n  /** Enables/disables the transmit timeout notification (5s before TOT). */\n  void enableTOTNotification(bool enable);\n\n  /** Returns @c true, if the weather alarm is enabled. */\n  bool wxAlarm() const;\n  /** Enables/disables weather alarm. */\n  void enableWXAlarm(bool enable);\n\nprotected:\n  bool _totNotification;           ///< TOT notification enabled.\n  bool _wxAlarm;                   ///< Weather alarm.\n};\n\n\n/** Implements the display settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneDisplaySettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The display frequency setting. */\n  Q_PROPERTY(bool displayFrequency READ displayFrequencyEnabled WRITE enableDisplayFrequency)\n  /** The display brightness [1-10]. */\n  Q_PROPERTY(unsigned int brightness READ brightness WRITE setBrightness)\n\n  Q_CLASSINFO(\"backlightDuration\", \"The duration in seconds, the backlight keeps lit after any action. \")\n  /** Backlight duration. */\n  Q_PROPERTY(Interval backlightDuration READ backlightDuration WRITE setBacklightDuration)\n\n  Q_CLASSINFO(\"backlightDurationTX\", \"The duration in seconds, the backlight is lit during TX. \"\n                                     \"A value of 0 means off.\")\n  /** TX backlight duration. */\n  Q_PROPERTY(Interval backlightDurationTX READ backlightDurationTX WRITE setBacklightDurationTX)\n\n  Q_CLASSINFO(\"backlightDurationRX\", \"The duration in seconds, the backlight is lit during RX.\")\n  /** RX backlight duration. */\n  Q_PROPERTY(Interval backlightDurationRX READ backlightDurationRX WRITE setBacklightDurationRX)\n\n  /** Enables custom channel background. */\n  Q_PROPERTY(bool customChannelBackground READ customChannelBackground WRITE enableCustomChannelBackground)\n\n  /** The volume-change prompt is shown. */\n  Q_PROPERTY(bool volumeChangePrompt READ volumeChangePromptEnabled WRITE enableVolumeChangePrompt)\n  /** The call-end prompt is shown. */\n  Q_PROPERTY(bool callEndPrompt READ callEndPromptEnabled WRITE enableCallEndPrompt)\n\n  /** If @c true, the clock is shown. */\n  Q_PROPERTY(bool showClock READ showClockEnabled WRITE enableShowClock)\n  /** If @c true, the call is shown. */\n  Q_PROPERTY(bool showCall READ showCallEnabled WRITE enableShowCall)\n  /** Shows the contact. */\n  Q_PROPERTY(bool showContact READ showContact WRITE enableShowContact)\n  /** Shows the channel number. */\n  Q_PROPERTY(bool showChannelNumber READ showChannelNumberEnabled WRITE enableShowChannelNumber)\n  /** Shows the global channel number. */\n  Q_PROPERTY(bool showGLobalChannelNumber READ showGlobalChannelNumber WRITE enableShowGlobalChannelNumber)\n  /** Shows the color code. */\n  Q_PROPERTY(bool showColorCode READ showColorCode WRITE enableShowColorCode)\n  /** Shows the time slot. */\n  Q_PROPERTY(bool showTimeSlot READ showTimeSlot WRITE enableShowTimeSlot)\n  /** Shows the channel type. */\n  Q_PROPERTY(bool showChannelType READ showChannelType WRITE enableShowChannelType)\n  /** Shows the last caller. */\n  Q_PROPERTY(bool showLastHeard READ showLastHeardEnabled WRITE enableShowLastHeard)\n  /** The last-caller display mode. */\n  Q_PROPERTY(LastCallerDisplayMode lastCallerDisplay READ lastCallerDisplay WRITE setLastCallerDisplay)\n\n  /** The color of the call. */\n  Q_PROPERTY(Color callColor READ callColor WRITE setCallColor)\n  /** The standby text color. */\n  Q_PROPERTY(Color standbyTextColor READ standbyTextColor WRITE setStandbyTextColor)\n  /** The standby background color. */\n  Q_PROPERTY(Color standbyBackgroundColor READ standbyBackgroundColor WRITE setStandbyBackgroundColor)\n\n  Q_CLASSINFO(\"channelNameColorDescription\", \"Specifies the color of the channel name.\")\n  /** The channel name color. */\n  Q_PROPERTY(Color channelNameColor READ channelNameColor WRITE setChannelNameColor)\n  Q_CLASSINFO(\"channelBNameColorDescription\", \"Specifies the color of the channel name for VFO B.\")\n  /** The channel name color for VFO B. */\n  Q_PROPERTY(Color channelBNameColor READ channelBNameColor WRITE setChannelBNameColor)\n\n  Q_CLASSINFO(\"zoneNameColorDescription\", \"Specifies the color of the zone name.\")\n  /** The zone name color. */\n  Q_PROPERTY(Color zoneNameColor READ zoneNameColor WRITE setZoneNameColor)\n  Q_CLASSINFO(\"zoneBNameColorDescription\", \"Specifies the color of the zone name for VFO B.\")\n  /** The zone name color for VFO B. */\n  Q_PROPERTY(Color zoneBNameColor READ zoneBNameColor WRITE setZoneBNameColor)\n\n  /** Specifies the UI language. */\n  Q_PROPERTY(Language language READ language WRITE setLanguage)\n  /** Specifies the date format. */\n  Q_PROPERTY(DateFormat dateFormat READ dateFormat WRITE setDateFormat)\n\npublic:\n  /** What to show from the last caller. */\n  enum class LastCallerDisplayMode {\n    Off = 0, ID = 1, Call = 2, Both = 3\n  };\n  Q_ENUM(LastCallerDisplayMode)\n\n  /** Possible display colors. */\n  enum class Color {\n    White = 0, Black = 1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7\n  };\n  Q_ENUM(Color)\n\n  /** Possible UI languages. */\n  enum class Language {\n    English = 0,                 ///< UI Language is english.\n    German  = 1                  ///< UI Language is german.\n  };\n  Q_ENUM(Language)\n\n  /** Possible date formats. */\n  enum class DateFormat {\n    YearFirst = 0,               ///< yyyy/mm/dd\n    DayFirst = 1                 ///< dd/mm/yyyy\n  };\n  Q_ENUM(DateFormat)\n\npublic:\n  /** Constructor. */\n  explicit AnytoneDisplaySettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true, if the frequency is displayed instead of the channel name. */\n  bool displayFrequencyEnabled() const;\n  /** Enables/disables display of frequency. */\n  void enableDisplayFrequency(bool enable);\n\n  /** Returns the display brightness [1-10]. */\n  unsigned int brightness() const;\n  /** Sets the display brightness [1-10]. */\n  void setBrightness(unsigned int level);\n\n  /** Returns @c true if the volume-change prompt is shown. */\n  bool volumeChangePromptEnabled() const;\n  /** Enables/disables the volume-change prompt. */\n  void enableVolumeChangePrompt(bool enable);\n\n  /** Returns @c true if the call-end prompt is shown. */\n  bool callEndPromptEnabled() const;\n  /** Enables/disables the call-end prompt. */\n  void enableCallEndPrompt(bool enable);\n\n  /** Returns the last caller display mode. */\n  LastCallerDisplayMode lastCallerDisplay() const;\n  /** Sets the last caller display mode. */\n  void setLastCallerDisplay(LastCallerDisplayMode mode);\n\n  /** Returns @c true if the clock is shown. */\n  bool showClockEnabled() const;\n  /** Enables/disables clock. */\n  void enableShowClock(bool enable);\n\n  /** Returns @c true if the call is shown. */\n  bool showCallEnabled() const;\n  /** Enables/disables display of call. */\n  void enableShowCall(bool enable);\n\n  /** Returns the color of the call. */\n  Color callColor() const;\n  /** Sets the color of the call. */\n  void setCallColor(Color color);\n\n  /** Returns the UI language. */\n  Language language() const;\n  /** Sets the UI language. */\n  void setLanguage(Language lang);\n  /** Returns the date format */\n  DateFormat dateFormat() const;\n  /** Sets the date format. */\n  void setDateFormat(DateFormat format);\n\n  /** Returns @c true if the channel number is shown. */\n  bool showChannelNumberEnabled() const;\n  /** Enables/disables the display of the channel number. */\n  void enableShowChannelNumber(bool enable);\n  /** Returns @c true, if the global channel number is shown. Otherwise,\n   * the channel number within the current zone is shown. */\n  bool showGlobalChannelNumber() const;\n  /** Enables/disables showing the global channel number. */\n  void enableShowGlobalChannelNumber(bool enable);\n  /** Returns @c true if the color code is shown. */\n  bool showColorCode() const;\n  /** Shows/hides color code. */\n  void enableShowColorCode(bool enable);\n  /** Returns @c true if the time slot is shown. */\n  bool showTimeSlot() const;\n  /** Shows/hides time slot. */\n  void enableShowTimeSlot(bool enable);\n  /** Returns @c true if the channel type is shown. */\n  bool showChannelType() const;\n  /** Shows/hides channel type. */\n  void enableShowChannelType(bool enable);\n\n  /** Returns @c true if the contact is shown. */\n  bool showContact() const;\n  /** Enables/disables the display of calling contact. */\n  void enableShowContact(bool enable);\n\n  /** Returns the standby text color. */\n  Color standbyTextColor() const;\n  /** Sets the standby text color. */\n  void setStandbyTextColor(Color color);\n  /** Returns the standby background color. */\n  Color standbyBackgroundColor() const;\n  /** Sets the standby background color. */\n  void setStandbyBackgroundColor(Color color);\n\n  /** Shows the last caller. */\n  bool showLastHeardEnabled() const;\n  /** Enables/disables display of last caller. */\n  void enableShowLastHeard(bool enable);\n\n  /** Returns backlight duration. */\n  Interval backlightDuration() const;\n  /** Sets the backlight duration in seconds. */\n  void setBacklightDuration(Interval sec);\n\n  /** Returns backlight duration during TX. */\n  Interval backlightDurationTX() const;\n  /** Sets the backlight duration during TX in seconds. */\n  void setBacklightDurationTX(Interval sec);\n\n  /** Returns the color of the channel name. */\n  Color channelNameColor() const;\n  /** Sets the color of the channel name. */\n  void setChannelNameColor(Color color);\n  /** Returns the color of the channel name for VFO B. */\n  Color channelBNameColor() const;\n  /** Sets the channel name color for VFO B. */\n  void setChannelBNameColor(Color color);\n\n  /** Returns the color of the zone name. */\n  Color zoneNameColor() const;\n  /** Sets the color of the zone name. */\n  void setZoneNameColor(Color color);\n  /** Returns the color of the zone name for VFO B. */\n  Color zoneBNameColor() const;\n  /** Sets the zone name color for VFO B. */\n  void setZoneBNameColor(Color color);\n\n  /** Returns backlight duration during RX. */\n  Interval backlightDurationRX() const;\n  /** Sets the backlight duration during RX in seconds. */\n  void setBacklightDurationRX(Interval sec);\n  /** Returns @c true if the custom channel background is enabled. */\n  bool customChannelBackground() const;\n  /** Enables/disables the custom channel background. */\n  void enableCustomChannelBackground(bool enable);\n\nprotected:\n  bool _displayFrequency;                   ///< Display frequency property.\n  unsigned int _brightness;                 ///< The display brightness.\n  bool _volumeChangePrompt;                 ///< Volume-change prompt enabled.\n  bool _callEndPrompt;                      ///< Call-end prompt enabled.\n  LastCallerDisplayMode _lastCallerDisplay; ///< Last-caller display mode.\n  bool _showClock;                          ///< Display clock.\n  bool _showCall;                           ///< Display call.\n  Color _callColor;                         ///< Color of call.\n  Language _language;                       ///< UI language.\n  DateFormat _dateFormat;                   ///< The date format.\n  bool _showChannelNumber;                  ///< Show channel number.\n  bool _showGlobalChannelNumber;            ///< Show global channel number.\n  bool _showColorCode;                      ///< Show color code.\n  bool _showTimeSlot;                       ///< Show time slot.\n  bool _showChannelType;                    ///< Show channel type.\n  bool _showContact;                        ///< Enables showing the contact.\n  Color _standbyTextColor;                  ///< Standby text color.\n  Color _standbyBackgroundColor;            ///< Standby background color.\n  bool _showLastHeard;                      ///< Shows the last caller.\n  Interval _backlightDuration;              ///< Backlight duration.\n  Interval _backlightDurationTX;            ///< Backlight duration during TX (0=off).\n  Interval _backlightDurationRX;            ///< Backlight duration during RX.\n  bool _customChannelBackground;            ///< Custom channel background enabled.\n  Color _channelNameColor;                  ///< Color of channel name.\n  Color _channelBNameColor;                 ///< Color of channel name for VFO B.\n  Color _zoneNameColor;                     ///< Color of zone name.\n  Color _zoneBNameColor;                    ///< Color of zone name for VFO B.\n};\n\n\n/** Implements the audio settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneAudioSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The VOX source. */\n  Q_PROPERTY(VoxSource voxSource READ voxSource WRITE setVOXSource)\n\n  /** If @c true, recording is enabled. */\n  Q_PROPERTY(bool recording READ recordingEnabled WRITE enableRecording)\n\n  /** If @c true, the audio is \"enhanced\". */\n  Q_PROPERTY(bool enhance READ enhanceAudioEnabled WRITE enableEnhanceAudio)\n\n  /** The mute delay in minutes. */\n  Q_PROPERTY(Interval muteDelay READ muteDelay WRITE setMuteDelay)\n\n  /** The enables speakers. */\n  Q_PROPERTY(Speaker speaker READ speaker WRITE setSpeaker)\n  /** The source for the handset speaker. */\n  Q_PROPERTY(HandsetSpeakerSource handsetSpeaker READ handsetSpeaker WRITE setHandsetSpeaker)\n  /** The handset type. */\n  Q_PROPERTY(HandsetType handsetType READ handsetType WRITE setHandsetType)\n\npublic:\n  /** Source for the VOX. */\n  enum class VoxSource {\n    Internal = 0, External = 1, Both = 2\n  };\n  Q_ENUM(VoxSource)\n\n  /** Possible speaker settings. */\n  enum class Speaker {\n    Handset, Radio, Both\n  };\n  Q_ENUM(Speaker);\n\n  /** Possible handset speaker sources. */\n  enum class HandsetSpeakerSource {\n    MainChannel, SubChannel\n  };\n  Q_ENUM(HandsetSpeakerSource)\n\n  /** Specifies possible handset types. */\n  enum class HandsetType {\n    Anytone, Generic\n  };\n  Q_ENUM(HandsetType)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneAudioSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the VOX source. */\n  VoxSource voxSource() const;\n  /** Sets the VOX source. */\n  void setVOXSource(VoxSource source);\n\n  /** Returns @c true if recording is enabled. */\n  bool recordingEnabled() const;\n  /** Enables/disables recording. */\n  void enableRecording(bool enable);\n\n  /** Returns @c true if the audio is \"enhanced\". */\n  bool enhanceAudioEnabled() const;\n  /** Enables/disables enhanced audio. */\n  void enableEnhanceAudio(bool enable);\n\n  /** Returns the mute delay. */\n  Interval muteDelay() const;\n  /** Sets the mute delay. */\n  void setMuteDelay(Interval intv);\n\n  /** Returns the speaker that are enabled. */\n  Speaker speaker() const;\n  /** Sets, which speacker are enabled. */\n  void setSpeaker(Speaker speaker);\n\n  /** Returns the handset speaker source. */\n  HandsetSpeakerSource handsetSpeaker() const;\n  /** Sets the handset speaker source. */\n  void setHandsetSpeaker(HandsetSpeakerSource src);\n\n  /* Returns the handset type. */\n  HandsetType handsetType() const;\n  /** Sets the handset type. */\n  void setHandsetType(HandsetType type);\n\nprotected:\n  Interval _voxDelay;               ///< VOX delay in ms.\n  bool _recording;                  ///< Recording enabled.\n  VoxSource _voxSource;             ///< The VOX source.\n  bool _enhanceAudio;               ///< Enhance audio.\n  Interval _muteDelay;              ///< Mute delay in minutes.\n  Speaker _speaker;                 ///< Specifies which speaker are enabled.\n  HandsetSpeakerSource _handsetSpeaker; ///< Specifies the handset speaker source.\n  HandsetType _handsetType;         ///< Handset type.\n};\n\n\n/** Implements the menu settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneMenuSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"durationDescription\", \"The time in seconds, the menu is shown.\")\n  /** Menu exit time in seconds. */\n  Q_PROPERTY(Interval duration READ duration WRITE setDuration)\n\n  Q_CLASSINFO(\"separatorDescription\", \"If enabled, the menu items are separated by a line.\")\n  /** Menu separator. */\n  Q_PROPERTY(bool separator READ separatorEnabled WRITE enableSeparator)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneMenuSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the menu duration in seconds. */\n  Interval duration() const;\n  /** Sets the menu duration in seconds. */\n  void setDuration(Interval sec);\n  /** Returns @c true, if the menu separator lines are shown. */\n  bool separatorEnabled() const;\n  /** Enables/disables the menu separator lines. */\n  void enableSeparator(bool enable);\n\nprotected:\n  Interval _menuDuration;          ///< Menu display duration in seconds.\n  bool _showSeparator;             ///< Show menu separator lines.\n};\n\n\n/** Implements the config representation of a repeater offset. This is just a transmit\n *  offset-frequency in Hz.\n * @ingroup anytone */\nclass AnytoneAutoRepeaterOffset: public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"off\")\n\n  Q_CLASSINFO(\"offsetDecription\",\n              \"Transmit-frequency offset in Hz.\")\n  Q_CLASSINFO(\"offsetLongDecription\",\n              \"The transmit-frequency offset is specified as a positive integer in Hz. The offset \"\n              \"direction is specified for each VFO separately.\")\n  /** The offset frequency. */\n  Q_PROPERTY(Frequency offset READ offset WRITE setOffset)\n\npublic:\n  /** Default constructor. */\n  explicit Q_INVOKABLE AnytoneAutoRepeaterOffset(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the transmit frequency offset in Hz. */\n  Frequency offset() const;\n  /** Sets the transmit frequency offset in Hz. */\n  void setOffset(Frequency offset);\n\nprotected:\n  /** The transmit frequency offset in Hz. */\n  Frequency _offset;\n};\n\n\n/** Represents a reference to a repeater offset.\n * @ingroup anytone */\nclass AnytoneAutoRepeaterOffsetRef: public ConfigObjectReference\n{\n  Q_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneAutoRepeaterOffsetRef(QObject *parent=nullptr);\n};\n\n\n/** Represents a list of auto-repeater offsets.\n * @ingroup anytone */\nclass AnytoneAutoRepeaterOffsetList: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit AnytoneAutoRepeaterOffsetList(QObject *parent=nullptr);\n\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err);\n};\n\n\n/** Implements the auto-repeater settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneAutoRepeaterSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"description\", \"Auto-repeater settings for AnyTone devices.\")\n\n  Q_CLASSINFO(\"directionADescription\", \"Auto-repeater transmit-frequency offset direction for VFO A.\")\n  /** Specifies the auto-repeater transmit-frequency offset direction for VFO A. */\n  Q_PROPERTY(Direction directionA READ directionA WRITE setDirectionA)\n\n  Q_CLASSINFO(\"directionBDescription\", \"Auto-repeater transmit-frequency offset direction for VFO B.\")\n  /** Specifies the auto-repeater transmit-frequency offset direction for VFO B. */\n  Q_PROPERTY(Direction directionB READ directionB WRITE setDirectionB)\n\n  Q_CLASSINFO(\"vhfMin\", \"The minimum frequency in Hz of the VHF auto-repeater frequency range.\")\n  /** The minimum frequency in Hz of the VHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency vhfMin READ vhfMin WRITE setVHFMin)\n\n  Q_CLASSINFO(\"vhfMax\", \"The maximum frequency in Hz of the VHF auto-repeater frequency range.\")\n  /** The maximum frequency in Hz of the VHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency vhfMax READ vhfMax WRITE setVHFMax)\n\n  Q_CLASSINFO(\"uhfMin\", \"The minimum frequency in Hz of the UHF auto-repeater frequency range.\")\n  /** The minimum frequency in Hz of the UHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency uhfMin READ uhfMin WRITE setUHFMin)\n\n  Q_CLASSINFO(\"uhfMax\", \"The maximum frequency in Hz of the UHF auto-repeater frequency range.\")\n  /** The maximum frequency in Hz of the UHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency uhfMax READ uhfMax WRITE setUHFMax)\n\n  Q_CLASSINFO(\"vhfDescription\", \"A reference to an offset frequency for the VHF band.\")\n  /** A reference to the auto-repeater frequency for VHF. */\n  Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* vhf READ vhfRef)\n\n  Q_CLASSINFO(\"uhfDescription\", \"A reference to an offset frequency for the UHF band.\")\n  /** A reference to the auto-repeater frequency for UHF. */\n  Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* uhf READ uhfRef)\n\n  Q_CLASSINFO(\"vhf2Min\", \"The minimum frequency in Hz of the second VHF auto-repeater frequency range.\")\n  /** The minimum frequency in Hz of the second VHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency vhf2Min READ vhf2Min WRITE setVHF2Min)\n\n  Q_CLASSINFO(\"vhf2Max\", \"The maximum frequency in Hz of the second VHF auto-repeater frequency range.\")\n  /** The maximum frequency in Hz of the second VHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency vhf2Max READ vhf2Max WRITE setVHF2Max)\n\n  Q_CLASSINFO(\"uhf2Min\", \"The minimum frequency in Hz of the second UHF auto-repeater frequency range.\")\n  /** The minimum frequency in Hz of the second UHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency uhf2Min READ uhf2Min WRITE setUHF2Min)\n\n  Q_CLASSINFO(\"uhf2Max\", \"The maximum frequency in Hz of the second UHF auto-repeater frequency range.\")\n  /** The maximum frequency in Hz of the second UHF auto-repeater frequency range. */\n  Q_PROPERTY(Frequency uhf2Max READ uhf2Max WRITE setUHF2Max)\n\n  Q_CLASSINFO(\"vhf2Description\", \"A reference to an offset frequency for the second VHF band.\")\n  /** A reference to the auto-repeater frequency for the second VHF band. */\n  Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* vhf2 READ vhf2Ref)\n\n  Q_CLASSINFO(\"uhf2Description\", \"A reference to an offset frequency for the second UHF band.\")\n  /** A reference to the auto-repeater frequency for the second UHF band. */\n  Q_PROPERTY(AnytoneAutoRepeaterOffsetRef* uhf2 READ uhf2Ref)\n\n  Q_CLASSINFO(\"offsetDescription\", \"The lists of offset frequencies.\")\n  /** The repeater transmit offset frequencies. */\n  Q_PROPERTY(AnytoneAutoRepeaterOffsetList* offsets READ offsets)\n\npublic:\n  /** Encodes the auto-repeater offset sign. */\n  enum class Direction {\n    Off = 0,       ///< Disabled.\n    Positive = 1,  ///< Positive frequency offset.\n    Negative = 2   ///< Negative frequency offset.\n  };\n  Q_ENUM(Direction)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneAutoRepeaterSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** The auto-repeater offset direction for VFO A. */\n  Direction directionA() const;\n  /** Set the auto-repeater offset direction for VFO A. */\n  void setDirectionA(Direction dir);\n  /** The auto-repeater offset direction for VFO B. */\n  Direction directionB() const;\n  /** Set the auto-repeater offset direction for VFO V. */\n  void setDirectionB(Direction dir);\n\n  /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */\n  Frequency vhfMin() const;\n  /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */\n  void setVHFMin(Frequency Hz);\n  /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */\n  Frequency vhfMax() const;\n  /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the VHF band. */\n  void setVHFMax(Frequency Hz);\n  /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */\n  Frequency uhfMin() const;\n  /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */\n  void setUHFMin(Frequency Hz);\n  /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */\n  Frequency uhfMax() const;\n  /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the UHF band. */\n  void setUHFMax(Frequency Hz);\n\n  /** Returns the reference for the UHF offset freuqency. */\n  AnytoneAutoRepeaterOffsetRef *uhfRef() const;\n  /** Returns the reference for the VHF offset freuqency. */\n  AnytoneAutoRepeaterOffsetRef *vhfRef() const;\n\n  /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */\n  Frequency vhf2Min() const;\n  /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */\n  void setVHF2Min(Frequency Hz);\n  /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */\n  Frequency vhf2Max() const;\n  /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the second VHF band. */\n  void setVHF2Max(Frequency Hz);\n  /** Returns the minimum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */\n  Frequency uhf2Min() const;\n  /** Sets the minimum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */\n  void setUHF2Min(Frequency Hz);\n  /** Returns the maximum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */\n  Frequency uhf2Max() const;\n  /** Sets the maximum frequency (in Hz) of the auto-repeater frequency range in the second UHF band. */\n  void setUHF2Max(Frequency Hz);\n\n  /** Returns the reference for the second UHF offset freuqency. */\n  AnytoneAutoRepeaterOffsetRef *uhf2Ref() const;\n  /** Returns the reference for the second VHF offset freuqency. */\n  AnytoneAutoRepeaterOffsetRef *vhf2Ref() const;\n\n  /** Returns a weak reference to the offset list. */\n  AnytoneAutoRepeaterOffsetList *offsets() const;\n\nprotected:\n  /** The auto-repeater direction for VFO A. */\n  Direction _directionA;\n  /** The auto-repeater direction for VFO B. */\n  Direction _directionB;\n  /** Minimum frequency of the VHF auto-repeater range. */\n  Frequency _minVHF;\n  /** Maximum frequency of the VHF auto-repeater range. */\n  Frequency _maxVHF;\n  /** Minimum frequency of the UHF auto-repeater range. */\n  Frequency _minUHF;\n  /** Maximum frequency of the UHF auto-repeater range. */\n  Frequency _maxUHF;\n  /** A reference to the VHF offset frequency. */\n  AnytoneAutoRepeaterOffsetRef *_vhfOffset;\n  /** A reference to the UHF offset frequency. */\n  AnytoneAutoRepeaterOffsetRef *_uhfOffset;\n  /** Minimum frequency of the second VHF auto-repeater range. */\n  Frequency _minVHF2;\n  /** Maximum frequency of the second VHF auto-repeater range. */\n  Frequency _maxVHF2;\n  /** Minimum frequency of the second UHF auto-repeater range. */\n  Frequency _minUHF2;\n  /** Maximum frequency of the second UHF auto-repeater range. */\n  Frequency _maxUHF2;\n  /** A reference to the second VHF offset frequency. */\n  AnytoneAutoRepeaterOffsetRef *_vhf2Offset;\n  /** A reference to the second UHF offset frequency. */\n  AnytoneAutoRepeaterOffsetRef *_uhf2Offset;\n  /** The list of repeater offsets. */\n  AnytoneAutoRepeaterOffsetList *_offsets;\n};\n\n\n/** Implements the DMR settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneDMRSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** Manual dialed group-call hang-time in seconds. */\n  Q_PROPERTY(Interval manualGroupCallHangTime READ manualGroupCallHangTime WRITE setManualGroupCallHangTime)\n  /** Manual dialed private-call hang-time in seconds. */\n  Q_PROPERTY(Interval manualPrivateCallHangTime READ manualPrivateCallHangTime WRITE setManualPrivateCallHangTime)\n\n  Q_CLASSINFO(\"wakeHeadPeriod\", \"Sets the wake head-period in ms. Should be set to 100ms.\")\n  /** Wake head-period in ms. */\n  Q_PROPERTY(Interval wakeHeadPeriod READ wakeHeadPeriod WRITE setWakeHeadPeriod)\n\n  Q_CLASSINFO(\"filterOwnIDDescription\", \"If enabled, own ID is not shown in call lists.\")\n  /** Filter own ID from call lists. */\n  Q_PROPERTY(bool filterOwnID READ filterOwnIDEnabled WRITE enableFilterOwnID)\n\n  Q_CLASSINFO(\"monitorSlotMatchDescription\", \"Time-slot match-mode for DMR monitor.\")\n  /** Slot-match mode for DMR monitor. */\n  Q_PROPERTY(SlotMatch monitorSlotMatch READ monitorSlotMatch WRITE setMonitorSlotMatch)\n\n  Q_CLASSINFO(\"monitorColorCodeMatchDescription\", \"If enabled, the DMR monitor will only open for \"\n              \"matching color-codes.\")\n  /** Color-code match for DMR monitor. */\n  Q_PROPERTY(bool monitorColorCodeMatch READ monitorColorCodeMatchEnabled WRITE enableMonitorColorCodeMatch)\n\n  Q_CLASSINFO(\"monitorIDMatchDescription\", \"If enabled, the DMR monitor will only open for matching IDs.\")\n  /** ID match for DMR monitor. */\n  Q_PROPERTY(bool monitorIDMatch READ monitorIDMatchEnabled WRITE enableMonitorIDMatch)\n\n  Q_CLASSINFO(\"monitorTimeSlotHold\", \"Whether the DMR monitor holds the time-slot.\")\n  /** The DMR monitor holds the time-slot. */\n  Q_PROPERTY(bool monitorTimeSlotHold READ monitorTimeSlotHoldEnabled WRITE enableMonitorTimeSlotHold)\n\n  /** Specifies the talker alias source. */\n  Q_PROPERTY(TalkerAliasSource talkerAliasSource READ talkerAliasSource WRITE setTalkerAliasSource)\n\n  /** The encryption type to be used. */\n  Q_PROPERTY(EncryptionType encryption READ encryption WRITE setEncryption)\n\n\npublic:\n  /** Possible monitor slot matches. */\n  enum class SlotMatch {\n    Off = 0, Single = 1, Both = 2\n  };\n  Q_ENUM(SlotMatch)\n\n  /** Talker alias display preference. */\n  enum class TalkerAliasSource {\n    Off = 0, UserDB = 1, Air = 2\n  };\n  Q_ENUM(TalkerAliasSource)\n\n  /** Possible encryption types. */\n  enum class EncryptionType {\n    AES=0, DMR=1\n  };\n  Q_ENUM(EncryptionType)\n\npublic:\n  /** Constructor. */\n  explicit AnytoneDMRSettingsExtension(QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the manual dialed group-call hang-time in seconds. */\n  Interval manualGroupCallHangTime() const;\n  /** Sets the manual dialed group-call hang-time in seconds. */\n  void setManualGroupCallHangTime(Interval sec);\n  /** Returns the manual dialed private-call hang-time in seconds. */\n  Interval manualPrivateCallHangTime() const;\n  /** Sets the manual dialed private-call hang-time in seconds. */\n  void setManualPrivateCallHangTime(Interval sec);\n\n  /** Returns the wake head-period in ms. */\n  Interval wakeHeadPeriod() const;\n  /** Sets the wake head-period in ms. */\n  void setWakeHeadPeriod(Interval ms);\n\n  /** If @c true, the own ID is not shown in call lists. */\n  bool filterOwnIDEnabled() const;\n  /** Enables/disables filtering of own ID. */\n  void enableFilterOwnID(bool enable);\n\n  /** Returns the slot-match mode for the DMR monitor. */\n  SlotMatch monitorSlotMatch() const;\n  /** Sets the slot-match mode for the DMR monitor. */\n  void setMonitorSlotMatch(SlotMatch match);\n  /** Returns @c true if the CC match is enabled for the DMR monitor. */\n  bool monitorColorCodeMatchEnabled() const;\n  /** Enables/disables the CC match for the DMR monitor. */\n  void enableMonitorColorCodeMatch(bool enable);\n  /** Returns @c true if the ID match is enabled for the DMR monitor. */\n  bool monitorIDMatchEnabled() const;\n  /** Enables/disables ID match for the DMR monitor. */\n  void enableMonitorIDMatch(bool enable);\n  /** Returns @c true if the time-slot is held by the DMR monitor. */\n  bool monitorTimeSlotHoldEnabled() const;\n  /** Enables/disables the time-slot hold for the DMR monitor. */\n  void enableMonitorTimeSlotHold(bool enable);\n\n  /** Returns the talker alias source. */\n  TalkerAliasSource talkerAliasSource() const;\n  /** Sets the talker alias source. */\n  void setTalkerAliasSource(TalkerAliasSource mode);\n\n  /** Returns the encryption type. */\n  EncryptionType encryption() const;\n  /** Sets the encryption type. */\n  void setEncryption(EncryptionType type);\n\nprotected:\n  Interval _manualGroupCallHangTime;    ///< Hang-time for manual dialed group-calls in seconds.\n  Interval _manualPrivateCallHangTime;  ///< Hang-time for manual dialed private-calls in seconds.\n  Interval _wakeHeadPeriod;             ///< Wake head-period in ms, should be 100ms.\n  bool _filterOwnID;                    ///< If enabled, the own ID is not shown in call lists.\n  SlotMatch _monitorSlotMatch;          ///< Slot-match mode for DMR monitor.\n  bool _monitorColorCodeMatch;          ///< Enables CC match for DMR monitor.\n  bool _monitorIDMatch;                 ///< Enables ID match for DMR monitor.\n  bool _monitorTimeSlotHold;            ///< Enables the time-slot hold for the DMR monitor.\n  TalkerAliasSource _talkerAliasSource; ///< Source for the talker alias.\n  EncryptionType _encryption;           ///< DMR encryption type.\n};\n\n\n/** Implements the GPS settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneGPSSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"timeZoneDescription\", \"Specifies the GPS time-zone (IANA name).\")\n  /** The time-zone IANA Id. */\n  Q_PROPERTY(QString timeZone READ ianaTimeZone WRITE setIANATimeZone)\n\n  Q_CLASSINFO(\"positionReportingDescription\", \"Enables GPS range reporting.\")\n  /** Enables GPS range reporting. */\n  Q_PROPERTY(bool reportPosition READ positionReportingEnabled WRITE enablePositionReporting)\n\n  Q_CLASSINFO(\"updatePeriodDescription\", \"Specifies the GPS reporting interval in seconds.\")\n  /** GPS ranging interval in seconds. */\n  Q_PROPERTY(Interval updatePeriod READ updatePeriod WRITE setUpdatePeriod)\n\npublic:\n  /** Constructor. */\n  explicit AnytoneGPSSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the IANA ID of the time zone. */\n  QString ianaTimeZone() const;\n  /** Returns the time-zone. */\n  QTimeZone timeZone() const;\n  /** Sets the time zone by IANA ID. */\n  void setIANATimeZone(const QString &id);\n  /** Sets the time zone. */\n  void setTimeZone(const QTimeZone &zone);\n\n  /** Returns @c true if the GPS range reporting is enabled. */\n  bool positionReportingEnabled() const;\n  /** Enables/disables the GPS range reporting. */\n  void enablePositionReporting(bool enable);\n\n  /** Returns the GPS ranging interval in seconds. */\n  Interval updatePeriod() const;\n  /** Sets the GPS ranging interval in seconds. */\n  void setUpdatePeriod(Interval sec);\n\nprotected:\n  QTimeZone _timeZone;                         ///< The time zone.\n  bool _gpsRangeReporting;                     ///< Enables GPS range reporting.\n  Interval _gpsRangingInterval;                ///< The GPS ranging interval in seconds.\n};\n\n\n/** Implements the ranging/roaming settings extension of AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneRoamingSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"Description\", \"Collects all ranging/roaming settings for AnyTone devices.\")\n\n  /** Enables auto-roaming. */\n  Q_PROPERTY(bool autoRoam READ autoRoam WRITE enableAutoRoam)\n\n  Q_CLASSINFO(\"autoRoamPeriodDescription\", \"Specifies the auto-roaming period in minutes.\")\n  /** The auto-roaming period in minutes. */\n  Q_PROPERTY(Interval autoRoamPeriod READ autoRoamPeriod WRITE setAutoRoamPeriod)\n\n  Q_CLASSINFO(\"autoRoamDelayDescription\", \"A delay in seconds before starting the auto-roaming.\")\n  /** The auto-roam delay. */\n  Q_PROPERTY(Interval autoRoamDelay READ autoRoamDelay WRITE setAutoRoamDelay)\n\n  Q_CLASSINFO(\"roamStart\", \"Start condition for auto-roaming.\")\n  /** Auto-roaming start condition. */\n  Q_PROPERTY(RoamStart roamStart READ roamingStartCondition WRITE setRoamingStartCondition)\n\n  Q_CLASSINFO(\"roamReturn\", \"Condition to return to the original repeater.\")\n  /** Auto-roaming end/return condition. */\n  Q_PROPERTY(RoamStart roamReturn READ roamingReturnCondition WRITE setRoamingReturnCondition)\n\n  Q_CLASSINFO(\"rangeCheckDescription\", \"Repeater range check.\")\n  /** Repeater range check. */\n  Q_PROPERTY(bool rangeCheck READ repeaterRangeCheckEnabled WRITE enableRepeaterRangeCheck)\n\n  Q_CLASSINFO(\"checkIntervalDescription\", \"Repeater range check interval in seconds.\")\n  /** Repeater range check interval in seconds. */\n  Q_PROPERTY(Interval checkInterval READ repeaterCheckInterval WRITE setRepeaterCheckInterval)\n\n  Q_CLASSINFO(\"retryCount\", \"Number of retries to connect to a repeater before giving up.\")\n  /** Retry count. */\n  Q_PROPERTY(unsigned int retryCount READ repeaterRangeCheckCount WRITE setRepeaterRangeCheckCount)\n\n  /** Repeater out-of-range alert type. */\n  Q_PROPERTY(OutOfRangeAlert outOfRangeAlert READ outOfRangeAlert WRITE setOutOfRangeAlert)\n\n  Q_CLASSINFO(\"notificationDescription\", \"Enables the repeater-check notification.\")\n  /** Repeater-check notification. */\n  Q_PROPERTY(bool notification READ notificationEnabled WRITE enableNotification)\n\n  Q_CLASSINFO(\"notificationCountDescription\", \"The number of repeater-check notifications.\")\n  /** Repeater-check notification count. */\n  Q_PROPERTY(unsigned int notificationCount READ notificationCount WRITE setNotificationCount)\n\n  /** The default roaming zone. */\n  Q_PROPERTY(RoamingZoneReference* defaultZone READ defaultZone)\n\n  /** GPS roaming enabled. */\n  Q_PROPERTY(bool gpsRoaming READ gpsRoaming WRITE enableGPSRoaming)\n\npublic:\n  /** Possible roaming start conditions. */\n  enum class RoamStart {\n    Periodic=0, OutOfRange=1\n  };\n  Q_ENUM(RoamStart)\n\n  /** Possible repeater out-of-range alerts. */\n  enum class OutOfRangeAlert {\n    None = 0x00, Bell = 0x01, Voice = 0x02\n  };\n  Q_ENUM(OutOfRangeAlert)\n\npublic:\n  /** Constructor. */\n  explicit AnytoneRoamingSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if auto-roaming is enabled. */\n  bool autoRoam() const;\n  /** Enables/disables auto-roaming. */\n  void enableAutoRoam(bool enable);\n\n  /** Returns the auto-roaming period in minutes. */\n  Interval autoRoamPeriod() const;\n  /** Sets the auto-roam period in minutes. */\n  void setAutoRoamPeriod(Interval min);\n  /** Returns the auto-roam delay in seconds. */\n  Interval autoRoamDelay() const;\n  /** Sets the auto-roam delay in seconds. */\n  void setAutoRoamDelay(Interval sec);\n\n  /** Returns @c true if the repeater range check is enabled. */\n  bool repeaterRangeCheckEnabled() const;\n  /** Enables/disables repeater range check. */\n  void enableRepeaterRangeCheck(bool enable);\n  /** Returns the repeater check interval in seconds. */\n  Interval repeaterCheckInterval() const;\n  /** Sets the repeater check interval in seconds. */\n  void setRepeaterCheckInterval(Interval sec);\n  /** Number of retries before givnig up. */\n  unsigned int repeaterRangeCheckCount() const;\n  /** Sets the number of retries before giving up. */\n  void setRepeaterRangeCheckCount(unsigned int count);\n  /** Returns the repeater out-of-range alert type. */\n  OutOfRangeAlert outOfRangeAlert() const;\n  /** Sets the repeater out-of-range alert type. */\n  void setOutOfRangeAlert(OutOfRangeAlert type);\n\n  /** Returns the auto-roaming start condition. */\n  RoamStart roamingStartCondition() const;\n  /** Sets the auto-roaming start condition. */\n  void setRoamingStartCondition(RoamStart start);\n  /** Returns the auto-roaming return condition. */\n  RoamStart roamingReturnCondition() const;\n  /** Sets the auto-roaming return condition. */\n  void setRoamingReturnCondition(RoamStart start);\n\n  /** Returns @c true, if the repeater check notification is enabled. */\n  bool notificationEnabled() const;\n  /** Enables/disables the repeater-check notification. */\n  void enableNotification(bool enable);\n  /** Returns the number of notifications. */\n  unsigned int notificationCount() const;\n  /** Sets the number of repeater-check notifications. */\n  void setNotificationCount(unsigned int n);\n\n  /** Returns @c true if GPS roaming is enabled. */\n  bool gpsRoaming() const;\n  /** Enables/disables GPS roaming. */\n  void enableGPSRoaming(bool enable);\n\n  /** Returns a reference to the default roaming zone. */\n  RoamingZoneReference *defaultZone() const;\n\nprotected:\n  bool _autoRoam;                              ///< Enables auto roaming.\n  Interval _autoRoamPeriod;                    ///< The auto-roam period in minutes.\n  Interval _autoRoamDelay;                     ///< The auto-roam delay in seconds.\n  bool _repeaterRangeCheck;                    ///< Enables the repeater range-check.\n  Interval _repeaterCheckInterval;             ///< The repeater check interval in seconds.\n  unsigned int _repeaterRangeCheckCount;       ///< Number of range checks before giving up.\n  OutOfRangeAlert _outOfRangeAlert;            ///< Type of the out-out-range alert.\n  RoamStart _roamingStartCondition;            ///< Auto-roaming start condition.\n  RoamStart _roamingReturnCondition;           ///< Auto-roaming return condition.\n  bool _notification;                          ///< Repeater check notification.\n  unsigned int _notificationCount;             ///< Number of notifications.\n  bool _gpsRoaming;                            ///< Enables GPS roaming.\n  RoamingZoneReference *_defaultRoamingZone;   ///< The default roaming zone.\n};\n\n\n\n/** Implements the bluetooth handset settings for AnyTone devices.\n * This extension is part of the @c AnyToneBlueoothSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneBluetoothHandsetSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"Description\", \"Collects all bluetooth handset settings for AnyTone devices.\")\n\n  Q_CLASSINFO(\"shutdownDescription\", \"Shuts the handset off too, when the radio is turned off.\")\n  Q_PROPERTY(bool shutdown READ shutdownEnabled WRITE enableShutdown)\n\n  Q_CLASSINFO(\"volumeLevelADescription\", \"Sets the volume level for VFO A.\")\n  Q_PROPERTY(unsigned int volumeLevelA READ volumeLevelA WRITE setVolumeLevelA)\n  Q_CLASSINFO(\"volumeLevelBDescription\", \"Sets the volume level for VFO B.\")\n  Q_PROPERTY(unsigned int volumeLevelB READ volumeLevelB WRITE setVolumeLevelB)\n\n  Q_CLASSINFO(\"micGainDescription\", \"Specifies the microphone gain.\")\n  Q_PROPERTY(unsigned int micGain READ micGain WRITE setMicGain)\n\n  Q_PROPERTY(unsigned int squelch READ squelch WRITE setSquelch)\n\n  Q_PROPERTY(unsigned int txNoiseReduction READ txNoiseReduction WRITE setTxNoiseReduction)\n\n  Q_PROPERTY(unsigned int voxLevel READ voxLevel WRITE setVoxLevel)\n  Q_PROPERTY(Interval voxDelay READ voxDelay WRITE setVoxDelay)\n\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyAShort     READ funcKeyAShort     WRITE setFuncKeyAShort)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyBShort     READ funcKeyBShort     WRITE setFuncKeyBShort)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyCShort     READ funcKeyCShort     WRITE setFuncKeyCShort)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyALong      READ funcKeyALong      WRITE setFuncKeyALong)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyBLong      READ funcKeyBLong      WRITE setFuncKeyBLong)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyCLong      READ funcKeyCLong      WRITE setFuncKeyCLong)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyAVeryLong  READ funcKeyAVeryLong  WRITE setFuncKeyAVeryLong)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyBVeryLong  READ funcKeyBVeryLong  WRITE setFuncKeyBVeryLong)\n  Q_PROPERTY(AnytoneKeySettingsExtension::KeyFunction funcKeyCVeryLong  READ funcKeyCVeryLong  WRITE setFuncKeyCVeryLong)\n\n  Q_PROPERTY(Interval backlight READ backlight WRITE setBacklight)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneBluetoothHandsetSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const override;\n\n  /** Returns @c true, if the handset is shutdown along with the radio. */\n  bool shutdownEnabled() const;\n  /** Enables/disables shutting the handset down along with the radio. */\n  void enableShutdown(bool enable);\n\n  /** Returns the volume level for VFO A. */\n  unsigned int volumeLevelA() const;\n  /** Sets the volume level for VFO A. */\n  void setVolumeLevelA(unsigned int level);\n  /** Returns the volume level for VFO B. */\n  unsigned int volumeLevelB() const;\n  /** Sets the volume level for VFO B. */\n  void setVolumeLevelB(unsigned int level);\n\n  /** Returns the mic gain. */\n  unsigned int micGain() const;\n  /** Sets the mic gain. */\n  void setMicGain(unsigned int gain);\n\n  /** Returns the squlech level. */\n  unsigned int squelch() const;\n  /** Sets the squelch level. */\n  void setSquelch(unsigned int level);\n\n  /** Returns the transmit noise reduction level. */\n  unsigned int txNoiseReduction() const;\n  /** Sets the transmit noise reduction level. */\n  void setTxNoiseReduction(unsigned int level);\n\n  /** Returns the VOX level. */\n  unsigned int voxLevel() const;\n  /** Set the VOX level. */\n  void setVoxLevel(unsigned int level);\n\n  /** Returns the VOX delay. */\n  Interval voxDelay() const;\n  /** Sets the VOX delay. */\n  void setVoxDelay(Interval delay);\n\n  /** Returns function key A short press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const;\n  /** Set function key A short press function. */\n  void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key B short press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const;\n  /** Set function key B short press function. */\n  void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key C short press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const;\n  /** Set function key C short press function. */\n  void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key A long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const;\n  /** Set function key A long press function. */\n  void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key B long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const;\n  /** Set function key B long press function. */\n  void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key C long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const;\n  /** Set function key C long press function. */\n  void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key A very long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyAVeryLong() const;\n  /** Set function key A very long press function. */\n  void setFuncKeyAVeryLong(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key B very long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyBVeryLong() const;\n  /** Set function key B very long press function. */\n  void setFuncKeyBVeryLong(AnytoneKeySettingsExtension::KeyFunction func);\n  /** Returns function key C very long press function. */\n  AnytoneKeySettingsExtension::KeyFunction funcKeyCVeryLong() const;\n  /** Set function key C very long press function. */\n  void setFuncKeyCVeryLong(AnytoneKeySettingsExtension::KeyFunction func);\n\n  /** Returns the backlight duration. */\n  Interval backlight() const;\n  /** Sets the backlight duration. */\n  void setBacklight(Interval dur);\n\nprotected:\n  bool _shutdown;                    ///< Shut the handset down along with the radio.\n  unsigned int _volumeLevelA;        ///< Specifies the volume level for VFO A.\n  unsigned int _volumeLevelB;        ///< Specifies the volume level for VFO B.\n  unsigned int _micGain;             ///< Specifies the mic gain.\n  unsigned int _squelch;             ///< Specifies the squelch level.\n  unsigned int _txNoiseReduction;    ///< Specifies the transmit noise reduction level.\n  unsigned int _voxLevel;            ///< Specifies the VOX level.\n  Interval _voxDelay;                ///< Specifies the VOX delay.\n  /// Function key A short press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyAShort;\n  /// Function key B short press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyBShort;\n  /// Function key B short press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyCShort;\n  /// Function key A long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyALong;\n  /// Function key B long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyBLong;\n  /// Function key B long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyCLong;\n  /// Function key A very long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyAVeryLong;\n  /// Function key B very long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyBVeryLong;\n  /// Function key B very long press function.\n  AnytoneKeySettingsExtension::KeyFunction _funcKeyCVeryLong;\n  Interval _backlight;                ///< Backlight duration.\n};\n\n\n\n/** Implements the bluetooth settings for some AnyTone devices.\n * This extension is part of the @c AnytoneSettingsExtension.\n *\n * @ingroup anytone */\nclass AnytoneBluetoothSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"Description\", \"Collects all bluetooth settings for AnyTone devices.\")\n\n  /** If @c true, bluetooth is enabled. */\n  Q_PROPERTY(bool enabled READ bluetoothEnabled WRITE enableBluetooth)\n  /** If @c true, the PTT latch is enabled. */\n  Q_PROPERTY(bool pttLatch READ pttLatch WRITE enablePTTLatch)\n  /** The sleep timeout of the PTT button. If 0, sleep timer is disabled. */\n  Q_PROPERTY(Interval pttSleepTimer READ pttSleepTimer WRITE setPTTSleepTimer)\n  /** If @c true, the internal mic is enabled additionally to the bluetooth input. */\n  Q_PROPERTY(bool internalMic READ internalMicEnabled WRITE enableInternalMic)\n  /** If @c true, the internal speaker is enabled additionally to the bluetooth output. */\n  Q_PROPERTY(bool internalSpeaker READ internalSpeakerEnabled WRITE enableInternalSpeaker)\n  /** The bluetooth mic gain. */\n  Q_PROPERTY(unsigned int micGain READ micGain WRITE setMicGain);\n  /** The bluetooth speaker gain. */\n  Q_PROPERTY(unsigned int speakerGain READ speakerGain WRITE setSpeakerGain);\n  /** The bluetooth hold duration. */\n  Q_PROPERTY(Interval hold READ holdDuration WRITE setHoldDuration)\n  /** The bluetooth hold delay. */\n  Q_PROPERTY(Interval holdDelay READ holdDelay WRITE setHoldDelay)\n  /** The handset settings. */\n  Q_PROPERTY(AnytoneBluetoothHandsetSettingsExtension* handset READ handset)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneBluetoothSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the handset settings. */\n  AnytoneBluetoothHandsetSettingsExtension *handset() const;\n\n  /** Returns @c true if bluetooth is enabled. */\n  bool bluetoothEnabled() const;\n  /** Enables/disables bluetooth. */\n  void enableBluetooth(bool enable);\n\n  /** Returns @c true if the PTT latch is enabled. */\n  bool pttLatch() const;\n  /** Enables/disables the PTT latch. */\n  void enablePTTLatch(bool enable);\n\n  /** Returns the PTT sleep timeout, 0 means off. */\n  Interval pttSleepTimer() const;\n  /** Sets the PTT sleep timeout, 0 means off. */\n  void setPTTSleepTimer(Interval intv);\n\n  /** Returns @c true if the internal mic is enabled additionally to the bluetooth input. */\n  bool internalMicEnabled() const;\n  /** Enables/disables internal mic additionally to the bluetooth input. */\n  void enableInternalMic(bool enable);\n\n  /** Returns @c true if the internal speaker is enabled additionally to the bluetooth output. */\n  bool internalSpeakerEnabled() const;\n  /** Enables/disables internal speaker additionally to the bluetooth output. */\n  void enableInternalSpeaker(bool enable);\n\n  /** Returns the bluetooth mic gain. */\n  unsigned int micGain() const;\n  /** Sets the bluetooth mic gain. */\n  void setMicGain(unsigned int gain);\n\n  /** Returns the bluetooth speaker gain. */\n  unsigned int speakerGain() const;\n  /** Sets the bluetooth speaker gain. */\n  void setSpeakerGain(unsigned int gain);\n\n  /** Returns the bluetooth hold duration. */\n  const Interval &holdDuration() const;\n  /** Sets the bluetooth hold duration. */\n  void setHoldDuration(const Interval &dur);\n\n  /** Returns the bluetooth hold delay. */\n  const Interval &holdDelay() const;\n  /** Sets the bluetooth hold delay. */\n  void setHoldDelay(const Interval &dur);\n\nprotected:\n  /// Handset settings.\n  AnytoneBluetoothHandsetSettingsExtension *_handset;\n  bool _bluetoothEnabled;      ///< Enables bluetooth.\n  bool _pttLatch;              ///< PTT latch flag.\n  Interval _pttSleep;          ///< PTT sleep timer, 0 is off.\n  bool _internalMic;           ///< Additionally enables internal mic.\n  bool _internalSpeaker;       ///< Additionally enables internal speaker.\n  unsigned int _micGain;       ///< The bluetooth mic gain.\n  unsigned int _speakerGain;   ///< The bluetooth speaker gain.\n  Interval _holdDuration;      ///< What ever.\n  Interval _holdDelay;         ///< What ever.\n};\n\n\n\n/** Implements the repeater settings for the BTECH DMR-6X2UV and AT-D578UV.\n * This extension is part of the @c AnytoneSettingsExtension\n *\n * @ingroup anytone */\nclass AnytoneRepeaterSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"enabledDescription\", \"If true, the repeater feature is enabled.\")\n  /** Enables/disables the repeater feature. */\n  Q_PROPERTY(bool enabled READ enabled WRITE enable)\n\n  Q_CLASSINFO(\"monitorDescription\", \"If true, the repeater-monitoring is enabled.\")\n  /** Enables/disables the repeater monitor. */\n  Q_PROPERTY(bool monitor READ monitorEnabled WRITE enableMonitor)\n\n  Q_CLASSINFO(\"timeSlotDescription\", \"Specifies the time-slot of the primary repeater channel.\")\n  /** Time-slot of the primary repeater channel. */\n  Q_PROPERTY(TimeSlot timeSlot READ timeSlot WRITE setTimeSlot)\n\n  Q_CLASSINFO(\"secTimeSlotDescription\", \"Specifies the time-slot of the secondary repeater channel.\")\n  /** Time-slot of the secondary repeater channel. */\n  Q_PROPERTY(TimeSlot secTimeSlot READ secTimeSlot WRITE setSecTimeSlot)\n\n  Q_CLASSINFO(\"colorCodeDescription\", \"Specifies the repeater color code.\")\n  Q_PROPERTY(ColorCode colorCode READ colorCode WRITE setColorCode)\n\npublic:\n  /** Possible repeater time-slots. */\n  enum class TimeSlot {\n    Any, TS1, TS2, Channel\n  };\n  Q_ENUM(TimeSlot)\n\n  /** Possible color code settings. */\n  enum class ColorCode {\n    Ignored, VFOA, VFOB\n  };\n  Q_ENUM(ColorCode)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneRepeaterSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** If @c true, the simplex-repeater is enabled. */\n  bool enabled() const;\n  /** Enables/disables the simplex repeater. */\n  void enable(bool enable);\n\n  /** If @c true, repeater monitoring is enabled. */\n  bool monitorEnabled() const;\n  /** Enables/disables repeater monitoring. */\n  void enableMonitor(bool enable);\n\n  /** Returns the primary (simplex) channel time-slot. */\n  TimeSlot timeSlot() const;\n  /** Sets the primary (simplex) channel time-slot. */\n  void setTimeSlot(TimeSlot ts);\n\n  /** Returns the secondary channel time-slot. */\n  TimeSlot secTimeSlot() const;\n  /** Sets the secondary channel time-slot. */\n  void setSecTimeSlot(TimeSlot ts);\n\n  /** Returns the color code match mode. */\n  ColorCode colorCode() const;\n  /** Sets the color code match mode. */\n  void setColorCode(ColorCode code);\n\nprotected:\n  bool _enabled;                ///< If @c true, the simplex repeater is enabled.\n  bool _monitor;                ///< If enabled, the radio will monitor the channel.\n  TimeSlot _timeSlot;           ///< The primary channel time-slot.\n  TimeSlot _secTimeSlot;        ///< The secondary channel time-slot.\n  ColorCode _colorCode;         ///< The color code match mode.\n};\n\n\n\n/** Implements the satellite mode settings for the BTECH DMR-6X2UV PRO.\n * This extension is part of the @c AnytoneSettingsExtension\n *\n * @ingroup anytone */\nclass AnytoneSatelliteSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"powerDescription\", \"Specifies the transmit power in satellite mode.\")\n  /** Transmit power in satellite mode. */\n  Q_PROPERTY(Channel::Power power READ power WRITE setPower)\n\n  Q_CLASSINFO(\"squelchDescription\", \"Specifies FM squelch level in satellite mode.\")\n  /** Enables/disables the repeater monitor. */\n  Q_PROPERTY(unsigned int squelch READ squelch WRITE setSquelch)\n\npublic:\n  /** Default constructor. */\n  explicit AnytoneSatelliteSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns transmit power in satellite mode. */\n  Channel::Power power() const;\n  /** Sets transmit power in satellite mode. */\n  void setPower(Channel::Power power);\n\n  /** Returns FM squelch in satellite mode. */\n  unsigned int squelch() const;\n  /** Sets squelch in satellite mode. */\n  void setSquelch(unsigned int squelch);\n\nprotected:\n  /** Transmit power in satellite mode. */\n  Channel::Power _power;\n  /** FM squelch in satellite mode. */\n  unsigned int _squelch;\n};\n\n\n\n/** Implements the device specific extension for the general settings of AnyTone devices.\n *\n * As there are a huge amount of different settings, they are split into separate extensions.\n * One for each topic.\n *\n * @ingroup anytone */\nclass AnytoneSettingsExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"Device specific settings for AnyTone devices.\")\n\n  Q_CLASSINFO(\"subChannelDescription\", \"Enables/disables the sub-channel.\")\n  /** If @c true, the sub-channel is enabled. */\n  Q_PROPERTY(bool subChannel READ subChannelEnabled WRITE enableSubChannel)\n\n  Q_CLASSINFO(\"selectedVFODecription\", \"Specifies the currently selected VFO (A or B).\")\n  /** The current active VFO. */\n  Q_PROPERTY(VFO selectedVFO READ selectedVFO WRITE setSelectedVFO)\n\n  Q_CLASSINFO(\"modeADescription\", \"Specifies the mode (memory or VFO) for VFO A.\")\n  Q_CLASSINFO(\"modeBDescription\", \"Specifies the mode (memory or VFO) for VFO B.\")\n  /** The mode of VFO A. */\n  Q_PROPERTY(VFOMode modeA READ modeA WRITE setModeA)\n  /** The mode of VFO B. */\n  Q_PROPERTY(VFOMode modeB READ modeB WRITE setModeB)\n\n  Q_CLASSINFO(\"zoneADescription\", \"Specifies the current zone for VFO A.\")\n  Q_CLASSINFO(\"zoneBDescription\", \"Specifies the current zone for VFO B.\")\n  /** The current zone for VFO A. */\n  Q_PROPERTY(ZoneReference* zoneA READ zoneA)\n  /** The current zone for VFO B. */\n  Q_PROPERTY(ZoneReference* zoneB READ zoneB)\n\n  /** The VFO scan type. */\n  Q_PROPERTY(VFOScanType vfoScanType READ vfoScanType WRITE setVFOScanType)\n  /** The minimum UHF VFO-scan frequency in Hz. */\n  Q_PROPERTY(Frequency minVFOScanFrequencyUHF READ minVFOScanFrequencyUHF WRITE setMinVFOScanFrequencyUHF)\n  /** The maximum UHF VFO-scan frequency in Hz. */\n  Q_PROPERTY(Frequency maxVFOScanFrequencyUHF READ maxVFOScanFrequencyUHF WRITE setMaxVFOScanFrequencyUHF)\n  /** The minimum VHF VFO-scan frequency in Hz. */\n  Q_PROPERTY(Frequency minVFOScanFrequencyVHF READ minVFOScanFrequencyVHF WRITE setMinVFOScanFrequencyVHF)\n  /** The maximum VHF VFO-scan frequency in Hz. */\n  Q_PROPERTY(Frequency maxVFOScanFrequencyVHF READ maxVFOScanFrequencyVHF WRITE setMaxVFOScanFrequencyVHF)\n\n  Q_CLASSINFO(\"keepLastCallerDescription\", \"Keeps the last caller on channel switch\")\n  /** The keep-last-caller setting. */\n  Q_PROPERTY(bool keepLastCaller READ keepLastCallerEnabled WRITE enableKeepLastCaller)\n\n  Q_CLASSINFO(\"vfoStepDescription\", \"Specifies the VFO tuning steps in kHz.\")\n  /** The VFO tuning step-size in kHz. */\n  Q_PROPERTY(Frequency vfoStep READ vfoStep WRITE setVFOStep)\n\n  Q_CLASSINFO(\"steTypeDescription\", \"Specifies the STE (squelch tail elimination) type.\")\n  Q_CLASSINFO(\"steTypeLongDescription\", \"Can also be used to disable the STE entirely.\")\n  /** The STE type. */\n  Q_PROPERTY(STEType steType READ steType WRITE setSTEType)\n\n  Q_CLASSINFO(\"steFrequencyDescription\", \"Specifies the STE (squelch tail elimination) frequency in Hz.\")\n  /** The STE frequency in Hz. */\n  Q_PROPERTY(double steFrequency READ steFrequency WRITE setSTEFrequency)\n\n  Q_CLASSINFO(\"steDurationDescription\", \"Specifies the duration of the STE (squelch tail elimination) tone.\")\n  Q_CLASSINFO(\"steDurationLongDescription\", \"Valid values are usually in the range between 10 and 1000ms.\")\n  /** The STE duration in ms. */\n  Q_PROPERTY(Interval steDuration READ steDuration WRITE setSTEDuration)\n\n  Q_CLASSINFO(\"tbstFrequencyDescription\", \"Specifies the TBST frequency in Hz.\")\n  Q_CLASSINFO(\"tbstFrequencyDescription\", \"Should be one of 1000, 1450, 1750 and 2100 Hz. \"\n                                          \"D878UV(2), D578UV and DMR-6X2UV only.\")\n  /** The TBST frequency in Hz. */\n  Q_PROPERTY(Frequency tbstFrequency READ tbstFrequency WRITE setTBSTFrequency)\n\n  Q_CLASSINFO(\"proModeDescription\", \"Enables 'professional' mode. This limits the option reachable via the menu.\")\n  /** If @c true, the \"pro mode\" is enabled. */\n  Q_PROPERTY(bool proMode READ proModeEnabled WRITE enableProMode)\n\n  /** If @c true, the call-channel is maintained (whatever that means). */\n  Q_PROPERTY(bool maintainCallChannel READ maintainCallChannelEnabled WRITE enableMaintainCallChannel)\n\n  /** Specifies, what controls the fan. */\n  Q_PROPERTY(FanControl fan READ fan WRITE setFan);\n\n  /** The boot settings. */\n  Q_PROPERTY(AnytoneBootSettingsExtension* bootSettings READ bootSettings)\n  /** The power-save settings. */\n  Q_PROPERTY(AnytonePowerSaveSettingsExtension* powerSaveSettings READ powerSaveSettings)\n  /** The key settings. */\n  Q_PROPERTY(AnytoneKeySettingsExtension* keySettings READ keySettings)\n  /** The tone settings. */\n  Q_PROPERTY(AnytoneToneSettingsExtension* toneSettings READ toneSettings)\n  /** The display settings. */\n  Q_PROPERTY(AnytoneDisplaySettingsExtension* displaySettings READ displaySettings)\n  /** The audio settings. */\n  Q_PROPERTY(AnytoneAudioSettingsExtension* audioSettings READ audioSettings)\n  /** The menu settings. */\n  Q_PROPERTY(AnytoneMenuSettingsExtension* menuSettings READ menuSettings)\n  /** The auto-repeater settings. */\n  Q_PROPERTY(AnytoneAutoRepeaterSettingsExtension* autoRepeaterSettings READ autoRepeaterSettings)\n  /** The DMR settings. */\n  Q_PROPERTY(AnytoneDMRSettingsExtension* dmrSettings READ dmrSettings)\n  /** The GPS settings. */\n  Q_PROPERTY(AnytoneGPSSettingsExtension* gpsSettings READ gpsSettings)\n  /** The Roaming settings. */\n  Q_PROPERTY(AnytoneRoamingSettingsExtension* roamingSettings READ roamingSettings)\n  /** The Bluetooth settings. */\n  Q_PROPERTY(AnytoneBluetoothSettingsExtension* bluetoothSettings READ bluetoothSettings)\n\n  Q_CLASSINFO(\"simplexRepeaterSettingsDescription\",\n              \"Configuration for the DMR-6X2UV simplex-repeater feature.\")\n  /** The simplex-repeater settings. DMR-6X2UV only. */\n  Q_PROPERTY(AnytoneRepeaterSettingsExtension * repeaterSettings READ repeaterSettings)\n\n  Q_CLASSINFO(\"satelliteSettingsDescription\",\n              \"Some settings of for satellite mode.\")\n  Q_PROPERTY(AnytoneSatelliteSettingsExtension * satelliteSettings READ satelliteSettings)\n\npublic:\n  /** Encodes the possible VFO scan types. */\n  enum class VFOScanType {\n    Time = 0, Carrier = 1, Stop = 2\n  };\n  Q_ENUM(VFOScanType)\n\n  /** Possible VFO modes. */\n  enum class VFOMode {\n    Memory = 0, VFO = 1\n  };\n  Q_ENUM(VFOMode)\n\n  /** Possible VFOs. */\n  enum class VFO {\n    A = 0, B = 1\n  };\n  Q_ENUM(VFO)\n\n  /** All possible STE (squelch tail eliminate) types. */\n  enum class STEType {\n    Off = 0, Silent = 1, Deg120 = 2, Deg180 = 3, Deg240 = 4\n  };\n  Q_ENUM(STEType)\n\n  enum class FanControl {\n    PTT, Temperature, Both\n  };\n  Q_ENUM(FanControl)\n\n\npublic:\n  /** Constructor. */\n  Q_INVOKABLE explicit AnytoneSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** A reference to the boot settings. */\n  AnytoneBootSettingsExtension *bootSettings() const;\n  /** A reference to the power-save settings. */\n  AnytonePowerSaveSettingsExtension *powerSaveSettings() const;\n  /** A reference to the key settings. */\n  AnytoneKeySettingsExtension *keySettings() const;\n  /** A reference to the tone settings. */\n  AnytoneToneSettingsExtension *toneSettings() const;\n  /** A reference to the display settings. */\n  AnytoneDisplaySettingsExtension *displaySettings() const;\n  /** A reference to the audio settings. */\n  AnytoneAudioSettingsExtension *audioSettings() const;\n  /** A reference to the menu settings. */\n  AnytoneMenuSettingsExtension *menuSettings() const;\n  /** A reference to the auto-repeater settings. */\n  AnytoneAutoRepeaterSettingsExtension *autoRepeaterSettings() const;\n  /** A reference to the DMR settings. */\n  AnytoneDMRSettingsExtension *dmrSettings() const;\n  /** A reference to the GPS settings. */\n  AnytoneGPSSettingsExtension *gpsSettings() const;\n  /** A reference to the roaming settings. */\n  AnytoneRoamingSettingsExtension *roamingSettings() const;\n  /** A reference to the bluetooth settings. */\n  AnytoneBluetoothSettingsExtension *bluetoothSettings() const;\n  /** A reference to the simplex repeater settings. */\n  AnytoneRepeaterSettingsExtension *repeaterSettings() const;\n  /** A reference to the satellite settings. */\n  AnytoneSatelliteSettingsExtension *satelliteSettings() const;\n\n  /** Returns the VFO scan type. */\n  VFOScanType vfoScanType() const;\n  /** Sets the VFO scan type. */\n  void setVFOScanType(VFOScanType type);\n\n  /** Returns mode for VFO A. */\n  VFOMode modeA() const;\n  /** Sets the mode for VFO A. */\n  void setModeA(VFOMode mode);\n  /** Returns mode for VFO B. */\n  VFOMode modeB() const;\n  /** Sets the mode for VFO B. */\n  void setModeB(VFOMode mode);\n\n  /** Returns a reference to the current zone for VFO A. */\n  ZoneReference *zoneA();\n  /** Returns a reference to the current zone for VFO A. */\n  const ZoneReference *zoneA() const;\n  /** Returns a reference to the current zone for VFO B. */\n  ZoneReference *zoneB();\n  /** Returns a reference to the current zone for VFO B. */\n  const ZoneReference *zoneB() const;\n\n  /** Returns the selected VFO. */\n  VFO selectedVFO() const;\n  /** Sets the selected VFO. */\n  void setSelectedVFO(VFO vfo);\n\n  /** Returns @c true if the sub-channel is enabled. */\n  bool subChannelEnabled() const;\n  /** Enables/disables the sub-channel. */\n  void enableSubChannel(bool enable);\n\n  /** Returns the minimum VFO scan frequency for the UHF band in Hz. */\n  Frequency minVFOScanFrequencyUHF() const;\n  /** Sets the minimum VFO scan frequency for the UHF band in Hz. */\n  void setMinVFOScanFrequencyUHF(Frequency hz);\n  /** Returns the maximum VFO scan frequency for the UHF band in Hz. */\n  Frequency maxVFOScanFrequencyUHF() const;\n  /** Sets the maximum VFO scan frequency for the UHF band in Hz. */\n  void setMaxVFOScanFrequencyUHF(Frequency hz);\n\n  /** Returns the minimum VFO scan frequency for the VHF band in Hz. */\n  Frequency minVFOScanFrequencyVHF() const;\n  /** Sets the minimum VFO scan frequency for the VHF band in Hz. */\n  void setMinVFOScanFrequencyVHF(Frequency hz);\n  /** Returns the maximum VFO scan frequency for the VHF band in Hz. */\n  Frequency maxVFOScanFrequencyVHF() const;\n  /** Sets the maximum VFO scan frequency for the VHF band in Hz. */\n  void setMaxVFOScanFrequencyVHF(Frequency hz);\n\n  /** Returns @c true if the last caller is kept on channel switch. */\n  bool keepLastCallerEnabled() const;\n  /** Enables/disables keeping the last caller on channel switch. */\n  void enableKeepLastCaller(bool enable);\n\n  /** Returns the VFO tuning step in kHz. */\n  Frequency vfoStep() const;\n  /** Sets the VFO tuning step in kHz. */\n  void setVFOStep(Frequency step);\n\n  /** Returns the STE (squelch tail elimination) type. */\n  STEType steType() const;\n  /** Sets the STE (squelch tail elimination) type. */\n  void setSTEType(STEType type);\n  /** Returns the STE (squelch tail elimination) frequency in Hz.\n   * A frequency of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n  double steFrequency() const;\n  /** Sets the STE (squelch tail elimination) frequency in Hz.\n   * A frequency of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n  void setSTEFrequency(double freq);\n  /** Returns the STE duration in ms. */\n  Interval steDuration() const;\n  /** Sets the STE duration. */\n  void setSTEDuration(Interval intv);\n\n  /** Returns the TBST frequency in Hz. */\n  Frequency tbstFrequency() const;\n  /** Sets the TBST frequency in Hz. Should be one of 1000, 1450, 1750 and 2100 Hz. */\n  void setTBSTFrequency(Frequency Hz);\n\n  /** Returns @c true, if the \"pro mode\" is enabled. */\n  bool proModeEnabled() const;\n  /** Enables/disables the \"pro mode\". */\n  void enableProMode(bool enable);\n\n  /** Returns @c true if the call-channel is maintained. */\n  bool maintainCallChannelEnabled() const;\n  /** Enables/disables maintaining the call-channel. */\n  void enableMaintainCallChannel(bool enable);\n\n  /** Returns the fan control setting. */\n  FanControl fan() const;\n  /** Sets the fan control. */\n  void setFan(FanControl ctlr);\n\n  /** Returns power setting for satellite mode. */\n  //Channel::Power satPower();\n\nprotected:\n  /** The boot settings. */\n  AnytoneBootSettingsExtension *_bootSettings;\n  /** The power-save settings. */\n  AnytonePowerSaveSettingsExtension *_powerSaveSettings;\n  /** The key settings. */\n  AnytoneKeySettingsExtension *_keySettings;\n  /** The tone settings. */\n  AnytoneToneSettingsExtension *_toneSettings;\n  /** The display settings. */\n  AnytoneDisplaySettingsExtension *_displaySettings;\n  /** The audio settings. */\n  AnytoneAudioSettingsExtension *_audioSettings;\n  /** The menu settings. */\n  AnytoneMenuSettingsExtension *_menuSettings;\n  /** The auto-repeater settings. */\n  AnytoneAutoRepeaterSettingsExtension *_autoRepeaterSettings;\n  /** The DMR settings. */\n  AnytoneDMRSettingsExtension *_dmrSettings;\n  /** The GSP settings. */\n  AnytoneGPSSettingsExtension *_gpsSettings;\n  /** The roaming settings. */\n  AnytoneRoamingSettingsExtension *_roamingSettings;\n  /** The bluetooth settings. */\n  AnytoneBluetoothSettingsExtension *_bluetoothSettings;\n  /** The repeater settings. */\n  AnytoneRepeaterSettingsExtension *_repeaterSettings;\n  /** The satellite settings. */\n  AnytoneSatelliteSettingsExtension *_satelliteSettings;\n\n  VFOScanType _vfoScanType;        ///< The VFO scan-type property.\n  VFOMode _modeA;                  ///< Mode of VFO A.\n  VFOMode _modeB;                  ///< Mode of VFO B.\n  ZoneReference _zoneA;            ///< The current zone for VFO A.\n  ZoneReference _zoneB;            ///< The current zone for VFO B.\n  VFO _selectedVFO;                ///< The current VFO.\n  bool _subChannel;                ///< If @c true, the sub-channel is enabled.\n  Frequency _minVFOScanFrequencyUHF; ///< The minimum UHF VFO-scan frequency in Hz.\n  Frequency _maxVFOScanFrequencyUHF; ///< The maximum UHF VFO-scan frequency in Hz.\n  Frequency _minVFOScanFrequencyVHF; ///< The minimum VHF VFO-scan frequency in Hz.\n  Frequency _maxVFOScanFrequencyVHF; ///< The maximum VHF VFO-scan frequency in Hz.\n  bool _keepLastCaller;            ///< If @c true, the last caller is kept on channel switch.\n  Frequency _vfoStep;              ///< The VFO tuning step in kHz.\n  STEType _steType;                ///< The STE type.\n  double _steFrequency;            ///< STE frequency in Hz.\n  Interval _steDuration;           ///< STE duration\n  Frequency _tbstFrequency;        ///< The TBST frequency in Hz.\n  bool _proMode;                   ///< The \"pro mode\" flag.\n  bool _maintainCallChannel;       ///< Maintains the call channel.\n  FanControl _fan;\n};\n\n\n\n#endif // ANYTONE_SETTINGSEXTENSION_H\n"
  },
  {
    "path": "lib/auctus_a6_interface.cc",
    "content": "#include \"auctus_a6_interface.hh\"\n#include <QMetaEnum>\n#include <QtEndian>\n#include <QThread>\n#include \"logger.hh\"\n\n#define TIMEOUT 5000\n\nAuctusA6Interface::AuctusA6Interface(\n    const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : USBSerial(descriptor, QSerialPort::Baud9600, err, parent), _state(CLOSED)\n{\n  if (isOpen()) {\n    _state = IDLE;\n  } else {\n    errMsg(err) << \"Cannot construct Auctus A6 interface.\";\n    _state = ERROR;\n    return;\n  }\n  if (! clear(QSerialPort::AllDirections)) {\n    logWarn() << \"Cannot clear RX/TX buffer of serial interface.\";\n  }\n  logDebug() << \"Open interface to Auctus A6 based radio.\";\n}\n\nAuctusA6Interface::State\nAuctusA6Interface::state() const {\n  return _state;\n}\n\n\nbool\nAuctusA6Interface::send_receive(uint16_t command,\n                                const uint8_t *params, uint8_t plen,\n                                uint8_t *response, uint8_t &rlen, const ErrorStack &err)\n{\n  if (! send(command, params, plen, err)) {\n    errMsg(err) << \"Cannot send command.\";\n    return false;\n  }\n\n  uint16_t rcommand=0;\n  if (! receive(rcommand, response, rlen, err)) {\n    errMsg(err) << \"Cannot receive response.\";\n    return false;\n  }\n\n  if ((rcommand&0x7fff) != command) {\n    errMsg(err) << \"Request and response commands mismatch. Expected \"\n                << QString::number(command, 16) << \" got \"\n                << QString::number(command&0x7fff, 16) << \".\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nAuctusA6Interface::send(uint16_t command,\n                        const uint8_t *params, uint8_t plen, const ErrorStack &err)\n{\n  // check parameter length\n  if (plen>249) {\n    errMsg(err) << \"Parameter length cannot exceed 250 bytes.\";\n    errMsg(err) << \"Cannot send request \" << QString::number(command, 16) << \"h.\";\n    return false;\n  }\n\n  // assemble request\n  uint8_t buffer[255], *ptr=buffer;\n  uint8_t total_length = 6 + plen;\n  memset(buffer, 0, total_length);\n  // start of packet\n  *ptr = 0xaa; ptr += sizeof(uint8_t);\n  // length\n  *ptr = 6 + plen; ptr += sizeof(uint8_t); // total length\n  // command\n  (*(uint16_t *)(ptr)) = qToBigEndian(command); ptr += sizeof(uint16_t);\n  // copy parameters\n  memcpy(ptr, params, plen); ptr += plen;\n  // compute CRC\n  for (int i=0; i<(3+plen); i++)\n    (*ptr) ^= buffer[1+i]; // Skip start-of-packet byte.\n  ptr += sizeof(uint8_t);\n  // end of packet\n  *ptr = 0xbb;\n\n  // send request\n  logDebug() << \"Send \" << QByteArray((const char *)buffer, total_length).toHex();\n  if (total_length != QSerialPort::write((const char*)buffer, total_length)) {\n    errMsg(err) << \"QSerialPort: \" << errorString();\n    errMsg(err) << \"Cannot send request \" << QString::number(command, 16) << \"h.\";\n    return false;\n  }\n  if (! flush()) {\n    logWarn() << \"No data written to the device, \" << bytesToWrite()\n              << \" of \" << total_length << \" left in buffer.\";\n  }\n\n  return true;\n}\n\n\nbool\nAuctusA6Interface::receive(uint16_t &command,\n                           uint8_t *response, uint8_t &rlen, const ErrorStack &err)\n{\n  uint8_t buffer[255];\n\n  // read start-of-packet and length bytes\n  uint8_t total_length = 2;\n  if (! read(buffer, total_length, TIMEOUT, err)) {\n    errMsg(err) << \"Cannot read response \" << QString::number(command, 16) << \"h.\";\n    return false;\n  }\n\n  // check start of packet\n  if (0xaa != buffer[0]) {\n    errMsg(err) << \"Unexpected start-of-packet byte: Expected aah, got \"\n                << QString::number(buffer[0],16) << \"h.\";\n    return false;\n  }\n\n  // get length\n  total_length = buffer[1];\n  if (6 > total_length) {\n    errMsg(err) << \"Invalid packet: Expected minimum length of 6h, got \"\n                << QString::number(total_length, 16) << \"h.\";\n    return false;\n  }\n  // read remaining packet\n  if (! read(buffer+2, total_length-2, TIMEOUT, err)) {\n    errMsg(err) << \"Cannot read response \" << QString::number(command, 16) << \"h.\";\n    return false;\n  }\n\n  logDebug() << \"Got response \" << QByteArray((const char *)buffer, total_length).toHex() << \".\";\n\n  // unpack command\n  command = qFromBigEndian((*(uint16_t *)(buffer+2)));\n\n  // check CRC\n  uint8_t crc = 0;\n  for (int i=1; i<(total_length-1); i++)\n    crc ^= buffer[i];\n  if (crc) {\n    errMsg(err) << \"Invalid response: Invalid CRC.\";\n    return false;\n  }\n\n  // Check if response buffer is sufficiently large\n  if (rlen < (total_length-6)) {\n    errMsg(err) << \"Cannot store response in response buffer, buffer too small. \"\n                << \"Got \" << rlen << \"bytes, needs \" << (total_length-6) << \"bytes.\";\n    return false;\n  }\n\n  // copy response payload\n  rlen = total_length-6;\n  memcpy(response, buffer+4, rlen);\n\n  // done.\n  return true;\n}\n\nbool\nAuctusA6Interface::read(uint8_t *data, qint64 n, unsigned int timeout_ms, const ErrorStack &err)\n{\n  while (n > 0) {\n    if (0 == bytesAvailable()) {\n      if (! waitForReadyRead(timeout_ms)) {\n        errMsg(err) << \"QSerialPort: \" << errorString();\n        return false;\n      }\n    }\n\n    qint64 k = QSerialPort::read((char *)data, std::min(n, bytesAvailable()));\n    if (0 > k) {\n      errMsg(err) << \"QSerialPort: \" << errorString();\n      return false;\n    }\n\n    n -= k;\n    data += k;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/auctus_a6_interface.hh",
    "content": "/** @defgroup auctus Auctus A6/A7 Based devices.\n *\n * Several devices use the radio-on-a-chip Auctus A6, which enables very cheap DMR radios.\n *\n * @ingroup dsc */\n#ifndef AUCTUS_A6_INTERFACE_HH\n#define AUCTUS_A6_INTERFACE_HH\n\n#include <QObject>\n#include \"usbserial.hh\"\n\n/** Implements the communication interface to radios using the Auctus A6 chip.\n *\n * This includes devices like Cotre A1 or the Baofeng DR-1801. This class only implements\n * the basic communication protocol. Specifics, like particular commands are implemented in\n * derived interface classes.\n *\n * @ingroup auctus */\nclass AuctusA6Interface : public USBSerial\n{\n  Q_OBJECT\n\npublic:\n  /** Possible states of the interface. */\n  enum State {\n    CLOSED,        ///< Connection to device is closed.\n    IDLE,          ///< Connection is open and device is ready.\n    READ_THROUGH,  ///< Read from memory. The device just sends some amount of data.\n    WRITE_THROUGH, ///< Write to memory. The device just receives some amount of data.\n    ERROR          ///< An error state.\n  };\n\nprotected:\n  /** Hidden constructor. */\n  explicit AuctusA6Interface(const USBDeviceDescriptor &descriptor,\n                             const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\npublic:\n  /** Returns the interface state. */\n  State state() const;\n\nprotected:\n  /** Internal used method to send messages to and receive responses from radio. */\n  bool send_receive(uint16_t command, const uint8_t *params, uint8_t plen,\n                    uint8_t *response, uint8_t &rlen, const ErrorStack &err=ErrorStack());\n  /** Internal used method to send a command. */\n  bool send(uint16_t command, const uint8_t *params, uint8_t plen, const ErrorStack &err=ErrorStack());\n  /** Internal used method to receive a response. */\n  bool receive(uint16_t &command, uint8_t *response, uint8_t &rlen, const ErrorStack &err=ErrorStack());\n\n  /** Reads exactly @c n bytes or timeouts. */\n  bool read(uint8_t *data, qint64 n, unsigned int timeout_ms, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the state of the interface. */\n  State _state;\n};\n\n#endif // AUCTUS_A6_INTERFACE_HH\n"
  },
  {
    "path": "lib/audiosettings.cc",
    "content": "#include \"audiosettings.hh\"\n\n\nAudioSettings::AudioSettings(QObject *parent)\n  : ConfigItem(parent), _squelch(Level::fromValue(3)), _dmrSquelch(Level::invalid()),\n    _micGain(Level::fromValue(5)), _fmMicGain(Level::invalid()), _m17MicGain(Level::invalid()),\n    _maxSpeakerVolume(Level::fromValue(10)), _maxHeadphoneVolume(Level::fromValue(10)),\n    _vox(Level::null()), _voxDelay(Interval::null()), _speech(false)\n{\n  // pass...\n}\n\nConfigItem *\nAudioSettings::clone() const {\n  auto obj = new AudioSettings();\n  if (! obj->copy(*this)) {\n    delete obj;\n    return nullptr;\n  }\n  return obj;\n}\n\nLevel\nAudioSettings::squelch() const {\n  return _squelch;\n}\n\nvoid\nAudioSettings::setSquelch(Level squelch) {\n  if (squelch.isInvalid())\n    squelch = Level::fromValue(3);\n  // squelch = 0 -> open squelch\n  if (_squelch == squelch)\n    return;\n  _squelch = squelch;\n  emit modified(this);\n}\n\n\nbool\nAudioSettings::dmrSquelchEnabled() const {\n  return ! _dmrSquelch.isInvalid();\n}\n\nLevel\nAudioSettings::dmrSquelch() const {\n  return _dmrSquelch;\n}\n\nvoid\nAudioSettings::setDMRSquelch(Level dmrSquelch) {\n  if (_dmrSquelch == dmrSquelch)\n    return;\n  _dmrSquelch = dmrSquelch;\n  emit modified(this);\n}\n\nvoid\nAudioSettings::disableDMRSquelch() {\n  setDMRSquelch(Level::invalid());\n}\n\n\nLevel\nAudioSettings::micGain() const {\n  return _micGain;\n}\n\nvoid\nAudioSettings::setMicGain(Level micGain) {\n  if (! micGain.isFinite())\n    micGain = Level::fromValue(1);\n  if (_micGain == micGain)\n    return;\n  _micGain = micGain;\n  emit modified(this);\n}\n\n\nbool\nAudioSettings::fmMicGainEnabled() const {\n  return _fmMicGain.isFinite();\n}\n\nLevel\nAudioSettings::fmMicGain() const {\n  return _fmMicGain;\n}\n\nvoid\nAudioSettings::setFMMicGain(Level fmMicGain) {\n  if (_fmMicGain == fmMicGain)\n    return;\n  _fmMicGain = fmMicGain;\n  emit modified(this);\n}\n\nvoid\nAudioSettings::disableFMMicGain() {\n  setFMMicGain(Level::invalid());\n}\n\nbool\nAudioSettings::m17MicGainEnabled() const {\n  return _m17MicGain.isFinite();\n}\n\nLevel\nAudioSettings::m17MicGain() const {\n  return _m17MicGain;\n}\n\nvoid\nAudioSettings::setM17MicGain(Level m17MicGain) {\n  if (_m17MicGain == m17MicGain)\n    return;\n  _m17MicGain = m17MicGain;\n  emit modified(this);\n}\n\nvoid\nAudioSettings::disableM17MicGain() {\n  setM17MicGain(Level::invalid());\n}\n\n\nbool\nAudioSettings::voxEnabled() const {\n  return _vox.isFinite();\n}\n\nLevel\nAudioSettings::vox() const {\n  return _vox;\n}\n\nvoid\nAudioSettings::setVox(Level vox) {\n  if (vox.isInvalid())\n    vox = Level::null();\n  if (_vox == vox)\n    return;\n  _vox = vox;\n  emit modified(this);\n}\n\nvoid\nAudioSettings::disableVox() {\n  setVox(Level::null());\n}\n\n\nLevel\nAudioSettings::maxSpeakerVolume() const {\n  return _maxSpeakerVolume;\n}\n\nvoid\nAudioSettings::setMaxSpeakerVolume(Level maxSpeakerVolume) {\n  if (_maxSpeakerVolume == maxSpeakerVolume)\n    return;\n  _maxSpeakerVolume = maxSpeakerVolume;\n  emit modified(this);\n}\n\nLevel\nAudioSettings::maxHeadphoneVolume() const {\n  return _maxHeadphoneVolume;\n}\n\nvoid\nAudioSettings::setMaxHeadphoneVolume(Level maxHeadphoneVolume) {\n  if (_maxHeadphoneVolume == maxHeadphoneVolume)\n    return;\n  _maxHeadphoneVolume = maxHeadphoneVolume;\n  emit modified(this);\n}\n\n\nInterval\nAudioSettings::voxDelay() const {\n  return _voxDelay;\n}\n\nvoid\nAudioSettings::setVOXDelay(Interval voxDelay) {\n  if (_voxDelay == voxDelay)\n    return;\n  _voxDelay = voxDelay;\n  emit modified(this);\n}\n\nbool\nAudioSettings::speechSynthesisEnabled() const {\n  return _speech;\n}\n\nvoid\nAudioSettings::enableSpeechSynthesis(bool speechSynthesis) {\n  if (_speech == speechSynthesis)\n    return;\n  _speech = speechSynthesis;\n  emit modified(this);\n}"
  },
  {
    "path": "lib/audiosettings.hh",
    "content": "#ifndef AUDIOSETTINGS_HH\n#define AUDIOSETTINGS_HH\n\n#include \"configobject.hh\"\n#include \"level.hh\"\n#include \"interval.hh\"\n\n\n/** Collects common audio and tone settings */\nclass AudioSettings: public ConfigItem\n{\n  Q_OBJECT\n\n  Q_PROPERTY(Level squelch READ squelch WRITE setSquelch)\n  Q_PROPERTY(Level dmrSquelch READ dmrSquelch WRITE setDMRSquelch)\n  Q_PROPERTY(Level micGain READ micGain WRITE setMicGain)\n  Q_PROPERTY(Level fmMicGain READ fmMicGain WRITE setFMMicGain)\n  Q_PROPERTY(Level m17MicGain READ m17MicGain WRITE setM17MicGain)\n  Q_PROPERTY(Level maxSpeakerVolume READ maxSpeakerVolume WRITE setMaxSpeakerVolume)\n  Q_PROPERTY(Level maxHeadphoneVolume READ maxHeadphoneVolume WRITE setMaxHeadphoneVolume)\n  Q_PROPERTY(Level vox READ vox WRITE setVox)\n  Q_PROPERTY(Interval voxDelay READ voxDelay WRITE setVOXDelay)\n  Q_PROPERTY(bool speech READ speechSynthesisEnabled WRITE enableSpeechSynthesis)\n\npublic:\n  /** Default constructor. */\n  explicit AudioSettings(QObject *parent = nullptr);\n\n  ConfigItem *clone() const override;\n\n  /** Returns the default (FM) squelch. */\n  Level squelch() const;\n  /** Sets the default squelch. */\n  void setSquelch(Level squelch);\n\n  /** Returns @c true if the DMR squelch is set. */\n  bool dmrSquelchEnabled() const;\n  /** Returns the DMR squelch. */\n  Level dmrSquelch() const;\n  /** Set the DMR squelch. */\n  void setDMRSquelch(Level dmrSquelch);\n  /** Disables the DMR squelch setting. The global FM squelch is then used. */\n  void disableDMRSquelch();\n\n  /** Returns the default mic gain. */\n  Level micGain() const;\n  /** Sets the default mic gain. */\n  void setMicGain(Level micGain);\n\n  /** Returns @c true if the FM mic gain is enabled. */\n  bool fmMicGainEnabled() const;\n  /** Returns the FM mic gain. If null, the dmr mic gain is used. */\n  Level fmMicGain() const;\n  /** Sets the FM mic gain. If set to null, the global DMR mic gain is used. */\n  void setFMMicGain(Level fmMicGain);\n  /** Disables FM mic gain. */\n  void disableFMMicGain();\n\n  /** Returns @c true if the M17 mic gain is enabled. */\n  bool m17MicGainEnabled() const;\n  /** Returns the M17 mic gain. If null, the dmr mic gain is used. */\n  Level m17MicGain() const;\n  /** Sets the M17 mic gain. If set to null, the global DMR mic gain is used. */\n  void setM17MicGain(Level m17MicGain);\n  /** Disables M17 mic gain. */\n  void disableM17MicGain();\n\n  /** Returns @c true, if the VOX is enabled. */\n  bool voxEnabled() const;\n  /** Returns the VOX sensitivity. */\n  Level vox() const;\n  /** Sets the VOX sensitivity. */\n  void setVox(Level vox);\n  /** Disables VOX. */\n  void disableVox();\n\n  /** Returns the VOX delay. */\n  Interval voxDelay() const;\n  /** Sets the VOX delay. */\n  void setVOXDelay(Interval ms);\n\n  /** Returns the maximum speaker volume. */\n  Level maxSpeakerVolume() const;\n  /** Sets the maximum speaker volume. */\n  void setMaxSpeakerVolume(Level maxSpeakerVolume);\n  /** Returns the maximum headphone volume. */\n  Level maxHeadphoneVolume() const;\n  /** Sets the maximum headphone volume. */\n  void setMaxHeadphoneVolume(Level maxHeadphoneVolume);\n\n  /** Returns @c true if the speech synthesis is enabled. */\n  bool speechSynthesisEnabled() const;\n  /** Enable speech synthesis. */\n  void enableSpeechSynthesis(bool enabled);\n\nprotected:\n  /** Default (FM) squelch. */\n  Level _squelch;\n  /** DMR squelch setting. */\n  Level _dmrSquelch;\n  /** Default mic gain. */\n  Level _micGain;\n  /** The FM mic gain. */\n  Level _fmMicGain;\n  /** The M17 mic gain. */\n  Level _m17MicGain;\n  /** Maximum speaker volume. */\n  Level _maxSpeakerVolume;\n  /** Maximum headphone volume. */\n  Level _maxHeadphoneVolume;\n  /** Specifies the VOX level. */\n  Level _vox;\n  /** Specifies the VOX delay. */\n  Interval _voxDelay;\n  /** Enables speech synthesis. */\n  bool _speech;\n};\n\n\n\n#endif //QDMR_AUDIOSETTINGS_HH\n"
  },
  {
    "path": "lib/bootsettings.cc",
    "content": "#include \"bootsettings.hh\"\n\n\nBootSettings::BootSettings(QObject *parent)\n: ConfigExtension{parent}, _bootDisplay(BootDisplay::Logo), _message1(), _message2(),\n  _bootPasswordEnabled(false), _bootPassword(),\n  _defaultChannel(false),\n  _zoneA(new ZoneReference(this)), _channelA(new ChannelReference(this)),\n  _zoneB(new ZoneReference(this)), _channelB(new ChannelReference(this)),\n  _reset(true)\n{\n  // pass...\n}\n\n\nConfigItem *\nBootSettings::clone() const {\n  auto clone = new BootSettings();\n  if (! clone->copy(*this)) {\n    delete clone;\n    return nullptr;\n  }\n  return clone;\n}\n\n\nBootSettings::BootDisplay\nBootSettings::bootDisplay() const {\n  return _bootDisplay;\n}\n\nvoid\nBootSettings::setBootDisplay(BootDisplay mode) {\n  if (_bootDisplay == mode)\n    return;\n  _bootDisplay = mode;\n  emit modified(this);\n}\n\n\nconst QString &\nBootSettings::message1() const {\n  return _message1;\n}\n\nvoid\nBootSettings::setMessage1(const QString &message1) {\n  if (_message1 == message1)\n    return;\n  _message1 = message1;\n  emit modified(this);\n}\n\nconst QString &\nBootSettings::message2() const {\n  return _message2;\n}\n\nvoid\nBootSettings::setMessage2(const QString &message2) {\n  if (_message2 == message2)\n    return;\n  _message2 = message2;\n  emit modified(this);\n}\n\n\nbool\nBootSettings::bootPasswordEnabled() const {\n  return _bootPasswordEnabled;\n}\n\nvoid\nBootSettings::enableBootPassword(bool enable) {\n  if (_bootPasswordEnabled == enable)\n    return;\n  _bootPasswordEnabled = enable;\n  emit modified(this);\n}\n\n\nconst QString &\nBootSettings::bootPassword() const {\n  return _bootPassword;\n}\n\nvoid\nBootSettings::setBootPassword(const QString &pass) {\n  if (_bootPassword == pass)\n    return;\n  _bootPassword = pass;\n  emit modified(this);\n}\n\n\nbool\nBootSettings::defaultChannelEnabled() const {\n  return _defaultChannel;\n}\n\nvoid\nBootSettings::enableDefaultChannel(bool enable) {\n  if (_defaultChannel == enable)\n    return;\n  _defaultChannel = enable;\n  emit modified(this);\n}\n\n\nZoneReference *\nBootSettings::zoneA() const {\n  return _zoneA;\n}\n\nChannelReference *\nBootSettings::channelA() const {\n  return _channelA;\n}\n\n\nZoneReference *\nBootSettings::zoneB() const {\n  return _zoneB;\n}\n\nChannelReference *\nBootSettings::channelB() const {\n  return _channelB;\n}\n\n\nbool\nBootSettings::resetEnabled() const {\n  return _reset;\n}\n\nvoid\nBootSettings::enableReset(bool enable) {\n  if (_reset == enable)\n    return;\n  _reset = enable;\n  emit modified(this);\n}\n\n"
  },
  {
    "path": "lib/bootsettings.hh",
    "content": "//\n// Created by hannes on 02.04.26.\n//\n\n#ifndef BOOTSETTINGS_HH\n#define BOOTSETTINGS_HH\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n\n\n/** Collects all common boot settings across devices.\n * @ingroup conf */\nclass BootSettings: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The boot display setting. */\n  Q_PROPERTY(BootDisplay display READ bootDisplay WRITE setBootDisplay)\n  /** The first boot message line. */\n  Q_PROPERTY(QString message1 READ message1 WRITE setMessage1)\n  /** The second boot message line. */\n  Q_PROPERTY(QString message2 READ message2 WRITE setMessage2)\n  /** If @c true, the boot password is enabled. */\n  Q_PROPERTY(bool passwordEnabled READ bootPasswordEnabled WRITE enableBootPassword)\n  /** Holds the boot password. */\n  Q_PROPERTY(QString password READ bootPassword WRITE setBootPassword)\n\n  Q_CLASSINFO(\"defaultChannelDescription\", \"If enabled, the default channels are selected at boot.\")\n  /** If enabled, the default channels are selected at boot. */\n  Q_PROPERTY(bool defaultChannel READ defaultChannelEnabled WRITE enableDefaultChannel)\n  Q_CLASSINFO(\"zoneADescription\", \"The default zone for VFO A.\")\n  /** The default zone for VFO A. */\n  Q_PROPERTY(ZoneReference* zoneA READ zoneA)\n  Q_CLASSINFO(\"channelADescription\", \"The default channel for VFO A. Must be within zone A.\")\n  /** The default channel for VFO A. */\n  Q_PROPERTY(ChannelReference* channelA READ channelA)\n  Q_CLASSINFO(\"zoneBDescription\", \"The default zone for VFO B.\")\n  /** The current zone for VFO B. */\n  Q_PROPERTY(ZoneReference* zoneB READ zoneB)\n  Q_CLASSINFO(\"channelBDescription\", \"The default channel for VFO B. Must be within zone B.\")\n  /** The default channel for VFO B. */\n  Q_PROPERTY(ChannelReference* channelB READ channelB)\n\n  /** Allows a factory reset during boot. Sometimes needed to enter FW programming mode. */\n  Q_PROPERTY(bool allowFactoryReset READ resetEnabled WRITE enableReset)\n\npublic:\n  /** What to display during boot. */\n  enum class BootDisplay {\n    Logo, ///< Default or build in logo.\n    Text, ///< Custom text.\n    Image ///< Custom image.\n  };\n  Q_ENUM(BootDisplay)\n\npublic:\n  /** Default constructor. */\n  explicit BootSettings(QObject *parent=nullptr);\n\n  ConfigItem *clone() const override;\n\n  /** Returns the boot display setting. */\n  BootDisplay bootDisplay() const;\n  /** Sets the boot display. */\n  void setBootDisplay(BootDisplay mode);\n\n  /** Returns the first boot message line. */\n  const QString &message1() const;\n  /** Sets the first boot message line. */\n  void setMessage1(const QString &value);\n\n  /** Returns the second boot message line. */\n  const QString &message2() const;\n  /** Sets the second boot message line. */\n  void setMessage2(const QString &value);\n\n  /** Returns @c true if the boot password is enabled.*/\n  bool bootPasswordEnabled() const;\n  /** Enables the boot password. */\n  void enableBootPassword(bool enable);\n  /** Returns the boot password. */\n  const QString &bootPassword() const;\n  /** Sets the boot password. */\n  void setBootPassword(const QString &pass);\n\n  /** If @c true, the radio switches to the default channel at boot. */\n  bool defaultChannelEnabled() const;\n  /** Enables/disables boot default channel. */\n  void enableDefaultChannel(bool enable);\n  /** Returns a reference to the default zone for VFO A. */\n  ZoneReference *zoneA() const;\n  /** Returns a reference to the default channel for VFO A. */\n  ChannelReference *channelA() const;\n  /** Returns a reference to the default zone for VFO B. */\n  ZoneReference *zoneB() const;\n  /** Returns a reference to the default channel for VFO B. */\n  ChannelReference *channelB() const;\n\n  /** Returns @c true if the MCU is reset on boot. */\n  bool resetEnabled() const;\n  /** Enables/disables MCU reset on boot. */\n  void enableReset(bool enable);\n\nprotected:\n  BootDisplay _bootDisplay;        ///< The boot display property.\n  QString _message1, _message2;    ///< The two boot messages.\n  bool _bootPasswordEnabled;       ///< If true, the boot password is enabled.\n  QString _bootPassword;           ///< The boot password\n  bool _defaultChannel;            ///< Change to the default channel on boot.\n  ZoneReference *_zoneA;           ///< Default zone for VFO A.\n  ChannelReference *_channelA;     ///< Default channel for VFO A, must be member of zone for VFO A.\n  ZoneReference *_zoneB;           ///< Default zone for VFO B.\n  ChannelReference *_channelB;     ///< Default channel for VFO B, must be member of zone for VFO B.\n  bool _reset;                     ///< Enables MCU reset on boot.\n};\n\n\n#endif //BOOTSETTINGS_HH\n"
  },
  {
    "path": "lib/c7000device.cc",
    "content": "#include \"c7000device.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n#include <QThread>\n\n#define C7000_VID 0x1206\n#define C7000_PID 0x0227\n\n\n/* ********************************************************************************************* *\n * Implementation of C7000Device::Packet\n * ********************************************************************************************* */\nC7000Device::Packet::Packet()\n  : _encoded()\n{\n  // pass...\n}\n\nC7000Device::Packet::Packet(uint8_t command, uint8_t sub, uint8_t flags, const QByteArray &payload)\n{\n  _encoded.resize(9 + payload.size()); _encoded.fill(0);\n  _encoded[0] = 0x68;\n  _encoded[1] = flags;\n  _encoded[2] = command;\n  _encoded[3] = sub;\n  *((uint16_t *)(_encoded.data()+6)) = qToLittleEndian((uint16_t)payload.size());\n  memcpy(_encoded.data()+8,payload.constData(), payload.size());\n  _encoded[8+payload.size()] = 0x10;\n\n  uint32_t crc = 0xffff;\n  for (int i=0; i<(_encoded.size()/2); i++) {\n    uint16_t v = qFromLittleEndian(*(uint16_t *)(_encoded.constData()+2*i));\n    if (crc < v) crc += 0xffff;\n    crc -= v;\n  }\n  if (_encoded.size()%2) {\n    uint16_t v = _encoded[_encoded.size()-1];\n    if (crc < v) crc += 0xffff;\n    crc -= v;\n  }\n  *((uint16_t *)(_encoded.data()+4)) = qToLittleEndian((uint16_t)crc);\n}\n\nC7000Device::Packet::Packet(const QByteArray &buffer)\n  : _encoded()\n{\n  _encoded.append(buffer);\n  if (! isValid())\n    _encoded.clear();\n}\n\n\nbool\nC7000Device::Packet::isValid() const {\n  if (_encoded.size() < 9) return false;\n  if (0x68 != _encoded[0]) return false;\n  if (0x10 != _encoded[8+payloadSize()]) return false;\n  int size = _encoded.size(); //9 + payloadSize();\n\n  uint32_t crc = 0xffff;\n  for (int i=0; i<size; i++) {\n    uint16_t v = qFromLittleEndian(*(uint16_t *)(_encoded.constData()+2*i));\n    if (crc < v) crc += 0xffff;\n    crc -= v;\n  }\n  if (size%2) {\n    uint16_t v = _encoded[_encoded.size()-1];\n    if (crc < v) crc += 0xffff;\n    crc -= v;\n  }\n  /*if (crc)\n    return false;*/\n\n  return true;\n}\n\nuint8_t\nC7000Device::Packet::flags() const {\n  return _encoded[1];\n}\n\nuint8_t\nC7000Device::Packet::command() const {\n  return _encoded[2];\n}\n\nuint8_t\nC7000Device::Packet::subcommand() const {\n  return _encoded[3];\n}\n\nuint16_t\nC7000Device::Packet::payloadSize() const {\n  return qFromLittleEndian(*(const uint16_t *)(_encoded.constData()+6));\n}\n\nQByteArray\nC7000Device::Packet::payload() const {\n  return _encoded.mid(8, payloadSize());\n}\n\nconst QByteArray &\nC7000Device::Packet::encoded() const {\n  return _encoded;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of C7000Device::Descriptor\n * ********************************************************************************************* */\nC7000Device::Descriptor::Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device)\n  : USBDeviceDescriptor(info, USBDeviceHandle(bus, device))\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of C7000Device\n * ********************************************************************************************* */\nC7000Device::C7000Device(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : QObject(parent), _ctx(nullptr), _dev(nullptr)\n{\n  if (USBDeviceInfo::Class::C7K != descr.interfaceClass()) {\n    errMsg(err) << \"Cannot connect to C7000 device using a non C7K descriptor: \"\n                << descr.description() << \".\";\n    return;\n  }\n\n  int error = libusb_init(&_ctx);\n  if (error < 0) {\n    errMsg(err) << \"Libusb init failed (\" << error << \"): \"\n                << libusb_strerror((enum libusb_error) error) << \".\";\n    return;\n  }\n\n  int num=0;\n  libusb_device **lst;\n  libusb_device *dev=nullptr;\n  if (0 > (num = libusb_get_device_list(_ctx, &lst))) {\n    errMsg(err) << \"Cannot obtain list of USB devices.\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  logDebug() << \"Try to detect USB C7000 interface \" << descr.description() << \".\";\n  USBDeviceHandle addr = descr.device().value<USBDeviceHandle>();\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    if (addr.bus != libusb_get_bus_number(lst[i]))\n      continue;\n    if (addr.device != libusb_get_device_address(lst[i]))\n      continue;\n    libusb_device_descriptor usb_descr;\n    if (0 > libusb_get_device_descriptor(lst[i],&usb_descr))\n      continue;\n    if (descr.vendorId() != usb_descr.idVendor)\n      continue;\n    if (descr.productId() != usb_descr.idProduct)\n      continue;\n    logDebug() << \"Matching device found at bus \" << addr.bus << \", device \" << addr.device\n               << \" with vendor ID \" << QString::number(usb_descr.idVendor, 16)\n               << \" and product ID \" << QString::number(usb_descr.idProduct, 16) << \".\";\n    libusb_ref_device(lst[i]); dev = lst[i];\n  }\n  // Unref all devices and free list, matching device was referenced earlier\n  libusb_free_device_list(lst, 1);\n\n  if (nullptr == dev) {\n    errMsg(err) << \"No matching device found: \" << descr.description() << \".\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  if (0 > (error = libusb_open(dev, &_dev))) {\n    errMsg(err) << \"Cannot open device \" << descr.description()\n                << \": \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_unref_device(dev);\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n\n  if (libusb_kernel_driver_active(_dev, 0) && libusb_detach_kernel_driver(_dev, 0)) {\n    errMsg(err) << \"Cannot detach kernel driver for device \" << descr.description()\n                << \". Interface claim will likely fail.\";\n  }\n\n  if (0 > (error = libusb_claim_interface(_dev, 0))) {\n    errMsg(err) << \"Failed to claim USB interface \"  << descr.description()\n                << \": \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_close(_dev);\n    _dev = nullptr;\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  logDebug() << \"Connected to C7000 device \" << descr.description() << \".\";\n}\n\nC7000Device::~C7000Device() {\n  close();\n}\n\nUSBDeviceInfo\nC7000Device::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::C7K, C7000_VID, C7000_PID);\n}\n\n\nQList<USBDeviceDescriptor>\nC7000Device::detect(bool saveOnly)\n{\n  Q_UNUSED(saveOnly)\n  QList<USBDeviceDescriptor> res;\n\n  int error, num;\n  libusb_context *ctx;\n  if (0 > (error = libusb_init(&ctx))) {\n    logError() << \"Libusb init failed (\" << error << \"): \"\n               << libusb_strerror((enum libusb_error) error) << \".\";\n    return res;\n  }\n\n  libusb_device **lst;\n  if (0 == (num = libusb_get_device_list(ctx, &lst))) {\n    logDebug() << \"No USB devices found at all.\";\n    // unref devices and free list\n    libusb_free_device_list(lst, 1);\n    return res;\n  }\n\n  logDebug() << \"Search for C7000 devices matching VID:PID \"\n             << QString::number(C7000_VID, 16) << \":\" << QString::number(C7000_PID, 16) << \".\";\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    libusb_device_descriptor descr;\n    libusb_get_device_descriptor(lst[i], &descr);\n    if ((C7000_VID == descr.idVendor) && (C7000_PID == descr.idProduct)) {\n      logDebug() << \"Found device on bus=\" << libusb_get_bus_number(lst[i])\n                 << \", device=\" << libusb_get_device_address(lst[i])\n                 << \" with \" << QString::number(C7000_VID, 16) << \":\" << QString::number(C7000_PID, 16) << \".\";\n      res.append(C7000Device::Descriptor(\n                   USBDeviceInfo(USBDeviceInfo::Class::C7K, C7000_VID, C7000_PID),\n                   libusb_get_bus_number(lst[i]),\n                   libusb_get_device_address(lst[i])));\n    }\n  }\n\n  libusb_free_device_list(lst, 1);\n  return res;\n}\n\nbool\nC7000Device::isOpen() const {\n  return nullptr != _dev;\n}\n\nvoid\nC7000Device::close() {\n  logDebug() << \"Close C7000 interface.\";\n  if (nullptr != _dev) {\n    libusb_release_interface(_dev, 0);\n    libusb_close(_dev);\n  }\n  if (nullptr != _ctx)\n    libusb_exit(_ctx);\n  _ctx = nullptr;\n  _dev = nullptr;\n}\n\nbool\nC7000Device::sendRecv(const Packet &request, Packet &response, const ErrorStack &err) {\n  if (! request.isValid()) {\n    errMsg(err) << \"Cannot send invalid request.\";\n    return false;\n  }\n\n  uint8_t buffer[64];\n  int bytes_send, bytes_received;\n\n  memcpy(buffer, request.encoded().constData(), request.encoded().size());\n  int ret = libusb_bulk_transfer(_dev, 0x02, buffer, request.encoded().size(), &bytes_send, 1000);\n  if (ret) {\n    errMsg(err) << \"Cannot send command to device: \" << libusb_error_name(ret) << \".\";\n    return false;\n  }\n  QObject().thread()->usleep(1000);\n\n  unsigned int retry_count = 0;\nretry_receive:\n  ret = libusb_bulk_transfer(_dev, 0x81, buffer, 64, &bytes_received, 1000);\n  if (ret) {\n    errMsg(err) << \"Cannot receive response from device: \" << libusb_error_name(ret) << \".\";\n    return false;\n  }\n\n  if (0x68 != buffer[0]) {\n    if ((++retry_count) > 10) {\n      errMsg(err) << \"Cannot receive response from device: Retry count of 10 exceeded.\";\n      return false;\n    }\n    goto retry_receive;\n  }\n\n  response = Packet(QByteArray((const char *)buffer, bytes_received));\n  if (! response.isValid()) {\n    errMsg(err) << \"Invalid response received.\";\n    return false;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/c7000device.hh",
    "content": "#ifndef C7000DEVICE_HH\n#define C7000DEVICE_HH\n\n#include <QObject>\n#include <libusb.h>\n#include \"errorstack.hh\"\n#include \"radiointerface.hh\"\n\n/** Base class for all C7000 based radios. This class implements the basic communication protocol\n * to these devices.\n * @ingroup rif */\nclass C7000Device : public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** Request/response packet. */\n  struct Packet {\n  public:\n    /** Default constructor. */\n    Packet();\n    /** Copy constructor. */\n    Packet(const Packet &other)  = default ;\n    /** Constructs a request/response from commands and payload. */\n    Packet(uint8_t command, uint8_t sub, uint8_t flags=0x0f, const QByteArray &payload=QByteArray());\n    /** Constructs a request/response from the given encoded packet. */\n    Packet(const QByteArray &buffer);\n\n    /** Assignment. */\n    Packet &operator =(const Packet &other) = default;\n\n    /** Returns @c true, if the packet is valid. */\n    bool isValid() const;\n\n    /** Returns the flags field. */\n    uint8_t flags() const;\n    /** Returns the command field. */\n    uint8_t command() const;\n    /** Returns the sub-command field. */\n    uint8_t subcommand() const;\n    /** Returns the payload size. */\n    uint16_t payloadSize() const;\n    /** Returns the payload field. */\n    QByteArray payload() const;\n    /** Returns the encoded packet. */\n    const QByteArray &encoded() const;\n\n  protected:\n    /** Holds the encoded packet. */\n    QByteArray _encoded;\n  };\n\npublic:\n  /** Specialization to address a DFU device uniquely. */\n  class Descriptor: public USBDeviceDescriptor\n  {\n  public:\n    /** Constructor from interface info, bus number and device address. */\n    Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device);\n  };\n\npublic:\n  /** Opens a connection to the C7000 device. */\n  C7000Device(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~C7000Device();\n\n  /** Returns @c true if the C7000 interface is open. */\n  bool isOpen() const;\n  /** Closes the C7000 interface. */\n  void close();\n\npublic:\n  /** Returns some information about the interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Finds all C7000 interfaces. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Sends the given request to the device and receives the response. */\n  bool sendRecv(const Packet &request, Packet &response, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** USB context. */\n  libusb_context *_ctx;\n  /** USB device object. */\n  libusb_device_handle *_dev;\n};\n\n#endif // C7000DEVICE_HH\n"
  },
  {
    "path": "lib/callsigndb.cc",
    "content": "#include \"callsigndb.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of CallsignDB::Flags\n * ********************************************************************************************* */\nCallsignDB::Flags::Flags(int64_t count)\n  : TransferFlags(), _count(count)\n{\n  // pass...\n}\n\nCallsignDB::Flags::Flags(const Flags &other)\n  : TransferFlags(other), _count(other._count)\n{\n  // pass...\n}\n\nbool\nCallsignDB::Flags::hasCountLimit() const {\n  return (0 <= _count);\n}\n\nsize_t\nCallsignDB::Flags::countLimit() const {\n  if (0 > _count)\n    return std::numeric_limits<size_t>::max();\n  return _count;\n}\n\nvoid\nCallsignDB::Flags::setCountLimit(size_t n) {\n  _count = n;\n}\n\nvoid\nCallsignDB::Flags::clearCountLimit() {\n  _count = -1;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of CallsignDB\n * ********************************************************************************************* */\nCallsignDB::CallsignDB(QObject *parent)\n  : DFUFile(parent)\n{\n  // pass...\n}\n\nCallsignDB::~CallsignDB() {\n  // pass...\n}\n"
  },
  {
    "path": "lib/callsigndb.hh",
    "content": "#ifndef CALLSIGNDB_HH\n#define CALLSIGNDB_HH\n\n#include \"dfufile.hh\"\n#include \"transferflags.hh\"\n\n\n// Forward decl.\nclass UserDatabase;\n\n/** Abstract base class of all callsign database implementations.\n * This class defines the interface for all device-specific binary encodings of call sign\n * databases. The interface is particularly simple: reimplement the @c encode method.\n * @ingroup conf */\nclass CallsignDB : public DFUFile\n{\n  Q_OBJECT\n\npublic:\n  /** Controls the selection of callsigns from the @c UserDatabase to be encoded into the\n   * callsign db. */\n  class Flags: public TransferFlags {\n  public:\n    /** Constructor. */\n    Flags(int64_t count=-1);\n    /** Copy constructor. */\n    Flags(const Flags &other);\n\n    /** Returns @c true if the selection has a limit on the number of callsigns to encode. */\n    bool hasCountLimit() const;\n    /** Returns the limit of callsigns to encode. */\n    size_t countLimit() const;\n    /** Sets the count limit. */\n    void setCountLimit(size_t n);\n    /** Clears the count limit. */\n    void clearCountLimit();\n\n  protected:\n    /** Specifies the maximum amount of callsigns to add. If negative, the device limit should be\n     * used. */\n    int64_t _count;\n  };\n\nprotected:\n  /** Hidden constructor. */\n  explicit CallsignDB(QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~CallsignDB();\n\n  /** Encodes the given user db into the device specific callsign db. */\n  virtual bool encode(UserDatabase *db, const Flags &selection=Flags(),\n                      const ErrorStack &err=ErrorStack()) = 0;\n};\n\n#endif // CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/channel.cc",
    "content": "#include \"channel.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"scanlist.hh\"\n#include \"logger.hh\"\n#include \"radioid.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include <cmath>\n#include <QAbstractProxyModel>\n#include <QMetaEnum>\n#include <QRegularExpression>\n\n#include \"opengd77_extension.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of Channel\n * ********************************************************************************************* */\nChannel::Channel(QObject *parent)\n  : ConfigObject(parent), _rxFreq(Frequency::fromHz(0)), _txFreq(Frequency::fromHz(0)),\n    _defaultPower(true), _power(Power::Low), _txTimeOut(Interval::null()),\n    _rxOnly(false), _vox(), _scanlist(),\n    _openGD77ChannelExtension(nullptr),\n    _tytChannelExtension(nullptr)\n{\n  Context::setTag(staticMetaObject.className(), \"timeout\",\n                  \"!default\", QVariant::fromValue(Interval::null()));\n  Context::setTag(staticMetaObject.className(), \"vox\",\n                  \"!default\", QVariant::fromValue(Level::invalid()));\n\n  // Link scan list modification event (e.g., scan list gets deleted).\n  connect(&_scanlist, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nChannel::Channel(const Channel &other, QObject *parent)\n  : ConfigObject(parent), _scanlist(), _openGD77ChannelExtension(nullptr),\n    _tytChannelExtension(nullptr)\n{\n  Channel::copy(other);\n\n  // Link scan list modification event (e.g., scan list gets deleted).\n  connect(&_scanlist, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nbool\nChannel::copy(const ConfigItem &other) {\n  const Channel *c = other.as<Channel>();\n  if ((nullptr == c) || (! ConfigObject::copy(other)))\n    return false;\n\n  if (c->defaultPower())\n    setDefaultPower();\n  if (c->defaultTimeout())\n    setDefaultTimeout();\n  if (c->defaultVOX())\n    setVOXDefault();\n\n  return true;\n}\n\nvoid\nChannel::clear() {\n  ConfigObject::clear();\n\n  setRXFrequency(Frequency::fromHz(0));\n  setTXFrequency(Frequency::fromHz(0));\n  setDefaultPower();\n  setDefaultTimeout();\n  setRXOnly(false);\n  setVOXDefault();\n\n  _scanlist.clear();\n\n  setOpenGD77ChannelExtension(nullptr);\n  setTyTChannelExtension(nullptr);\n}\n\n\nFrequency Channel::rxFrequency() const {\n  return _rxFreq;\n}\n\nbool\nChannel::setRXFrequency(Frequency freq) {\n  if (freq == _rxFreq)\n    return true;\n\n  _rxFreq = freq;\n  emit modified(this);\n\n  return true;\n}\n\nFrequency\nChannel::txFrequency() const {\n  return _txFreq;\n}\n\nbool\nChannel::setTXFrequency(Frequency freq) {\n  if (freq == _txFreq)\n    return true;\n\n  _txFreq = freq;\n  emit modified(this);\n\n  return true;\n}\n\nFrequencyOffset\nChannel::offsetFrequency() const {\n\n    return _txFreq - _rxFreq;\n}\n\nChannel::OffsetShift\nChannel::offsetShift() const {\n    if (_rxFreq > _txFreq) {\n        return OffsetShift::Negative;\n    } else if (_txFreq > _rxFreq) {\n        return OffsetShift::Positive;\n    } else {\n        return OffsetShift::None;\n    }\n}\n\nbool\nChannel::defaultPower() const {\n  return _defaultPower;\n}\n\nChannel::Power\nChannel::power() const {\n  return _power;\n}\n\nvoid\nChannel::setPower(Power power) {\n  if ((power == _power) && (! _defaultPower))\n    return;\n  _power = power;\n  _defaultPower = false;\n  emit modified(this);\n}\n\nvoid\nChannel::setDefaultPower() {\n  if (defaultPower())\n    return;\n\n  _defaultPower = true;\n  emit modified(this);\n}\n\n\nbool\nChannel::defaultTimeout() const {\n  return timeout().isNull();\n}\n\nbool\nChannel::timeoutDisabled() const {\n  return timeout().isInfinite();\n}\n\nInterval\nChannel::timeout() const {\n  return _txTimeOut;\n}\n\nbool\nChannel::setTimeout(const Interval& dur) {\n  if (dur == _txTimeOut)\n    return true;\n\n  _txTimeOut = dur;\n  emit modified(this);\n\n  return true;\n}\n\nvoid\nChannel::setDefaultTimeout() {\n  if (defaultTimeout())\n    return;\n\n  setTimeout(Interval::null());\n  emit modified(this);\n}\n\nvoid\nChannel::disableTimeout() {\n  setTimeout(Interval::infinity());\n}\n\n\nbool\nChannel::rxOnly() const {\n  return _rxOnly;\n}\nbool\nChannel::setRXOnly(bool enable) {\n  _rxOnly = enable;\n  emit modified(this);\n  return true;\n}\n\nbool\nChannel::voxDisabled() const {\n  return _vox.isNull();\n}\nbool\nChannel::defaultVOX() const {\n  return _vox.isInvalid();\n}\nLevel\nChannel::vox() const {\n  return _vox;\n}\nvoid\nChannel::setVOX(Level level) {\n  if (_vox == level)\n    return;\n  _vox = level;\n  emit modified(this);\n}\nvoid\nChannel::setVOXDefault() {\n  setVOX(Level::invalid());\n}\nvoid\nChannel::disableVOX() {\n  setVOX(Level::null());\n}\n\nconst ScanListReference *\nChannel::scanListRef() const {\n  return &_scanlist;\n}\n\nScanListReference *\nChannel::scanListRef() {\n  return &_scanlist;\n}\n\nScanList *\nChannel::scanList() const {\n  return _scanlist.as<ScanList>();\n}\nbool\nChannel::setScanList(ScanList *list) {\n  return _scanlist.set(list);\n}\n\nvoid\nChannel::onReferenceModified() {\n  emit modified(this);\n}\n\nOpenGD77ChannelExtension *\nChannel::openGD77ChannelExtension() const {\n  return _openGD77ChannelExtension;\n}\nvoid\nChannel::setOpenGD77ChannelExtension(OpenGD77ChannelExtension *ext) {\n  if (_openGD77ChannelExtension == ext)\n    return;\n  if (_openGD77ChannelExtension)\n    _openGD77ChannelExtension->deleteLater();\n  _openGD77ChannelExtension = ext;\n  if (_openGD77ChannelExtension) {\n    _openGD77ChannelExtension->setParent(this);\n    connect(_openGD77ChannelExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified()));\n  }\n}\n\nTyTChannelExtension *\nChannel::tytChannelExtension() const {\n  return _tytChannelExtension;\n}\nvoid\nChannel::setTyTChannelExtension(TyTChannelExtension *ext) {\n  if (_tytChannelExtension == ext)\n    return;\n  if (_tytChannelExtension)\n    _tytChannelExtension->deleteLater();\n  _tytChannelExtension = ext;\n  if (_tytChannelExtension) {\n    _tytChannelExtension->setParent(this);\n    connect(_tytChannelExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified()));\n  }\n}\n\nbool\nChannel::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  if (! ConfigObject::populate(node, context, err))\n    return false;\n\n  if (defaultPower()) {\n    YAML::Node def = YAML::Node(YAML::NodeType::Scalar); def.SetTag(\"!default\");\n    node[\"power\"] = def;\n  } else {\n    QMetaEnum metaEnum = QMetaEnum::fromType<Power>();\n    node[\"power\"] = metaEnum.valueToKey((unsigned)power());\n  }\n\n  return true;\n}\n\nbool\nChannel::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node) {\n    errMsg(err) << \"YAML node is null!\";\n    return false;\n  }\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse channel: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node ch = node.begin()->second;\n  if ((!ch[\"power\"]) || (\"!default\" == ch[\"power\"].Tag())) {\n    setDefaultPower();\n  } else if (ch[\"power\"] && ch[\"power\"].IsScalar()) {\n    QMetaEnum meta = QMetaEnum::fromType<Channel::Power>();\n    setPower((Channel::Power)meta.keyToValue(ch[\"power\"].as<std::string>().c_str()));\n  }\n\n  return ConfigObject::parse(ch, ctx, err);\n}\n\nbool\nChannel::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot link channel: Expected object with one child.\";\n    return false;\n  }\n\n  return ConfigObject::link(node.begin()->second, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnalogChannel\n * ********************************************************************************************* */\nAnalogChannel::AnalogChannel(QObject *parent)\n  : Channel(parent), _squelch(Level::invalid())\n{\n  Context::setTag(staticMetaObject.className(), \"squelch\",\n                  \"!default\", QVariant::fromValue(Level::invalid()));\n}\n\nAnalogChannel::AnalogChannel(const AnalogChannel &other, QObject *parent)\n  : Channel(other, parent), _squelch(Level::invalid())\n{\n  Context::setTag(staticMetaObject.className(), \"squelch\",\n                  \"!default\", QVariant::fromValue(Level::invalid()));\n}\n\nbool\nAnalogChannel::defaultSquelch() const {\n  return _squelch.isInvalid();\n}\nbool\nAnalogChannel::squelchDisabled() const {\n  return _squelch.isNull();\n}\nLevel\nAnalogChannel::squelch() const {\n  return _squelch;\n}\nbool\nAnalogChannel::setSquelch(Level level) {\n  if (_squelch == level)\n    return true;\n  _squelch = level;\n  emit modified(this);\n  return true;\n}\nvoid\nAnalogChannel::disableSquelch() {\n  setSquelch(Level::null());\n}\nvoid\nAnalogChannel::setSquelchDefault() {\n  setSquelch(Level::invalid());\n}\n\n\n\n\n/* ********************************************************************************************* *\n * Implementation of FMChannel\n * ********************************************************************************************* */\nFMChannel::FMChannel(QObject *parent)\n  : AnalogChannel(parent),\n    _admit(Admit::Always), _rxTone(), _txTone(), _bw(Bandwidth::Narrow),\n    _aprsSystem(), _extended(new FMChannelExtension(this)), _anytoneExtension(nullptr)\n{\n  // Link APRS system reference\n  connect(&_aprsSystem, &FMAPRSSystemReference::modified, this, &FMChannel::onReferenceModified);\n  // Link extended settings\n  connect(_extended, &FMChannelExtension::modified, this, &FMChannel::modified);\n}\n\nbool\nFMChannel::copy(const ConfigItem &other) {\n  const FMChannel *c = other.as<FMChannel>();\n  if ((nullptr==c) || (! AnalogChannel::copy(other)))\n    return false;\n\n  setRXTone(c->rxTone());\n  setTXTone(c->txTone());\n\n  return true;\n}\n\nConfigItem *\nFMChannel::clone() const {\n  FMChannel *c = new FMChannel();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nvoid\nFMChannel::clear() {\n  AnalogChannel::clear();\n  setAdmit(Admit::Always);\n  setSquelchDefault();\n  setRXTone(SelectiveCall());\n  setTXTone(SelectiveCall());\n  setBandwidth(Bandwidth::Narrow);\n  setAPRS(nullptr);\n  setAnytoneChannelExtension(nullptr);\n}\n\nFMChannel::Admit\nFMChannel::admit() const {\n  return _admit;\n}\nvoid\nFMChannel::setAdmit(Admit admit) {\n  _admit = admit;\n  emit modified(this);\n}\n\nSelectiveCall\nFMChannel::rxTone() const {\n  return _rxTone;\n}\nbool\nFMChannel::setRXTone(SelectiveCall code) {\n  _rxTone = code;\n  emit modified(this);\n  return true;\n}\n\nSelectiveCall\nFMChannel::txTone() const {\n  return _txTone;\n}\nbool\nFMChannel::setTXTone(SelectiveCall code) {\n  _txTone = code;\n  emit modified(this);\n  return true;\n}\n\nFMChannel::Bandwidth\nFMChannel::bandwidth() const {\n  return _bw;\n}\nbool\nFMChannel::setBandwidth(Bandwidth bw) {\n  _bw = bw;\n  emit modified(this);\n  return true;\n}\n\nconst FMAPRSSystemReference *\nFMChannel::aprsRef() const {\n  return &_aprsSystem;\n}\n\nFMAPRSSystemReference *\nFMChannel::aprsRef() {\n  return &_aprsSystem;\n}\n\nFMAPRSSystem *\nFMChannel::aprs() const {\n  return _aprsSystem.as<FMAPRSSystem>();\n}\nvoid\nFMChannel::setAPRS(FMAPRSSystem *sys) {\n  _aprsSystem.set(sys);\n}\n\n\nFMChannelExtension *\nFMChannel::extended() const {\n  return _extended;\n}\n\n\nAnytoneFMChannelExtension *\nFMChannel::anytoneChannelExtension() const {\n  return _anytoneExtension;\n}\nvoid\nFMChannel::setAnytoneChannelExtension(AnytoneFMChannelExtension *ext) {\n  if (_anytoneExtension == ext)\n    return;\n  if (_anytoneExtension)\n    _anytoneExtension->deleteLater();\n  _anytoneExtension = ext;\n  if (_anytoneExtension) {\n    _anytoneExtension->setParent(this);\n    connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified()));\n  }\n}\n\nYAML::Node\nFMChannel::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = AnalogChannel::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"fm\"] = node;\n  return type;\n}\n\nbool\nFMChannel::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse analog channel: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node ch = node.begin()->second;\n\n  return AnalogChannel::parse(node, ctx, err);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AMChannel\n * ********************************************************************************************* */\nAMChannel::AMChannel(QObject *parent)\n  : AnalogChannel(parent)\n{\n  // pass...\n}\n\n\nbool\nAMChannel::copy(const ConfigItem &other) {\n  auto c = other.as<AMChannel>();\n  if ((nullptr==c) || (! AnalogChannel::copy(other)))\n    return false;\n  return true;\n}\n\nConfigItem *\nAMChannel::clone() const {\n  auto c = new AMChannel();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nvoid\nAMChannel::clear() {\n  AnalogChannel::clear();\n  setSquelchDefault();\n}\n\nYAML::Node\nAMChannel::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = AnalogChannel::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"am\"] = node;\n  return type;\n}\n\nbool\nAMChannel::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse analog channel: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node ch = node.begin()->second;\n\n  return AnalogChannel::parse(node, ctx, err);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DigitalChannel\n * ********************************************************************************************* */\nDigitalChannel::DigitalChannel(QObject *parent)\n  : Channel(parent)\n{\n  // pass...\n}\n\nDigitalChannel::DigitalChannel(const DigitalChannel &other, QObject *parent)\n  : Channel(other, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRChannel\n * ********************************************************************************************* */\nDMRChannel::DMRChannel(QObject *parent)\n  : DigitalChannel(parent), _admit(Admit::Always),\n    _colorCode(1), _timeSlot(TimeSlot::TS1),\n    _rxGroup(), _txContact(), _posSystem(), _roaming(), _radioId(),\n    _extended(new DMRChannelExtension(this)),\n    _commercialExtension(nullptr), _anytoneExtension(nullptr)\n{\n  // Register default tags\n  if (! ConfigItem::Context::tagIsSet(staticMetaObject.className(), \"roaming\", \"!default\"))\n    ConfigItem::Context::setTag(staticMetaObject.className(), \"roaming\", \"!default\", QVariant::fromValue(DefaultRoamingZone::get()));\n  if (! ConfigItem::Context::tagIsSet(staticMetaObject.className(), \"radioId\", \"!default\"))\n    ConfigItem::Context::setTag(staticMetaObject.className(), \"radioId\", \"!default\", QVariant::fromValue(DefaultRadioID::get()));\n\n  // Set default DMR Id\n  _radioId.set(DefaultRadioID::get());\n\n  // Connect signals of references\n  connect(&_rxGroup, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_txContact, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_posSystem, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_roaming, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_radioId, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(_extended, &DMRChannelExtension::modified, this, &DMRChannel::modified);\n}\n\nvoid\nDMRChannel::clear() {\n  DigitalChannel::clear();\n  setColorCode(1);\n  setTimeSlot(TimeSlot::TS1);\n  setGroupList(nullptr);\n  setContact(nullptr);\n  setAPRS(nullptr);\n  setRoaming(nullptr);\n  setRadioId(DefaultRadioID::get());\n  setCommercialExtension(nullptr);\n  setAnytoneChannelExtension(nullptr);\n}\n\nConfigItem *\nDMRChannel::clone() const {\n  DMRChannel *c = new DMRChannel();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nDMRChannel::Admit\nDMRChannel::admit() const {\n  return _admit;\n}\nvoid\nDMRChannel::setAdmit(Admit admit) {\n  _admit = admit;\n  emit modified(this);\n}\n\nunsigned\nDMRChannel::colorCode() const {\n  return _colorCode;\n}\nbool\nDMRChannel::setColorCode(unsigned cc) {\n  if (15 < cc) {\n    logWarn() << \"Color-code \" << cc << \" is not in range [0,15], set to 15.\";\n    cc = 15;\n  }\n  _colorCode = cc;\n  emit modified(this);\n  return true;\n}\n\nDMRChannel::TimeSlot\nDMRChannel::timeSlot() const {\n  return _timeSlot;\n}\nbool\nDMRChannel::setTimeSlot(TimeSlot slot) {\n  _timeSlot = slot;\n  emit modified(this);\n  return true;\n}\n\nconst GroupListReference *\nDMRChannel::groupListRef() const {\n  return &_rxGroup;\n}\n\nGroupListReference *\nDMRChannel::groupListRef() {\n  return &_rxGroup;\n}\n\nRXGroupList *\nDMRChannel::groupList() const {\n  return _rxGroup.as<RXGroupList>();\n}\n\nbool\nDMRChannel::setGroupList(RXGroupList *g) {\n  if(! _rxGroup.set(g))\n    return false;\n  emit modified(this);\n  return true;\n}\n\nconst DMRContactReference *\nDMRChannel::contactRef() const {\n  return &_txContact;\n}\n\nDMRContactReference *\nDMRChannel::contactRef() {\n  return &_txContact;\n}\n\nDMRContact *\nDMRChannel::contact() const {\n  return _txContact.as<DMRContact>();\n}\n\nbool\nDMRChannel::setContact(DMRContact *c) {\n  if(! _txContact.set(c))\n    return false;\n  emit modified(this);\n  return true;\n}\n\nconst PositioningSystemReference *\nDMRChannel::aprsRef() const {\n  return &_posSystem;\n}\n\nPositioningSystemReference *\nDMRChannel::aprsRef() {\n  return &_posSystem;\n}\n\nPositionReportingSystem *\nDMRChannel::aprs() const {\n  return _posSystem.as<PositionReportingSystem>();\n}\n\nbool\nDMRChannel::setAPRS(PositionReportingSystem *sys) {\n  if (! _posSystem.set(sys))\n    return false;\n  emit modified(this);\n  return true;\n}\n\nconst RoamingZoneReference *\nDMRChannel::roamingRef() const {\n  return &_roaming;\n}\n\nRoamingZoneReference *\nDMRChannel::roamingRef() {\n  return &_roaming;\n}\n\nRoamingZone *\nDMRChannel::roaming() const {\n  return _roaming.as<RoamingZone>();\n}\n\nbool\nDMRChannel::setRoaming(RoamingZone *zone) {\n  _roaming.set(zone);\n  emit modified(this);\n  return true;\n}\n\nconst DMRRadioIDReference *\nDMRChannel::radioIdRef() const {\n  return &_radioId;\n}\n\nDMRRadioIDReference *\nDMRChannel::radioIdRef() {\n  return &_radioId;\n}\n\nDMRRadioID *\nDMRChannel::radioId() const {\n  return _radioId.as<DMRRadioID>();\n}\n\nbool\nDMRChannel::setRadioId(DMRRadioID *id) {\n  if (! _radioId.set(id))\n    return false;\n  emit modified(this);\n  return true;\n}\n\n\nDMRChannelExtension *\nDMRChannel::extended() const {\n  return _extended;\n}\n\n\nCommercialChannelExtension *\nDMRChannel::commercialExtension() const {\n  return _commercialExtension;\n}\nvoid\nDMRChannel::setCommercialExtension(CommercialChannelExtension *ext) {\n  if (_commercialExtension == ext)\n    return;\n  if (_commercialExtension)\n    _commercialExtension->deleteLater();\n  _commercialExtension = ext;\n  if (_commercialExtension) {\n    _commercialExtension->setParent(this);\n    connect(_commercialExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified()));\n  }\n}\n\nAnytoneDMRChannelExtension *\nDMRChannel::anytoneChannelExtension() const {\n  return _anytoneExtension;\n}\nvoid\nDMRChannel::setAnytoneChannelExtension(AnytoneDMRChannelExtension *ext) {\n  if (_anytoneExtension == ext)\n    return;\n  if (_anytoneExtension)\n    _anytoneExtension->deleteLater();\n  _anytoneExtension = ext;\n  if (_anytoneExtension) {\n    _anytoneExtension->setParent(this);\n    connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onReferenceModified()));\n  }\n}\n\nYAML::Node\nDMRChannel::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = DigitalChannel::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"dmr\"] = node;\n  return type;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of M17Channel\n * ********************************************************************************************* */\nM17Channel::M17Channel(QObject *parent)\n  : DigitalChannel(parent), _mode(Mode::Voice), _accessNumber(0), _txContact(),\n    _gpsEnabled(false), _encryptionMode(EncryptionMode::None)\n{\n  // Connect signals of references\n  connect(&_txContact, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nM17Channel::M17Channel(const M17Channel &other, QObject *parent)\n  : DigitalChannel(other, parent), _mode(Mode::Voice), _accessNumber(0),\n    _txContact(), _gpsEnabled(false),\n    _encryptionMode(EncryptionMode::None)\n{\n  copy(other);\n\n  // Connect signals of references\n  connect(&_txContact, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nvoid\nM17Channel::clear() {\n  DigitalChannel::clear();\n  setMode(Mode::Voice);\n  setAccessNumber(0);\n  setContact(nullptr);\n  enableAPRS(false);\n  setEncryptionMode(EncryptionMode::None);\n}\n\nConfigItem *\nM17Channel::clone() const {\n  M17Channel *c = new M17Channel();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nM17Channel::Mode\nM17Channel::mode() const {\n  return _mode;\n}\n\nvoid\nM17Channel::setMode(Mode mode) {\n  if (_mode == mode)\n    return;\n  _mode = mode;\n  emit modified(this);\n}\n\nunsigned int\nM17Channel::accessNumber() const {\n  return _accessNumber;\n}\n\nvoid\nM17Channel::setAccessNumber(unsigned int can) {\n  can = std::min(15u, can);\n  if (_accessNumber == can)\n    return;\n  _accessNumber = can;\n  emit modified(this);\n}\n\nconst M17ContactReference *\nM17Channel::contactRef() const {\n  return &_txContact;\n}\n\nM17ContactReference *\nM17Channel::contactRef() {\n  return &_txContact;\n}\n\nvoid\nM17Channel::setContactRef(M17ContactReference *ref) {\n  if (nullptr == ref)\n    _txContact.clear();\n  else\n    _txContact.copy(ref);\n}\n\nM17Contact *\nM17Channel::contact() const {\n  return _txContact.as<M17Contact>();\n}\n\nbool\nM17Channel::setContact(M17Contact *c) {\n  if(! _txContact.set(c))\n    return false;\n  emit modified(this);\n  return true;\n}\n\nbool\nM17Channel::aprsEnabled() const {\n  return _gpsEnabled;\n}\n\nvoid\nM17Channel::enableAPRS(bool enabled) {\n  if (_gpsEnabled == enabled)\n    return;\n  _gpsEnabled = enabled;\n  emit modified(this);\n}\n\nM17Channel::EncryptionMode\nM17Channel::encryptionMode() const {\n  return _encryptionMode;\n}\n\nvoid\nM17Channel::setEncryptionMode(EncryptionMode mode) {\n  if (_encryptionMode == mode)\n    return;\n  _encryptionMode = mode;\n  emit modified(this);\n}\n\nYAML::Node\nM17Channel::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = DigitalChannel::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"m17\"] = node;\n  return type;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of SelectedChannel\n * ********************************************************************************************* */\nSelectedChannel *SelectedChannel::_instance = nullptr;\n\nSelectedChannel::SelectedChannel()\n  : Channel()\n{\n  setName(\"[Selected]\");\n}\n\nSelectedChannel::~SelectedChannel() {\n  SelectedChannel::_instance = nullptr;\n}\n\nbool\nSelectedChannel::copy(const ConfigItem &other) {\n  Q_UNUSED(other);\n  return false;\n}\nConfigItem *\nSelectedChannel::clone() const {\n  return nullptr;\n}\n\nSelectedChannel *\nSelectedChannel::get() {\n  if (nullptr == SelectedChannel::_instance)\n    SelectedChannel::_instance = new SelectedChannel();\n  return SelectedChannel::_instance;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelList\n * ********************************************************************************************* */\nChannelList::ChannelList(QObject *parent)\n  : ConfigObjectList(Channel::staticMetaObject, parent)\n{\n  // pass...\n}\n\nint\nChannelList::add(ConfigObject *obj, int row, bool unique) {\n  if ((nullptr == obj) || (! obj->is<Channel>())) {\n    logError() << \"Cannot add nullptr or non-channel objects to channel list.\";\n    return -1;\n  }\n  return ConfigObjectList::add(obj, row, unique);\n}\n\nChannel *\nChannelList::channel(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<Channel>();\n  return nullptr;\n}\n\nDMRChannel *\nChannelList::findDMRChannel(Frequency rx, Frequency tx, DMRChannel::TimeSlot ts, unsigned cc) const {\n  for (int i=0; i<count(); i++) {\n    if (! _items[i]->is<DMRChannel>())\n      continue;\n    /// @bug I should certainly change the frequency handling to integer values!\n    if ((channel(i)->txFrequency()!=tx) || (channel(i)->rxFrequency()!=rx))\n      continue;\n    DMRChannel *digi = channel(i)->as<DMRChannel>();\n    if (digi->timeSlot() != ts)\n      continue;\n    if (digi->colorCode() != cc)\n      continue;\n    return digi;\n  }\n  return nullptr;\n}\n\nFMChannel *\nChannelList::findFMChannelByTxFreq(Frequency freq) const {\n  for (int i=0; i<count(); i++) {\n    if (! channel(i)->is<FMChannel>())\n      continue;\n    if (channel(i)->txFrequency() == freq)\n      return channel(i)->as<FMChannel>();\n  }\n  return nullptr;\n}\n\nConfigItem *\nChannelList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  static bool digitalDepricated = true, analogDeprecated = true;\n  Q_UNUSED(ctx)\n  if (! node)\n    return nullptr;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create channel: Expected object with one child.\";\n    return nullptr;\n  }\n\n  QString type = QString::fromStdString(node.begin()->first.as<std::string>());\n  if ((\"digital\" == type) || (\"dmr\" == type)) {\n    if ((\"digital\" == type) && digitalDepricated) {\n      logWarn() << node.Mark().line << \":\" << node.Mark().column\n                << \": Using 'digital' for DMR channels is deprecated. Please use 'dmr' instead.\";\n      digitalDepricated = false;\n    }\n    return new DMRChannel();\n  } else if ((\"analog\" == type) || (\"fm\"==type)) {\n    if ((\"analog\" == type) && analogDeprecated) {\n      logWarn() << node.Mark().line << \":\" << node.Mark().column\n                << \": Using 'analog' for FM channels is deprecated. Please use 'fm' instead.\";\n      analogDeprecated = false;\n    }\n    return new FMChannel();\n  } else if (\"m17\" == type) {\n    return new M17Channel();\n  } else if (\"am\" == type) {\n    return new AMChannel();\n  }\n\n  errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n              << \": Cannot create channel: Unknown type '\" << type << \"'.\";\n\n  return nullptr;\n}\n\n"
  },
  {
    "path": "lib/channel.hh",
    "content": "#ifndef CHANNEL_HH\n#define CHANNEL_HH\n\n#include <QObject>\n#include <QAbstractTableModel>\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"signaling.hh\"\n#include \"interval.hh\"\n#include \"level.hh\"\n\n#include \"channel_extension.hh\"\n#include \"opengd77_extension.hh\"\n#include \"tyt_extensions.hh\"\n#include \"opengd77_extension.hh\"\n#include \"anytone_extension.hh\"\n#include \"commercial_extension.hh\"\n\nclass Config;\nclass RXGroupList;\nclass DMRContact;\nclass M17Contact;\nclass ScanList;\nclass FMAPRSSystem;\nclass PositionReportingSystem;\nclass RoamingZone;\nclass DMRRadioID;\n\n\n/** The base class of all channels (analog and digital) of a codeplug configuration.\n *\n * This class holds the common configuration of @c AnalogChannel and @c DigitalChannel, that is\n * the name, RX and TX frequencies, output power, TOT and default scanlist properties.\n *\n * @ingroup conf */\nclass Channel: public ConfigObject\n{\n  Q_OBJECT\n\n  /** The receive frequency of the channel in Hz. */\n  Q_PROPERTY(Frequency rxFrequency READ rxFrequency WRITE setRXFrequency)\n  /** The transmit frequency of the channel in Hz. */\n  Q_PROPERTY(Frequency txFrequency READ txFrequency WRITE setTXFrequency)\n  /** The transmit power. */\n  Q_PROPERTY(Power power READ power WRITE setPower SCRIPTABLE false)\n  /** The transmit timeout in seconds. */\n  Q_PROPERTY(Interval timeout READ timeout WRITE setTimeout)\n  /** If true, the channel is receive only. */\n  Q_PROPERTY(bool rxOnly READ rxOnly WRITE setRXOnly)\n  /** The scan list. */\n  Q_PROPERTY(ScanListReference* scanListRef READ scanListRef)\n  /** The VOX setting. */\n  Q_PROPERTY(Level vox READ vox WRITE setVOX)\n  /** The OpenGD77 channel extension. */\n  Q_PROPERTY(OpenGD77ChannelExtension* openGD77 READ openGD77ChannelExtension WRITE setOpenGD77ChannelExtension)\n  /** The TyT channel extension. */\n  Q_PROPERTY(TyTChannelExtension* tyt READ tytChannelExtension WRITE setTyTChannelExtension)\n\n  /** Specifies the prefix for every ID assigned to every channel during serialization. */\n  Q_CLASSINFO(\"IdPrefix\", \"ch\")\n\npublic:\n  /** Possible power settings. */\n  enum class Power {\n    Max,  ///< Highest power setting (e.g. > 5W, if available).\n    High, ///< High power setting (e.g, 5W).\n    Mid,  ///< Medium power setting (e.g., 2W, if available)\n    Low,  ///< Low power setting (e.g., 1W).\n    Min   ///< Lowest power setting (e.g., <1W, if available).\n  };\n  Q_ENUM(Power)\n\n  enum class OffsetShift {\n      None,     ///< No Offset between TX/RX frequencies.\n      Positive,  ///< Positive offset between TX/RX frequencies.\n      Negative  ///< Negative offset between TX/RX frequencies.\n  };\n  Q_ENUM(OffsetShift)\n\n  /** Options for type selection flags. */\n  enum class Type {\n    None=0, DMR=1, M17=2, FM=4, AM=8, Analog=FM|AM, Digital=DMR|M17, All=Analog|Digital\n  };\n  Q_ENUM(Type);\n  Q_DECLARE_FLAGS(Types, Type);\n  Q_FLAGS(Types);\n\nprotected:\n  /** Hidden constructor.\n   * Constructs a new empty channel. */\n  explicit Channel(QObject *parent=nullptr);\n  /** Copy constructor. */\n  Channel(const Channel &other, QObject *parent=nullptr);\n\npublic:\n  bool copy(const ConfigItem &other);\n  void clear();\n\n  /** Returns the RX frequency of the channel in Hz. */\n  Frequency rxFrequency() const;\n  /** (Re-)Sets the RX frequency of the channel in Hz. */\n  bool setRXFrequency(Frequency freq);\n  /** Returns the TX frequency of the channel in Hz. */\n  Frequency txFrequency() const;\n  /** (Re-)Sets the TX frequency of the channel in Hz. */\n  bool setTXFrequency(Frequency freq);\n  /** Returns the offset between tx and rx frequency of the channel in Hz. */\n  FrequencyOffset offsetFrequency() const;\n  /** Returns direction of offset if any.*/\n  OffsetShift offsetShift() const;\n\n  /** Returns @c true if the channel uses the global default power setting. */\n  bool defaultPower() const;\n  /** Returns the power setting of the channel if the channel does not use the default power. */\n  Power power() const;\n  /** (Re-)Sets the power setting of the channel, overrides default power. */\n  void setPower(Power power);\n  /** Sets the channel to use the default power setting. */\n  void setDefaultPower();\n\n  /** Returns @c true if the transmit timeout is specified by the global default value. */\n  bool defaultTimeout() const;\n  /** Returns @c true if the transmit timeout is disabled. */\n  bool timeoutDisabled() const;\n  /** Returns the TX timeout (TOT) in seconds. */\n  Interval timeout() const;\n  /** (Re-)Sets the TX timeout (TOT) in seconds. */\n  bool setTimeout(const Interval &dur);\n  /** Disables the transmit timeout. */\n  void disableTimeout();\n  /** Sets the timeout to the global default timeout. */\n  void setDefaultTimeout();\n\n  /** Returns @c true, if the channel is RX only. */\n  bool rxOnly() const;\n  /** Set, whether the channel is RX only. */\n  bool setRXOnly(bool enable);\n\n  /** Returns @c true if the VOX is disabled. */\n  bool voxDisabled() const;\n  /** Returns @c true if the VOX is specified by the global default value. */\n  bool defaultVOX() const;\n  /** Returns the VOX level [0-10]. */\n  Level vox() const;\n  /** Sets the VOX level [0-10]. */\n  void setVOX(Level level);\n  /** Sets the VOX level to the default value. */\n  void setVOXDefault();\n  /** Disables the VOX. */\n  void disableVOX();\n\n  /** Returns the reference to the scan list. */\n  const ScanListReference *scanListRef() const;\n  /** Returns the reference to the scan list. */\n  ScanListReference *scanListRef();\n  /** Returns the default scan list for the channel. */\n  ScanList *scanList() const;\n  /** (Re-) Sets the default scan list for the channel. */\n  bool setScanList(ScanList *list);\n\n  /** Returns the channel extension for the OpenGD77 firmware.\n   * If this extension is not set, returns @c nullptr. */\n  OpenGD77ChannelExtension *openGD77ChannelExtension() const;\n  /** Sets the OpenGD77 channel extension. */\n  void setOpenGD77ChannelExtension(OpenGD77ChannelExtension *ext);\n\n  /** Returns the channel extension for TyT devices.\n   * If this extension is not set, returns @c nullptr. */\n  TyTChannelExtension *tytChannelExtension() const;\n  /** Sets the TyT channel extension. */\n  void setTyTChannelExtension(TyTChannelExtension *ext);\n\npublic:\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected slots:\n  /** Gets called whenever a referenced object is changed or deleted. */\n  void onReferenceModified();\n\nprotected:\n  /** The RX frequency in Hz. */\n  Frequency _rxFreq;\n  /** The TX frequency in Hz. */\n  Frequency _txFreq;\n  /** If @c true, the channel uses the global power setting. */\n  bool _defaultPower;\n  /** The transmit power setting. */\n  Power _power;\n  /** Transmit timeout. If set to null, the global/default ToT interval is used. If set to\n   * infinity, ToT is disabled. */\n  Interval _txTimeOut;\n  /** RX only flag. */\n  bool _rxOnly;\n  /** Holds the VOX level. */\n  Level _vox;\n  /** Default scan list of the channel. */\n  ScanListReference _scanlist;\n  /** Owns the OpenGD77 channel extension object. */\n  OpenGD77ChannelExtension *_openGD77ChannelExtension;\n  /** Owns the TyT channel extension object. */\n  TyTChannelExtension *_tytChannelExtension;\n};\n\n\n\n/** Base class for all analog channels.\n *\n * @ingroup conf */\nclass AnalogChannel: public Channel\n{\n  Q_OBJECT\n\n  /** Specifies the squelch level for the channel. */\n  Q_PROPERTY(Level squelch READ squelch WRITE setSquelch FINAL)\n\nprotected:\n  /** Hidden constructor. */\n  explicit AnalogChannel(QObject *parent=nullptr);\n\npublic:\n  /** Copy constructor. */\n  AnalogChannel(const AnalogChannel &other, QObject *parent=nullptr);\n\n\n  /** Returns @c true if the global default squelch level is used. */\n  bool defaultSquelch() const;\n  /** Returns @c true if the squelch is disabled. */\n  bool squelchDisabled() const;\n  /** Returns the squelch level [1,10]. */\n  Level squelch() const;\n  /** (Re-)Sets the squelch level [0,10]. 0 Disables squelch (on some radios). */\n  bool setSquelch(Level squelch);\n  /** Disables the quelch. */\n  void disableSquelch();\n  /** Sets the squelch to the global default value. */\n  void setSquelchDefault();\n\n\nprotected:\n  /** Squelch. If set to 0 -> disabled. If invalid -> default squelch. */\n  Level _squelch;\n};\n\n\n\n/** Extension to the @c AnalogChannel class to implement an analog FM channel.\n *\n * This class implements all the properties specific to an FM channel. That is, the admit\n * criterion, squelch, RX and TX tones and bandwidth settings.\n *\n * @ingroup conf */\nclass FMChannel: public AnalogChannel\n{\n  Q_OBJECT\n\n  /** The admit criterion of the channel. */\n  Q_PROPERTY(Admit admit READ admit WRITE setAdmit)\n  /** The RX tone (CTCSS/DSC). */\n  Q_PROPERTY(SelectiveCall rxTone READ rxTone WRITE setRXTone)\n  /** The TX tone (CTCSS/DSC). */\n  Q_PROPERTY(SelectiveCall txTone READ txTone WRITE setTXTone)\n  /** The band width of the channel. */\n  Q_PROPERTY(Bandwidth bandwidth READ bandwidth WRITE setBandwidth)\n  /** The APRS system. */\n  Q_PROPERTY(FMAPRSSystemReference* aprs READ aprsRef)\n\n  /** Common extended channel settings. */\n  Q_PROPERTY(FMChannelExtension *extended READ extended);\n  /** The AnyTone FM channel extension. */\n  Q_PROPERTY(AnytoneFMChannelExtension* anytone READ anytoneChannelExtension WRITE setAnytoneChannelExtension)\n\n\npublic:\n  /** Admit criteria of analog channel. */\n  enum class Admit {\n    Always,  ///< Allow always.\n    Free,    ///< Allow when channel free.\n    Tone     ///< Allow when free or wrong ctcss/dcs tone is present.\n  };\n  Q_ENUM(Admit)\n\n  /** Possible bandwidth of an analog channel. */\n  enum class Bandwidth {\n    Narrow,  ///< Narrow bandwidth (12.5kHz).\n    Wide     ///< Wide bandwidth (25kHz).\n  };\n  Q_ENUM(Bandwidth)\n\npublic:\n  /** Constructs a new empty analog channel. */\n  Q_INVOKABLE explicit FMChannel(QObject *parent=nullptr);\n\n  bool copy(const ConfigItem &other);\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the admit criterion for the analog channel. */\n  Admit admit() const;\n  /** (Re-)Sets the admit criterion for the analog channel. */\n  void setAdmit(Admit admit);\n\n  /** Returns the CTCSS/DCS RX tone, @c SIGNALING_NONE means disabled. */\n  SelectiveCall rxTone() const;\n  /** (Re-)Sets the CTCSS/DCS RX tone, @c SIGNALING_NONE disables the RX tone. */\n  bool setRXTone(SelectiveCall code);\n  /** Returns the CTCSS/DCS TX tone, @c SIGNALING_NONE means disabled. */\n  SelectiveCall txTone() const;\n  /** (Re-)Sets the CTCSS/DCS TX tone, @c SIGNALING_NONE disables the TX tone. */\n  bool setTXTone(SelectiveCall code);\n\n  /** Returns the bandwidth of the analog channel. */\n  Bandwidth bandwidth() const;\n  /** (Re-)Sets the bandwidth of the analog channel. */\n  bool setBandwidth(Bandwidth bw);\n\n  /** Returns the reference to the APRS system. */\n  const FMAPRSSystemReference *aprsRef() const;\n  /** Returns the reference to the APRS system. */\n  FMAPRSSystemReference *aprsRef();\n  /** Returns the APRS system used for this channel or @c nullptr if disabled. */\n  FMAPRSSystem *aprs() const;\n  /** Sets the APRS system. */\n  void setAPRS(FMAPRSSystem *sys);\n\n  /** Returns the extended settings. */\n  FMChannelExtension *extended() const;\n\n  /** Returns the FM channel extension for AnyTone devices.\n   * If this extension is not set, returns @c nullptr. */\n  AnytoneFMChannelExtension *anytoneChannelExtension() const;\n  /** Sets the AnyTone FM channel extension. */\n  void setAnytoneChannelExtension(AnytoneFMChannelExtension *ext);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the admit criterion. */\n\tAdmit _admit;\n  /** The RX CTCSS/DCS setting. */\n  SelectiveCall _rxTone;\n  /** The TX CTCSS/DCS setting. */\n  SelectiveCall _txTone;\n  /** The channel bandwidth. */\n  Bandwidth _bw;\n  /** A reference to the APRS system used on the channel or @c nullptr if disabled. */\n  FMAPRSSystemReference _aprsSystem;\n  /** Owns the extended settings. */\n  FMChannelExtension *_extended;\n  /** Owns the AnyTone FM channel extension. */\n  AnytoneFMChannelExtension *_anytoneExtension;\n};\n\n\n\n/** Extension to the @c AnalogChannel class to implement an analog AM channel.\n *\n * This class implements all the properties specific to an AM channel. These channels are usually\n * used to represent air-band channels. So there are not that many settings.\n *\n * @ingroup conf */\nclass AMChannel: public AnalogChannel\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new empty AM channel. */\n  Q_INVOKABLE explicit AMChannel(QObject *parent=nullptr);\n\n  bool copy(const ConfigItem &other) override;\n  ConfigItem *clone() const override;\n  void clear() override;\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack()) override;\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n};\n\n\n\n/** Base class of all digital channels.\n *\n * @ingroup conf */\nclass DigitalChannel: public Channel\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit DigitalChannel(QObject *parent=nullptr);\n\npublic:\n  /** Copy constructor. */\n  DigitalChannel(const DigitalChannel &other, QObject *parent=nullptr);\n};\n\n\n\n/** Extension to the @c DigitalChannel class to implement an DMR channel.\n *\n * That is, the admit criterion, color code, time slot, RX group list and TX contact.\n *\n * @ingroup conf */\nclass DMRChannel: public DigitalChannel\n{\n  Q_OBJECT\n\n  /** The admit criterion of the channel. */\n  Q_PROPERTY(Admit admit READ admit WRITE setAdmit)\n  /** The color code of the channel. */\n  Q_PROPERTY(unsigned colorCode READ colorCode WRITE setColorCode)\n  /** The time slot of the channel. */\n  Q_PROPERTY(TimeSlot timeSlot READ timeSlot WRITE setTimeSlot)\n  /** The radio ID. */\n  Q_PROPERTY(DMRRadioIDReference* radioId READ radioIdRef)\n  /** The rx group list. */\n  Q_PROPERTY(GroupListReference* groupList READ groupListRef)\n  /** The tx contact. */\n  Q_PROPERTY(DMRContactReference* contact READ contactRef)\n  /** The positioning system. */\n  Q_PROPERTY(PositioningSystemReference* aprs READ aprsRef)\n  /** The roaming zone. */\n  Q_PROPERTY(RoamingZoneReference* roaming READ roamingRef)\n\n  /** The extended dmr channel settings. */\n  Q_PROPERTY(DMRChannelExtension* extended READ extended)\n  /** The commercial channel extension. */\n  Q_PROPERTY(CommercialChannelExtension* commercial READ commercialExtension WRITE setCommercialExtension)\n  /** The AnyTone DMR channel extension. */\n  Q_PROPERTY(AnytoneDMRChannelExtension* anytone READ anytoneChannelExtension WRITE setAnytoneChannelExtension)\n\npublic:\n  /** Possible admit criteria of digital channels. */\n  enum class Admit {\n    Always,      ///< No admit criteria, allows one to transmit any time.\n    Free,        ///< Transmit only if channel is free.\n    ColorCode    ///< Transmit if channel is free or differs given color code.\n  };\n  Q_ENUM(Admit)\n\n  /** Possible timeslots for digital channels. */\n  enum class TimeSlot {\n    TS1, ///< Time/repeater slot 1\n    TS2  ///< Time/repeater slot 2\n  };\n  Q_ENUM(TimeSlot)\n\npublic:\n  /** Constructs a new empty digital (DMR) channel. */\n  Q_INVOKABLE explicit DMRChannel(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the admit criterion for the channel. */\n\tAdmit admit() const;\n  /** (Re-)Sets the admit criterion for the channel. */\n\tvoid setAdmit(Admit admit);\n\n  /** Returns the color code for the channel. */\n\tunsigned colorCode() const;\n  /** (Re-)Sets the color code for the channel. */\n\tbool setColorCode(unsigned cc);\n\n  /** Returns the time slot for the channel. */\n  TimeSlot timeSlot() const;\n  /** (Re-)Sets the time slot for the channel. */\n\tbool setTimeSlot(TimeSlot ts);\n\n  /** Returns a reference to the group list. */\n  const GroupListReference *groupListRef() const;\n  /** Returns a reference to the group list. */\n  GroupListReference *groupListRef();\n  /** Returns the RX group list for the channel. */\n  RXGroupList *groupList() const;\n  /** (Re-)Sets the RX group list for the channel. */\n  bool setGroupList(RXGroupList *rxg);\n\n  /** Returns a reference to the transmit contactRef. */\n  const DMRContactReference *contactRef() const;\n  /** Returns a reference to the transmit contactRef. */\n  DMRContactReference *contactRef();\n  /** Returns the default TX contact to call on this channel. */\n  DMRContact *contact() const;\n  /** (Re-) Sets the default TX contact for this channel. */\n  bool setContact(DMRContact *c);\n\n  /** Returns a reference to the positioning system. */\n  const PositioningSystemReference *aprsRef() const;\n  /** Returns a reference to the positioning system. */\n  PositioningSystemReference *aprsRef();\n  /** Returns the GPS system associated with this channel or @c nullptr if not set. */\n  PositionReportingSystem *aprs() const;\n  /** Associates the GPS System with this channel. */\n  bool setAPRS(PositionReportingSystem *sys);\n\n  /** Returns a reference to the roamingRef zone. */\n  const RoamingZoneReference *roamingRef() const;\n  /** Returns a reference to the roamingRef zone. */\n  RoamingZoneReference *roamingRef();\n  /** Returns the roaming zone associated with this channel or @c nullptr if not set. */\n  RoamingZone *roaming() const;\n  /** Associates the given roaming zone with this channel. */\n  bool setRoaming(RoamingZone *zone);\n\n  /** Returns the reference to the radio ID. */\n  const DMRRadioIDReference *radioIdRef() const;\n  /** Returns the reference to the radio ID. */\n  DMRRadioIDReference *radioIdRef();\n  /** Returns the radio ID associated with this channel. */\n  DMRRadioID *radioId() const;\n  /** Associates the given radio ID with this channel. */\n  bool setRadioId(DMRRadioID *id);\n\n  /** Returns the extended channel settings. */\n  DMRChannelExtension *extended() const;\n\n  /** Returns the extension for commercial features. */\n  CommercialChannelExtension *commercialExtension() const;\n  /** Sets the commercial channel extension. */\n  void setCommercialExtension(CommercialChannelExtension *ext);\n\n  /** Returns the DMR channel extension for AnyTone devices.\n   * If this extension is not set, returns @c nullptr. */\n  AnytoneDMRChannelExtension *anytoneChannelExtension() const;\n  /** Sets the AnyTone DMR channel extension. */\n  void setAnytoneChannelExtension(AnytoneDMRChannelExtension *ext);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** The admit criterion. */\n  Admit _admit;\n  /** The channel color code. */\n  unsigned _colorCode;\n  /** The time slot for the channel. */\n  TimeSlot _timeSlot;\n  /** The RX group list for this channel. */\n  GroupListReference _rxGroup;\n  /** The default TX contact. */\n  DMRContactReference _txContact;\n  /** The GPS system. */\n  PositioningSystemReference _posSystem;\n  /** Roaming zone for the channel. */\n  RoamingZoneReference _roaming;\n  /** Radio ID to use on this channel. */\n  DMRRadioIDReference _radioId;\n\n  /** Owns the extended channel extension. */\n  DMRChannelExtension *_extended;\n  /** Owns the commercial channel extension. */\n  CommercialChannelExtension *_commercialExtension;\n  /** Owns the AnyTone DMR channel extension. */\n  AnytoneDMRChannelExtension *_anytoneExtension;\n};\n\n\n/** Implements an M17 channel.\n * @ingroup conf */\nclass M17Channel: public DigitalChannel\n{\n  Q_OBJECT\n\n  /** The channel access number. */\n  Q_PROPERTY(unsigned accessNumber READ accessNumber WRITE setAccessNumber)\n  /** The transmit contact. */\n  Q_PROPERTY(M17ContactReference* contact READ contactRef WRITE setContactRef)\n  /** The channel mode. */\n  Q_PROPERTY(Mode mode READ mode WRITE setMode)\n  /** The encryption mode. */\n  Q_PROPERTY(EncryptionMode encryptionMode READ encryptionMode WRITE setEncryptionMode)\n  /** If enabled, positioning data is send along with voice and data. */\n  Q_PROPERTY(bool aprs READ aprsEnabled WRITE enableAPRS)\n\npublic:\n  /** Possible channel modes. */\n  enum class Mode {\n    Voice, Data, VoiceAndData\n  };\n  Q_ENUM(Mode)\n\n  /** Possible encryption modes. */\n  enum class EncryptionMode {\n    None, AES256, Scrambled\n  };\n  Q_ENUM(EncryptionMode)\n\npublic:\n  /** Constructs a new empty M17 channel. */\n  M17Channel(QObject *parent=nullptr);\n  /** Copy constructor. */\n  M17Channel(const M17Channel &other, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the channel mode. */\n  Mode mode() const;\n  /** Sets the channel mode. */\n  void setMode(Mode mode);\n\n  /** Returns the channel access number. */\n  unsigned int accessNumber() const;\n  /** Sets the channel access number (0-15). */\n  void setAccessNumber(unsigned int can);\n\n  /** Returns a reference to the transmit contact. */\n  const M17ContactReference *contactRef() const;\n  /** Returns a reference to the transmit contact. */\n  M17ContactReference *contactRef();\n  /** Sets the reference to the transmit contact. */\n  void setContactRef(M17ContactReference *ref);\n  /** Returns the default TX contact to call on this channel. */\n  M17Contact *contact() const;\n  /** (Re-) Sets the default TX contact for this channel. */\n  bool setContact(M17Contact *c);\n\n  /** Returns @c true if APRS is enabled. */\n  bool aprsEnabled() const;\n  /** Enables/disables APRS. */\n  void enableAPRS(bool enabled);\n\n  /** Returns the encryption mode of the channel. */\n  EncryptionMode encryptionMode() const;\n  /** Sets the encryption mode of the channel. */\n  void setEncryptionMode(EncryptionMode mode);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the channel mode. */\n  Mode _mode;\n  /** Holds the channel access number. */\n  unsigned int _accessNumber;\n  /** The default TX contact. */\n  M17ContactReference _txContact;\n  /** If @c true, positioning information is send alonside voice and data. */\n  bool _gpsEnabled;\n  /** Holds the encryption mode for the channel. */\n  EncryptionMode _encryptionMode;\n};\n\n\n/** Internal singleton class representing the \"currently selected\" channel.\n * @ingroup conf */\nclass SelectedChannel: public Channel\n{\n  Q_OBJECT\n\nprotected:\n  /** Constructs the \"selected\" channel.\n   * @warning Do not use this class directly, call @c SelectedChannel::get() instead. */\n  explicit SelectedChannel();\n\npublic:\n  /** Destructor. */\n  virtual ~SelectedChannel();\n\n  bool copy(const ConfigItem &other);\n  ConfigItem *clone() const;\n\n  /** Constructs/gets the singleton instance. */\n  static SelectedChannel *get();\n\nprotected:\n  /** Holds the channel singleton instance. */\n  static SelectedChannel *_instance;\n};\n\n\n\n/** Container class holding all channels (analog and digital) for a specific configuration\n * (@c Config).\n *\n * This class also implements the QAbstractTableModel and can therefore be displayed using a\n * default QTableView instance.\n *\n * @ingroup conf */\nclass ChannelList: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs an empty channel list. */\n\texplicit ChannelList(QObject *parent=nullptr);\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\n  /** Gets the channel at the specified index. */\n  Channel *channel(int idx) const;\n  /** Finds a digital channel with the given frequencies, time slot and color code. */\n  DMRChannel *findDMRChannel(Frequency rx, Frequency tx, DMRChannel::TimeSlot ts, unsigned cc) const;\n  /** Finds an analog channel with the given frequency. */\n  FMChannel *findFMChannelByTxFreq(Frequency freq) const;\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\nQ_DECLARE_OPERATORS_FOR_FLAGS(Channel::Types)\n\n#endif // CHANNEL_HH\n"
  },
  {
    "path": "lib/channel_extension.cc",
    "content": "#include \"channel_extension.hh\"\n\n\n\n/* ******************************************************************************************* *\n * Implementation of common channel extended settings\n * ******************************************************************************************* */\nChannelExtension::ChannelExtension(QObject *parent)\n  : ConfigExtension{parent}, _talkaround(false)\n{\n  // pass...\n}\n\n\nbool\nChannelExtension::talkaround() const {\n  return _talkaround;\n}\nvoid\nChannelExtension::enableTalkaround(bool enable) {\n  if (enable == _talkaround)\n    return;\n  _talkaround = enable;\n  emit modified(this);\n}\n\n\n\n/* ******************************************************************************************* *\n * Implementation of common FM channel extended settings\n * ******************************************************************************************* */\nFMChannelExtension::FMChannelExtension(QObject *parent)\n  : ChannelExtension{parent}, _reverseBurst(false)\n{\n  // pass...\n}\n\n\nConfigItem *\nFMChannelExtension::clone() const {\n  auto ext = new FMChannelExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nbool\nFMChannelExtension::reverseBurst() const {\n  return _reverseBurst;\n}\n\nvoid\nFMChannelExtension::enableReverseBurst(bool enable) {\n  if (enable == _reverseBurst)\n    return;\n  _reverseBurst = enable;\n  emit modified(this);\n}\n\n\n\n/* ******************************************************************************************* *\n * Implementation of common DMR channel extended settings\n * ******************************************************************************************* */\nDMRChannelExtension::DMRChannelExtension(QObject *parent)\n  : ChannelExtension{parent}, _callConfirm(false), _sms(true), _smsConfirm(false),\n  _dataConfirm(true), _dcdm(false), _loneWorker(false)\n{\n  // pass...\n}\n\n\nConfigItem *\nDMRChannelExtension::clone() const {\n  auto ext = new DMRChannelExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\n\nbool\nDMRChannelExtension::privateCallConfirm() const {\n  return _callConfirm;\n}\n\nvoid\nDMRChannelExtension::enablePrivateCallConfirm(bool enabled) {\n  if (enabled == _callConfirm)\n    return;\n  _callConfirm = enabled;\n  emit modified(this);\n}\n\n\nbool\nDMRChannelExtension::sms() const {\n  return _sms;\n}\n\nvoid\nDMRChannelExtension::enableSMS(bool enable) {\n  if (enable == _sms)\n    return;\n  _sms = enable;\n  emit modified(this);\n}\n\n\nbool\nDMRChannelExtension::smsConfirm() const {\n  return _smsConfirm;\n}\n\nvoid\nDMRChannelExtension::enableSMSConfirm(bool enabled) {\n  if (enabled == _smsConfirm)\n    return;\n  _smsConfirm = enabled;\n  emit modified(this);\n}\n\n\nbool\nDMRChannelExtension::dataConfirm() const {\n  return _dataConfirm;\n}\n\nvoid\nDMRChannelExtension::enableDataConfirm(bool enable) {\n  if (enable==_dataConfirm)\n    return;\n  _dataConfirm = enable;\n  emit modified(this);\n}\n\n\nbool\nDMRChannelExtension::dcdm() const {\n  return _dcdm;\n}\n\nvoid\nDMRChannelExtension::enableDCDM(bool enable) {\n  if (enable == _dcdm)\n    return;\n  _dcdm = enable;\n  emit modified(this);\n}\n\n\nbool\nDMRChannelExtension::loneWorker() const {\n  return _loneWorker;\n}\n\nvoid\nDMRChannelExtension::enableLoneWorker(bool enable) {\n  if (enable == _loneWorker)\n    return;\n  _loneWorker = enable;\n  emit modified(this);\n}\n\n\n"
  },
  {
    "path": "lib/channel_extension.hh",
    "content": "#ifndef CHANNEL_EXTENSION_HH\n#define CHANNEL_EXTENSION_HH\n\n#include \"configobject.hh\"\n\n\n/** Common extended settings for all channels.\n * @ingroup conf */\nclass ChannelExtension : public ConfigExtension\n{\n  Q_OBJECT\n\n  /** If @c true, talkaround is enabled. */\n  Q_PROPERTY(bool talkaround READ talkaround WRITE enableTalkaround)\n\nprotected:\n  /** Hidden constructor. */\n  explicit ChannelExtension(QObject *parent = nullptr);\n\n\npublic:\n  /** Returns @c true, if talkaround is enabled. */\n  bool talkaround() const;\n  /** Enables/disables talkaround. */\n  void enableTalkaround(bool enable);\n\nprotected:\n  /** If @c true, talkaround is enabled. */\n  bool _talkaround;\n};\n\n\n\n/** Common FM channel extended settings.\n * @ingroup conf */\nclass FMChannelExtension: public ChannelExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the CTCSS phase-reverse burst at the end of transmission is enabled. */\n  Q_PROPERTY(bool reverseBurst READ reverseBurst WRITE enableReverseBurst)\n\npublic:\n  /** Default constructor. */\n  explicit FMChannelExtension(QObject *parent = nullptr);\n\n  ConfigItem *clone() const override;\n\n\n  /** Returns @c true, if the CTCSS phase-reverse burst is enabled. */\n  bool reverseBurst() const;\n  /** Enables/disables the CTCSS phase-reverse burst. */\n  void enableReverseBurst(bool enable);\n\nprotected:\n  /** If @c true, the CTCSS phase-reverse burst at the end of transmission is enabled. */\n  bool _reverseBurst;\n};\n\n\n/** Common DMR channel extended settings.\n * @ingroup conf */\nclass DMRChannelExtension: public ChannelExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the call confirmation is enabled. */\n  Q_PROPERTY(bool privateCallConfirm READ privateCallConfirm WRITE enablePrivateCallConfirm)\n  /** If @c true, SMS reception is enabled. */\n  Q_PROPERTY(bool sms READ sms WRITE enableSMS)\n  /** If @c true, the SMS confirmation is enabled. */\n  Q_PROPERTY(bool smsConfirm READ smsConfirm WRITE enableSMSConfirm)\n  /** If @c true, the radio will response to received data packages. Should be enabled. */\n  Q_PROPERTY(bool dataConfirm READ dataConfirm WRITE enableDataConfirm)\n  /** If @c true, the simplex DCDM mode is enabled. */\n  Q_PROPERTY(bool dcdm READ dcdm WRITE enableDCDM)\n  /** If @c true, the lone-worker feature is enabled for this channel. */\n  Q_PROPERTY(bool loneWorker READ loneWorker WRITE enableLoneWorker)\n\npublic:\n  /** Default constructor. */\n  explicit DMRChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const override;\n\n  /** Returns @c true if the private-call confirmation is enabled. */\n  bool privateCallConfirm() const;\n  /** Enables/disables the private-call confirmation. */\n  void enablePrivateCallConfirm(bool enabled);\n\n  /** Returns @c true if SMS reception is enabled. */\n  bool sms() const;\n  /** Enables/disables SMS reception. */\n  void enableSMS(bool enable);\n\n  /** Returns @c true if the SMS confirmation is enabled. */\n  bool smsConfirm() const;\n  /** Enables/disables the SMS confirmation. */\n  void enableSMSConfirm(bool enabled);\n\n  /** Returns @c true if the data acknowledgement is enabled. */\n  bool dataConfirm() const;\n  /** Enables/disables the data acknowledgement. */\n  void enableDataConfirm(bool enable);\n\n  /** Returns @c true if the simplex DCDM mode is enabled. */\n  bool dcdm() const;\n  /** Enables/disables the simplex DCDM mode. */\n  void enableDCDM(bool enable);\n\n  /** Returns @c true if the lone-worker feature is enabled. */\n  bool loneWorker() const;\n  /** Enables the lone-worker feature for this channel. */\n  void enableLoneWorker(bool enable);\n\nprotected:\n  /** If @c true, the call confirmation is enabled. */\n  bool _callConfirm;\n  /** If @c true, the SMS reception is enabled. */\n  bool _sms;\n  /** If @c true, the SMS confirmation is enabled. */\n  bool _smsConfirm;\n  /** If @c true, the data acknowledgement is enabled. */\n  bool _dataConfirm;\n  /** If @c true, the simplex TDMA mode is enabled. */\n  bool _dcdm;\n  /** If @c true the lone-worker feature is enabled. */\n  bool _loneWorker;\n\n};\n\n\n#endif // CHANNEL_EXTENSION_HH\n"
  },
  {
    "path": "lib/chirpformat.cc",
    "content": "#include \"chirpformat.hh\"\n#include \"logger.hh\"\n#include \"frequency.hh\"\n#include \"signaling.hh\"\n#include \"channel.hh\"\n#include \"config.hh\"\n\n#include <QStringList>\n\n\n/* ********************************************************************************************* *\n * Implementation of ChirpFormat\n * ********************************************************************************************* */\nQSet<QString> const ChirpFormat::_mandatoryHeaders = {\n  \"Location\", \"Name\", \"Frequency\", \"Duplex\", \"Offset\", \"Mode\"\n};\n\nQSet<QString> const ChirpFormat::_knownHeaders = {\n  \"Location\", \"Name\", \"Frequency\", \"Duplex\", \"Offset\", \"Mode\"\n  \"Tone\", \"rToneFreq\", \"cToneFreq\", \"DtcsCode\", \"DtcsPolarity\", \"RxDtcsCode\", \"CrossMode\",\n  \"TStep\", \"Skip\", \"Power\", \"Comment\", \"URCALL\", \"RPT1CALL\", \"RPT2CALL\", \"DVCODE\"\n};\n\nQHash<QString, ChirpFormat::Duplex> const ChirpFormat::_duplexCodes = {\n  {\"\", ChirpFormat::Duplex::None},\n  {\"+\", ChirpFormat::Duplex::Positive},\n  {\"-\", ChirpFormat::Duplex::Negative},\n  {\"Off\", ChirpFormat::Duplex::Off},\n};\n\nQHash<QString, ChirpFormat::Mode> const ChirpFormat::_modeCodes = {\n  {\"FM\", ChirpReader::Mode::FM},\n  {\"NFM\", ChirpReader::Mode::NFM},\n  {\"WFM\", ChirpReader::Mode::WFM},\n  {\"AM\", ChirpReader::Mode::AM},\n  {\"DV\", ChirpReader::Mode::DV},\n  {\"DN\", ChirpReader::Mode::DN},\n};\n\nQHash<QString, ChirpFormat::ToneMode> const ChirpFormat::_toneModeCodes = {\n  {\"\", ChirpFormat::ToneMode::None},\n  {\"Tone\", ChirpFormat::ToneMode::Tone},\n  {\"TSQL\", ChirpFormat::ToneMode::TSQL},\n  {\"TSQL-R\", ChirpFormat::ToneMode::TSQL_R},\n  {\"DTCS\", ChirpFormat::ToneMode::DTCS},\n  {\"DTCS-R\", ChirpFormat::ToneMode::DTCS_R},\n  {\"Cross\", ChirpFormat::ToneMode::Cross},\n};\n\nQHash<QString, ChirpFormat::CrossMode> const ChirpFormat::_crossModes = {\n  {\"->Tone\", ChirpFormat::CrossMode::NoneTone},\n  {\"->DTCS\", ChirpFormat::CrossMode::NoneDTCS},\n  {\"Tone->\", ChirpFormat::CrossMode::ToneNone},\n  {\"Tone->Tone\", ChirpFormat::CrossMode::ToneTone},\n  {\"Tone->DTCS\", ChirpFormat::CrossMode::ToneDTCS},\n  {\"DTCS->\", ChirpFormat::CrossMode::DTCSNone},\n  {\"DTCS->Tone\", ChirpFormat::CrossMode::DTCSTone},\n  {\"DTCS->DTCS\", ChirpFormat::CrossMode::DTCSDTCS},\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of ChirpReader\n * ********************************************************************************************* */\nbool\nChirpReader::read(QTextStream &stream, Config *config, const ErrorStack &err) {\n  // First read header\n  QStringList header;\n  if (! readLine(stream, header, err)) {\n    errMsg(err) << \"Cannot read CSV header.\";\n    return false;\n  }\n\n  // Some trivial sanity checks for the header\n  if (0 == header.size()) {\n    errMsg(err) << \"Invalid CSV file header: Got empty header.\";\n    return false;\n  }\n\n  if (\"Location\" != header.at(0)) {\n    errMsg(err) << \"Invalid CSV file header: 'Location' is not first column!\";\n    return false;\n  }\n\n  // check fields\n  foreach (QString field, _mandatoryHeaders) {\n    if (! header.contains(field)) {\n      errMsg(err) << \"Mandatory column '\" << field << \"' is missing.\";\n      return false;\n    }\n  }\n\n  foreach(QString field, header) {\n    if (! _knownHeaders.contains(field)) {\n      logInfo() << \"Unknown header field '\" << field << \"'.\";\n    }\n  }\n\n  int line=2;\n  while (! stream.atEnd()) {\n    QStringList entry;\n    if (! readLine(stream, entry, err)) {\n      errMsg(err) << \"In CSV file line \" << line << \": Cannot read line.\";\n      return false;\n    }\n    if (! processLine(header, entry, config, err)) {\n      errMsg(err) << \"In CSV file line \" << line << \": Cannot read line.\";\n      return false;\n    }\n    line++;\n  }\n\n  return true;\n}\n\n\nbool\nChirpReader::readLine(QTextStream &stream, QStringList &list, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  list.clear();\n  if (stream.atEnd())\n    return true;\n\n  QString token;\n  QChar ch; stream >> ch;\n  bool string = false;\n  while ((! stream.atEnd()) && (QChar('\\n') != ch)) {\n    if ((!string) && (QChar(',') == ch)) {\n      list.append(token); token.clear();\n    } else if ((! string) && (QChar('\"') == ch)) {\n      string = true;\n    } else if (string && (QChar('\"') == ch)) {\n      string = false;\n    } else {\n      token.append(ch);\n    }\n    stream >> ch;\n  }\n\n  list.append(token);\n\n  return true;\n}\n\n\nbool\nChirpReader::processLine(const QStringList &header, const QStringList &line, Config *config, const ErrorStack &err) {\n  if (header.size() != line.size()) {\n    errMsg(err) << \"Malformed line. Expected \" << header.size() << \" entries, got \" << line.size() << \".\";\n    return false;\n  }\n\n  bool ok;\n  QString name;\n  Frequency rxFrequency, txFrequency;\n  Duplex duplex = Duplex::None;\n  Mode mode = Mode::FM;\n  ToneMode toneMode = ToneMode::None;\n  CrossMode crossMode;\n  double txTone = 67.0, rxTone = 67.0;\n  int txDTCSCode = 000, rxDTCSCode = 000;\n  Polarity txPol = Polarity::Normal, rxPol = Polarity::Normal;\n\n  for (int i=1; i<header.size(); i++) {\n    if (\"Name\" == header.at(i)) {\n      name = line.at(i).simplified();\n    } else if (\"Frequency\" == header.at(i)) {\n      rxFrequency = Frequency::fromMHz(line.at(i).toDouble(&ok));\n      if (! ok) {\n        errMsg(err) << \"Cannot parse frequency '\" << line.at(i) << \"': Malformed frequency.\";\n        return false;\n      }\n    } else if ((\"Offset\" == header.at(i)) && (! line.at(i).isEmpty())) {\n      txFrequency = Frequency::fromMHz(line.at(i).toDouble(&ok));\n      if (! ok) {\n        errMsg(err) << \"Cannot parse offset frequency '\" << line.at(i) << \"': Malformed frequency.\";\n        return false;\n      }\n    } else if (\"Duplex\" == header.at(i)) {\n      if (! processDuplex(line.at(i), duplex, err))\n        return false;\n    } else if (\"Mode\" == header.at(i)) {\n      if (! processMode(line.at(i), mode, err))\n        return false;\n    } else if (\"Tone\" == header.at(i)) {\n      if (! processToneMode(line.at(i), toneMode, err))\n        return false;\n    } else if ((\"rToneFreq\" == header.at(i)) && (! line.at(i).isEmpty())) {\n      txTone = line.at(i).toDouble(&ok);\n      if (! ok) {\n        errMsg(err) << \"Cannot parse TX CTCSS tone frequency '\" << line.at(i) << \"'.\";\n        return false;\n      }\n    } else if ((\"cToneFreq\" == header.at(i)) && (! line.at(i).isEmpty())) {\n      rxTone = line.at(i).toDouble(&ok);\n      if (! ok) {\n        errMsg(err) << \"Cannot parse RX CTCSS tone frequency '\" << line.at(i) << \"'.\";\n        return false;\n      }\n    } else if ((\"DtcsCode\" == header.at(i)) && (! line.at(i).isEmpty())) {\n      txDTCSCode = line.at(i).toUInt(&ok);\n      if (! ok) {\n        errMsg(err) << \"Cannot decode TX DCS code '\" << line.at(i) <<\"': invalid format.\";\n        return false;\n      }\n    } else if ((\"RxDtcsCode\" == header.at(i)) && (! line.at(i).isEmpty())) {\n      rxDTCSCode = line.at(i).toUInt(&ok);\n      if (! ok) {\n        errMsg(err) << \"Cannot decode RX DCS code '\" << line.at(i) <<\"': invalid format.\";\n        return false;\n      }\n    } else if (\"DtcsPolarity\" == header.at(i)) {\n      if (! processPolarity(line.at(i), txPol, rxPol, err))\n        return false;\n    } else if (\"CrossMode\" == header.at(i)) {\n      if (! processCrossMode(line.at(i), crossMode, err))\n        return false;\n    }\n  }\n\n  // Some more sanity checks:\n  if (name.isEmpty()) {\n    errMsg(err) << \"Invalid empty name.\";\n    return false;\n  }\n\n  if ((Mode::FM == mode) || (Mode::NFM == mode)) {\n    FMChannel *fm = new FMChannel();\n\n    fm->setBandwidth(Mode::FM == mode ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow);\n    fm->setName(name);\n    fm->setRXFrequency(rxFrequency);\n\n    switch (duplex) {\n    case Duplex::None:\n      fm->setTXFrequency(fm->rxFrequency());\n      break;\n    case Duplex::Off:\n      fm->setTXFrequency(fm->rxFrequency());\n      fm->setRXOnly(true);\n      break;\n    case Duplex::Split:\n      fm->setTXFrequency(txFrequency);\n      break;\n    case Duplex::Negative:\n      fm->setTXFrequency(Frequency::fromHz(rxFrequency.inHz()-txFrequency.inHz()));\n      break;\n    case Duplex::Positive:\n      fm->setTXFrequency(Frequency::fromHz(rxFrequency.inHz()+txFrequency.inHz()));\n      break;\n    }\n\n    switch (toneMode) {\n    case ToneMode::None:\n      fm->setTXTone(SelectiveCall());\n      fm->setRXTone(SelectiveCall());\n      break;\n    case ToneMode::Tone:\n      fm->setTXTone(SelectiveCall(txTone));\n      fm->setRXTone(SelectiveCall());\n      break;\n    case ToneMode::TSQL:\n      fm->setTXTone(SelectiveCall(rxTone));\n      fm->setRXTone(SelectiveCall(rxTone));\n      break;\n    case ToneMode::TSQL_R:\n      errMsg(err) << \"Reversed CTCSS not supported.\";\n      return false;\n    case ToneMode::DTCS:\n      fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol));\n      fm->setRXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == rxPol));\n      break;\n    case ToneMode::DTCS_R:\n      errMsg(err) << \"Reversed DCS not supported.\";\n      return false;\n    case ToneMode::Cross:\n      switch (crossMode) {\n      case CrossMode::NoneTone:\n        fm->setTXTone(SelectiveCall());\n        fm->setRXTone(SelectiveCall(rxTone));\n        break;\n      case CrossMode::NoneDTCS:\n        fm->setTXTone(SelectiveCall());\n        fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol));\n        break;\n      case CrossMode::ToneNone:\n        fm->setTXTone(SelectiveCall(txTone));\n        fm->setRXTone(SelectiveCall());\n        break;\n      case CrossMode::ToneTone:\n        fm->setTXTone(SelectiveCall(txTone));\n        fm->setRXTone(SelectiveCall(rxTone));\n        break;\n      case CrossMode::ToneDTCS:\n        fm->setTXTone(SelectiveCall(txTone));\n        fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol));\n        break;\n      case CrossMode::DTCSNone:\n        fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol));\n        fm->setRXTone(SelectiveCall());\n        break;\n      case CrossMode::DTCSTone:\n        fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol));\n        fm->setRXTone(SelectiveCall(rxTone));\n        break;\n      case CrossMode::DTCSDTCS:\n        fm->setTXTone(SelectiveCall(txDTCSCode, Polarity::Reversed == txPol));\n        fm->setRXTone(SelectiveCall(rxDTCSCode, Polarity::Reversed == rxPol));\n        break;\n      }\n    }\n\n    config->channelList()->add(fm);\n    return true;\n  }\n\n  errMsg(err) << \"Unhandled channel format.\";\n\n  return false;\n}\n\n\nbool\nChirpReader::processDuplex(const QString &code, Duplex &duplex, const ErrorStack &err) {\n  if (! _duplexCodes.contains(code.simplified())) {\n    errMsg(err) << \"Cannot decode duplex '\" << code << \"': Unknown setting.\";\n    return false;\n  }\n  duplex = _duplexCodes[code.simplified()];\n  return true;\n}\n\nbool\nChirpReader::processMode(const QString &code, Mode &mode, const ErrorStack &err) {\n  if (! _modeCodes.contains(code.simplified())) {\n    errMsg(err) << \"Cannot decode mode '\" << code << \"': Unknown setting.\";\n    return false;\n  }\n  mode = _modeCodes[code.simplified()];\n  return true;\n}\n\nbool\nChirpReader::processToneMode(const QString &code, ToneMode &mode, const ErrorStack &err) {\n  if (! _toneModeCodes.contains(code.simplified())) {\n    errMsg(err) << \"Cannot decode tone mode '\" << code << \"': Unknown setting.\";\n    return false;\n  }\n  mode = _toneModeCodes[code.simplified()];\n  return true;\n}\n\nbool\nChirpReader::processPolarity(const QString &code, Polarity &txPol, Polarity &rxPol, const ErrorStack &err) {\n  if (2 != code.simplified().size()) {\n    errMsg(err) << \"Cannot parse polarity code '\" << code << \"': invalid format.\";\n    return false;\n  }\n\n  QChar tx = code.simplified().at(0), rx = code.simplified().at(1);\n  if ('N' == tx) {\n    txPol = Polarity::Normal;\n  } else if ('R' == tx) {\n    txPol = Polarity::Reversed;\n  } else {\n    errMsg(err) << \"Invalid polarity code: '\" << tx << \"': expected 'N' or 'R'.\";\n    return false;\n  }\n\n  if ('N' == rx) {\n    rxPol = Polarity::Normal;\n  } else if ('R' == rx) {\n    rxPol = Polarity::Reversed;\n  } else {\n    errMsg(err) << \"Invalid polarity code: '\" << rx << \"': expected 'N' or 'R'.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nChirpReader::processCrossMode(const QString &code, CrossMode &crossMode, const ErrorStack &err) {\n  if (! _crossModes.contains(code.simplified())) {\n    errMsg(err) << \"Cannot decode cross-mode '\" << code << \"': unknown mode.\";\n    return false;\n  }\n  crossMode = _crossModes[code.simplified()];\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of ChirpWriter\n * ********************************************************************************************* */\nbool\nChirpWriter::write(QTextStream &stream, Config *config, const ErrorStack &err) {\n  if (! writeHeader(stream, err)) {\n    errMsg(err) << \"Cannot write CHIRP CSV file.\";\n    return false;\n  }\n\n  for (int i=0, j=0; i<config->channelList()->count(); i++) {\n    if (! config->channelList()->channel(i)->is<FMChannel>())\n      continue;\n    if (! writeChannel(stream, j, config->channelList()->channel(i)->as<FMChannel>(), err)) {\n      errMsg(err) << \"Cannot encode FM channel '\" << config->channelList()->channel(i)->name()\n                  << \"'.\";\n      return false;\n    }\n    j++;\n  }\n\n  return true;\n}\n\nbool\nChirpWriter::writeHeader(QTextStream &stream, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  stream << \"Location\" << \",\" << \"Name\" << \",\" << \"Frequency\" << \",\" << \"Duplex\" << \",\"\n         << \"Offset\" << \",\"\n         << \"Tone\" << \",\" << \"rToneFreq\" << \",\" << \"cToneFreq\" << \",\"\n         << \"DtcsCode\" << \",\" \"RxDtcsCode\" << \",\" << \"DtcsPolarity\" << \",\" << \"CrossMode\" << \",\"\n         << \"Mode\" << \"\\n\";\n\n  return true;\n}\n\nbool\nChirpWriter::writeChannel(QTextStream &stream, int i, FMChannel *channel, const ErrorStack &err) {\n  stream << i << \",\" << '\"' << channel->name() << '\"';\n\n  if (! encodeFrequency(stream, channel, err)) {\n    errMsg(err) << \"Cannot encode frequencies of channel '\" << channel->name() << \"'.\";\n    return false;\n  }\n\n  if (! encodeSubTone(stream, channel, err)) {\n    errMsg(err) << \"Cannot encode sub-tone setting for channel '\" << channel->name() << \"'.\";\n    return false;\n  }\n\n  if (! encodeBandwidth(stream, channel, err)) {\n    errMsg(err) << \"Cannot encode sub-tone setting for channel '\" << channel->name() << \"'.\";\n    return false;\n  }\n  stream << \"\\n\";\n\n  return true;\n}\n\nbool\nChirpWriter::encodeFrequency(QTextStream &stream, FMChannel *channel, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  stream << \",\" << channel->rxFrequency().inMHz();\n\n  if (channel->rxOnly())\n    stream << \",\" << \"Off\" << \",\" << 0.0;\n  else if (channel->rxFrequency() == channel->txFrequency())\n    stream << \",\" << \"\" << \",\" << 0.0;\n  else if (channel->rxFrequency() > channel->txFrequency())\n    stream << \",\" << \"-\" << \",\" << channel->rxFrequency().inMHz()-channel->txFrequency().inMHz();\n  else\n    stream << \",\" << \"+\" << \",\" << channel->txFrequency().inMHz()-channel->rxFrequency().inMHz();\n\n  return true;\n}\n\nbool\nChirpWriter::encodeSubTone(QTextStream &stream, FMChannel *channel, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Serializes \", Tone, rToneFreq, cToneFreq, DtcsCode, RxDtcsCode, Polarity, CrossMode\"\n\n  if (channel->txTone().isInvalid())\n    stream << \",\" << \"\" << \",\" << 67.0 << \",\" << 67.0 << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"Tone->Tone\" ;\n  else if (channel->txTone().isCTCSS() && channel->rxTone().isInvalid())\n    stream << \",\" << \"Tone\" << \",\"\n           << QString::number(channel->txTone().Hz(), 'f', 1) << \",\" << 67.0 << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"Tone->Tone\";\n  else if (channel->txTone().isCTCSS() && (channel->txTone() == channel->rxTone()))\n    stream << \",\" << \"TSQL\" << \",\" << 67.0 << \",\"\n           << QString::number(channel->txTone().Hz(), 'f', 1) << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"Tone->Tone\";\n  else if (channel->txTone().isInvalid() && channel->rxTone().isCTCSS())\n    stream << \",\" << \"Cross\" << \",\" << 67.0 << \",\"\n           << QString::number(channel->rxTone().Hz(), 'f', 1) << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"->Tone\";\n  else if (channel->txTone().isInvalid() && channel->rxTone().isDCS())\n    stream << \",\" << \"Cross\" << \",\" << 67.0 << \",\" << 67.0 << \",\"\n           << \"023\" << \",\" << channel->rxTone().octalCode() << \",\"\n           << (channel->rxTone().isInverted() ? \"NR\" : \"NN\") << \",\"\n           << \"->DTCS\";\n  else if (channel->txTone().isCTCSS() && channel->rxTone().isCTCSS() &&\n           (channel->txTone() != channel->rxTone()))\n    stream << \",\" << \"Cross\" << \",\"\n           << QString::number(channel->txTone().Hz(), 'f', 1) << \",\"\n           << QString::number(channel->rxTone().Hz(), 'f', 1) << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"Tone->Tone\";\n  else if (channel->txTone().isCTCSS() && channel->rxTone().isDCS())\n    stream << \",\" << \"Cross\" << \",\"\n           << QString::number(channel->txTone().Hz(), 'f', 1) << \",\" << 67.0 << \",\"\n           << \"023\" << \",\" << channel->rxTone().octalCode() << \",\"\n           << (channel->rxTone().isInverted() ? \"NR\" : \"NN\") << \",\"\n           << \"Tone->DTCS\";\n  else if (channel->txTone().isDCS() && channel->rxTone().isCTCSS())\n    stream << \",\" << \"Cross\" << \",\" << 67.0 << \",\"\n           << QString::number(channel->rxTone().Hz(), 'f', 1) << \",\"\n           << channel->txTone().octalCode() << \",\" << \"023\" << \",\"\n           << (channel->txTone().isInverted() ? \"RN\" : \"NN\") << \",\"\n           << \"DTCS->Tone\";\n  else if (channel->txTone().isDCS() && channel->rxTone().isDCS() &&\n           (channel->txTone().binCode() == channel->rxTone().binCode()))\n    stream << \",\" << \"DTCS\" << \",\" << 67.0 << \",\" << 67.0 << \",\"\n           << channel->txTone().octalCode() << \",\" << channel->rxTone().octalCode() << \",\"\n           << (channel->txTone().isInverted() ? 'R' : 'N')\n           << (channel->rxTone().isInverted() ? 'R' : 'N') << \",\"\n           << \"Tone->Tone\";\n  else if (channel->txTone().isDCS() && channel->rxTone().isDCS())\n    stream << \",\" << \"Cross\" << \",\" << 67.0 << \",\" << 67.0 << \",\"\n           << channel->txTone().octalCode() << \",\" << channel->rxTone().octalCode() << \",\"\n           << (channel->txTone().isInverted() ? 'R' : 'N')\n           << (channel->rxTone().isInverted() ? 'R' : 'N') << \",\"\n           << \"DTCS->DTCS\";\n  else\n    stream << \",\" << \"\" << \",\" << 67.0 << \",\" << 67.0 << \",\"\n           << \"023\" << \",\" << \"023\" << \",\" << \"NN\" << \",\"\n           << \"Tone->Tone\" ;\n\n  return true;\n}\n\nbool\nChirpWriter::encodeBandwidth(QTextStream &stream, FMChannel *channel, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (FMChannel::Bandwidth::Narrow == channel->bandwidth())\n    stream << \",\" << \"NFM\";\n  else\n    stream << \",\" << \"FM\";\n\n  return true;\n}\n"
  },
  {
    "path": "lib/chirpformat.hh",
    "content": "/** @defgroup chrip Import/Export from/to CHIRP CSV format\n * @ingroup util\n *\n * These classes implement the (partial) import and export of FM channels from and to the CHIRP\n * CSV format.\n */\n\n#ifndef CHIRPFORMAT_HH\n#define CHIRPFORMAT_HH\n\n#include \"errorstack.hh\"\n#include <QSet>\n\nclass QTextStream;\nclass Config;\nclass FMChannel;\n\n\n/** Some common constants for the CIRP reader/writer.\n * @ingroup chirp */\nclass ChirpFormat\n{\nprotected:\n  /** Possible values for the \"duplex\" column. Specifies the frequency offset direction for the\n   *  transmit frequency wrt to the receive frequency. */\n  enum class Duplex {\n    None,      ///< No offset at all. That is, the TX and RX frequencies are equal.\n    Positive,  ///< Positive frequency offset, TX above RX frequency.\n    Negative,  ///< Negative frequency offset, TX below RX frequency.\n    Split,     ///< Explicit transmit frequency.\n    Off        ///< No transmit frequency specified. Channel is RX only.\n  };\n\n  /** Possible CHIRP channel modes. */\n  enum class Mode {\n    FM,  ///< 25kHz FM (still NBFM).\n    NFM, ///< 12.5kHz FM (also NBFM).\n    WFM, ///< 100kHz FM (WBFM, broadcast).\n    AM,  ///< AM, usually airband (not supported by qdmr yet).\n    DV,  ///< D-STAR (not supported by qdmr yet).\n    DN   ///< SystemFusion (not supported by qdmr yet).\n  };\n\n  /** Possible modes for transmitting and processing sub tones. */\n  enum class ToneMode {\n    None,    ///< No transmission of subtones.\n    Tone,    ///< Transmission of a CTCSS tone.\n    TSQL,    ///< CTCSS tone squelch.\n    TSQL_R,  ///< Inverted, CTCSS tone squelch. That is, the squelch opens if a specific CTCSS tone is not received.\n    DTCS,    ///< Transmission of a DCS code and also DCS squelch.\n    DTCS_R,  ///< Transmission of a DCS code and also DCS squelch.\n    Cross    ///< More complex setting (see @c CrossMode).\n  };\n\n  /** Polarity of DCS codes. */\n  enum class Polarity {\n    Normal,   ///< Normal DCS encoding.\n    Reversed  ///< Reversed DCS encoding.\n  };\n\n  /** Generic mode for sub tones. There is no reason to use any other mode. This one covers\n   * everything. */\n  enum class CrossMode {\n    NoneTone,   ///< No TX, RX CTCSS\n    NoneDTCS,   ///< No TX, RX DCS\n    ToneNone,   ///< TX CTCSS, no RX\n    ToneTone,   ///< TX CTCSS, RX CTCSS\n    ToneDTCS,   ///< TX CTCSS, RX DCS\n    DTCSNone,   ///< TX DCS, no RX\n    DTCSTone,   ///< TX DCS, RX CTCSS\n    DTCSDTCS    ///< TX DCS, RX DCS\n  };\n\nprotected:\n  /** Internal set of mandatory headers. */\n  static const QSet<QString> _mandatoryHeaders;\n  /** Internal used set of known headers. */\n  static const QSet<QString> _knownHeaders;\n  /** Mapping of duplex codes. */\n  static const QHash<QString, Duplex> _duplexCodes;\n  /** Mapping of mode codes. */\n  static const QHash<QString, Mode> _modeCodes;\n  /** Mapping of tone mode codes. */\n  static const QHash<QString, ToneMode> _toneModeCodes;\n  /** Mapping of cross mode codes. */\n  static const QHash<QString, CrossMode> _crossModes;\n};\n\n\n/** Implements the CHIRP CSV reader.\n * @ingroup chirp */\nclass ChirpReader: public ChirpFormat\n{\n\npublic:\n  /** Reads a CHIRP CSV file from the given stream and updates the given configuration.\n   * Please note, that the CHIRP generic CSV does not contain a functional DMR codeplug. */\n  static bool read(QTextStream &stream, Config *config, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Internal used method to read a line from the given stream.\n   * This method also implements the proper quotation parsing of strings. */\n  static bool readLine(QTextStream &stream, QStringList &list, const ErrorStack &err=ErrorStack());\n\n  /** Line parser, the header must be read before and passed to this method. The parsed channel is\n   * added to the configuration. */\n  static bool processLine(const QStringList &header, const QStringList &line,\n                          Config *config, const ErrorStack &err=ErrorStack());\n\n  /** Helper function to parse a duplex column. */\n  static bool processDuplex(const QString &code, Duplex &duplex, const ErrorStack &err=ErrorStack());\n  /** Helper function to parse a mode column. */\n  static bool processMode(const QString &code, Mode &mode, const ErrorStack &err=ErrorStack());\n  /** Helper function to parse a tone mode column. */\n  static bool processToneMode(const QString &code, ToneMode &mode, const ErrorStack &err=ErrorStack());\n  /** Helper function to parse a polarity column. */\n  static bool processPolarity(const QString &code, Polarity &txPol, Polarity &rxPol, const ErrorStack &err=ErrorStack());\n  /** Helper function to parse a cross mode column. */\n  static bool processCrossMode(const QString &code, CrossMode &crossMode, const ErrorStack &err = ErrorStack());\n};\n\n\n/** Implements the chirp writer.\n * @ingroup chirp */\nclass ChirpWriter: public ChirpFormat\n{\npublic:\n  /** Writes the (FM channels) of the given codeplug as a CHIRP CSV file into the given stream. */\n  static bool write(QTextStream &stream, Config *config, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Writes the header into the given stream. */\n  static bool writeHeader(QTextStream &stream, const ErrorStack &err = ErrorStack());\n  /** Writes a channel into the given stream. */\n  static bool writeChannel(QTextStream &stream, int i, FMChannel *channel, const ErrorStack &err=ErrorStack());\n  /** Writes frequency related columns to the given stream. */\n  static bool encodeFrequency(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack());\n  /** Writes sub tone related columns to the given stream. */\n  static bool encodeSubTone(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack());\n  /** Writes the bandwidth column to the given stream */\n  static bool encodeBandwidth(QTextStream &stream, FMChannel *channel, const ErrorStack &err = ErrorStack());\n};\n\n\n#endif // CHIRPFORMAT_HH\n"
  },
  {
    "path": "lib/codeplug.cc",
    "content": "#include \"codeplug.hh\"\n#include \"config.hh\"\n#include <QtEndian>\n#include \"logger.hh\"\n#include \"roamingchannel.hh\"\n#include \"configcopyvisitor.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of CodePlug::Flags\n * ********************************************************************************************* */\nCodeplug::Flags::Flags()\n  : TransferFlags(), _updateCodeplug(true), _autoEnableGPS(false), _autoEnableRoaming(false)\n{\n  // pass...\n}\n\n\nbool\nCodeplug::Flags::updateCodeplug() const {\n  return _updateCodeplug;\n}\n\nvoid\nCodeplug::Flags::setUpdateCodeplug(bool enable) {\n  _updateCodeplug = enable;\n}\n\n\nbool\nCodeplug::Flags::autoEnableGPS() const {\n  return _autoEnableGPS;\n}\n\nvoid\nCodeplug::Flags::setAutoEnableGPS(bool enable) {\n  _autoEnableGPS = enable;\n}\n\n\nbool\nCodeplug::Flags::autoEnableRoaming() const {\n  return _autoEnableRoaming;\n}\n\nvoid\nCodeplug::Flags::setAutoEnableRoaming(bool enable) {\n  _autoEnableRoaming = enable;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of CodePlug::Element\n * ********************************************************************************************* */\nCodeplug::Element::Element(uint8_t *ptr, size_t size)\n  : _data(ptr), _size(size)\n{\n  // pass...\n}\n\nCodeplug::Element::Element(const Element &other)\n  : _data(other._data), _size(other._size)\n{\n  // pass...\n}\n\nCodeplug::Element::~Element() {\n  // pass...\n}\n\nCodeplug::Element &\nCodeplug::Element::operator =(const Element &other) {\n  this->_data = other._data;\n  this->_size = other._size;\n  return *this;\n}\n\nbool\nCodeplug::Element::isValid() const {\n  return nullptr != _data;\n}\n\nvoid\nCodeplug::Element::clear() {\n  // pass...\n}\n\nbool\nCodeplug::Element::fill(uint8_t value, unsigned offset, int size) {\n  if (0 > size)\n    size = _size-offset;\n  if ((offset+size) > _size) {\n    logFatal() << \"Cannot fill codeplug element from \" << QString::number(offset,16)\n               << \" size \" << QString::number(size, 16) << \": overflow.\";\n    return false;\n  }\n  memset(_data+offset, value, size);\n  return true;\n}\n\nbool\nCodeplug::Element::getBit(const Offset::Bit &offset) const {\n  return getBit(offset.byte, offset.bit);\n}\n\nbool\nCodeplug::Element::getBit(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get bit at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return false;\n  }\n\n  uint8_t *ptr = (_data+offset);\n  return (1<<bit) & (*ptr);\n}\n\nvoid\nCodeplug::Element::setBit(const Offset::Bit &offset, bool value) {\n  setBit(offset.byte, offset.bit, value);\n}\nvoid\nCodeplug::Element::setBit(unsigned offset, unsigned bit, bool value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set bit at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  uint8_t *ptr = (_data+offset);\n  if (value)\n    (*ptr) |= (1<<bit);\n  else\n    (*ptr) &= ~(1<<bit);\n}\n\nvoid\nCodeplug::Element::clearBit(const Offset::Bit &offset) {\n  clearBit(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::clearBit(unsigned offset, unsigned bit) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot clear bit at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  uint8_t *ptr = (_data+offset);\n  (*ptr) &= ~(1<<bit);\n}\n\n\nuint8_t\nCodeplug::Element::getUInt2(const Offset::Bit &offset) const {\n  return getUInt2(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::setUInt2(const Offset::Bit &offset, uint8_t value) {\n  setUInt2(offset.byte, offset.bit, value);\n}\n\nuint8_t\nCodeplug::Element::getUInt2(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint2 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return 0;\n  }\n\n  return (((*(_data+offset)) >> bit) & 0b11);\n}\nvoid\nCodeplug::Element::setUInt2(unsigned offset, unsigned bit, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set uint2 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  *(_data+offset) &= ~(0b11 << bit);\n  *(_data+offset) |= ((value & 0b11)<<bit);\n}\n\nuint8_t\nCodeplug::Element::getUInt3(const Offset::Bit &offset) const {\n  return getUInt3(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::setUInt3(const Offset::Bit &offset, uint8_t value) {\n  setUInt3(offset.byte, offset.bit, value);\n}\n\nuint8_t\nCodeplug::Element::getUInt3(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint3 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return 0;\n  }\n\n  return (((*(_data+offset)) >> bit) & 0b111);\n}\nvoid\nCodeplug::Element::setUInt3(unsigned offset, unsigned bit, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set uint3 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  *(_data+offset) &= ~(0b111 << bit);\n  *(_data+offset) |= ((value & 0b111)<<bit);\n}\n\nuint8_t\nCodeplug::Element::getUInt4(const Offset::Bit &offset) const {\n  return getUInt4(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::setUInt4(const Offset::Bit &offset, uint8_t value) {\n  setUInt4(offset.byte, offset.bit, value);\n}\n\n\nuint8_t\nCodeplug::Element::getUInt4(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint4 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return 0;\n  }\n\n  return (((*(_data+offset)) >> bit) & 0b1111);\n}\nvoid\nCodeplug::Element::setUInt4(unsigned offset, unsigned bit, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set uint4 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  *(_data+offset) &= ~(0b1111 << bit);\n  *(_data+offset) |= ((value & 0b1111)<<bit);\n}\n\nuint8_t\nCodeplug::Element::getUInt5(const Offset::Bit &offset) const {\n  return getUInt5(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::setUInt5(const Offset::Bit &offset, uint8_t value) {\n  setUInt5(offset.byte, offset.bit, value);\n}\n\nuint8_t\nCodeplug::Element::getUInt5(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint5 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return 0;\n  }\n\n  return (((*(_data+offset)) >> bit) & 0b11111);\n}\nvoid\nCodeplug::Element::setUInt5(unsigned offset, unsigned bit, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint5 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  *(_data+offset) &= ~(0b11111 << bit);\n  *(_data+offset) |= ((value & 0b11111)<<bit);\n}\n\nuint8_t\nCodeplug::Element::getUInt6(const Offset::Bit &offset) const {\n  return getUInt6(offset.byte, offset.bit);\n}\nvoid\nCodeplug::Element::setUInt6(const Offset::Bit &offset, uint8_t value) {\n  setUInt6(offset.byte, offset.bit, value);\n}\n\nuint8_t\nCodeplug::Element::getUInt6(unsigned offset, unsigned bit) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint6 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return 0;\n  }\n\n  return (((*(_data+offset)) >> bit) & 0b111111);\n}\nvoid\nCodeplug::Element::setUInt6(unsigned offset, unsigned bit, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set uint6 at \" << QString::number(offset, 16)\n               << \" bit \" << bit << \": Overflow.\";\n    return;\n  }\n\n  *(_data+offset) &= ~(0b111111 << bit);\n  *(_data+offset) |= ((value & 0b111111)<<bit);\n}\n\nuint8_t\nCodeplug::Element::getUInt8(unsigned offset) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get uint8 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  return _data[offset];\n}\nvoid\nCodeplug::Element::setUInt8(unsigned offset, uint8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set uint8 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  _data[offset] = value;\n}\n\nint8_t\nCodeplug::Element::getInt8(unsigned offset) const {\n  if (offset >= _size) {\n    logFatal() << \"Cannot get int8 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n  return ((int8_t *)_data)[offset];\n}\nvoid\nCodeplug::Element::setInt8(unsigned offset, int8_t value) {\n  if (offset >= _size) {\n    logFatal() << \"Cannot set int8 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  ((int8_t *)_data)[offset] = value;\n}\n\nuint16_t\nCodeplug::Element::getUInt16_be(unsigned offset) const {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot get int16 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint16_t *ptr = (uint16_t *)(_data+offset);\n  return qFromBigEndian(*ptr);\n}\n\nuint16_t\nCodeplug::Element::getUInt16_le(unsigned offset) const {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot get int16 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  quint16 *ptr = (quint16 *)(_data+offset);\n  return qFromLittleEndian(*ptr);\n}\n\nvoid\nCodeplug::Element::setUInt16_be(unsigned offset, uint16_t value) {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot set int16 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  quint16 *ptr = (quint16 *)(_data+offset);\n  (*ptr) = qToBigEndian((quint16)value);\n}\n\nvoid\nCodeplug::Element::setUInt16_le(unsigned offset, uint16_t value) {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot set int16 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  quint16 *ptr = (quint16 *)(_data+offset);\n  (*ptr) = qToLittleEndian(value);\n}\n\nuint32_t\nCodeplug::Element::getUInt24_be(unsigned offset) const {\n  if ((offset+3) > _size) {\n    logFatal() << \"Cannot get int24 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint8_t *ptr = _data+offset;\n  return uint32_t(ptr[2]) + (uint32_t(ptr[1])<<8) + (uint32_t(ptr[0])<<16);\n}\nuint32_t\nCodeplug::Element::getUInt24_le(unsigned offset) const {\n  if ((offset+3) > _size) {\n    logFatal() << \"Cannot get int24 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint8_t *ptr = _data+offset;\n  return uint32_t(ptr[0]) + (uint32_t(ptr[1])<<8) + (uint32_t(ptr[2])<<16);\n}\nvoid\nCodeplug::Element::setUInt24_be(unsigned offset, uint32_t value) {\n  if ((offset+3) > _size) {\n    logFatal() << \"Cannot set int24 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint8_t *ptr = _data+offset;\n  ptr[0] = ((value >> 16) & 0xff);\n  ptr[1] = ((value >> 8)  & 0xff);\n  ptr[2] = ((value >> 0)  & 0xff);\n}\nvoid\nCodeplug::Element::setUInt24_le(unsigned offset, uint32_t value) {\n  if ((offset+3) > _size) {\n    logFatal() << \"Cannot set int24 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint8_t *ptr = _data+offset;\n  ptr[0] = ((value >> 0)  & 0xff);\n  ptr[1] = ((value >> 8)  & 0xff);\n  ptr[2] = ((value >> 16) & 0xff);\n}\n\nuint32_t\nCodeplug::Element::getUInt32_be(unsigned offset) const {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot get int32 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t *ptr = (uint32_t *)(_data+offset);\n  return qFromBigEndian(*ptr);\n}\nuint32_t\nCodeplug::Element::getUInt32_le(unsigned offset) const {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot get int32 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t *ptr = (uint32_t *)(_data+offset);\n  return qFromLittleEndian(*ptr);\n}\nvoid\nCodeplug::Element::setUInt32_be(unsigned offset, uint32_t value) {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot set int32 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t *ptr = (uint32_t *)(_data+offset);\n  (*ptr) = qToBigEndian(value);\n}\nvoid\nCodeplug::Element::setUInt32_le(unsigned offset, uint32_t value) {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot set int32 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t *ptr = (uint32_t *)(_data+offset);\n  (*ptr) = qToLittleEndian(value);\n}\n\nuint64_t\nCodeplug::Element::getUInt64_be(unsigned offset) const {\n  if ((offset+8) > _size) {\n    logFatal() << \"Cannot get int64 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  quint64 *ptr = (quint64 *)(_data+offset);\n  return qFromBigEndian<quint64>(*ptr);\n}\nuint64_t\nCodeplug::Element::getUInt64_le(unsigned offset) const {\n  if ((offset+8) > _size) {\n    logFatal() << \"Cannot get int64 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  quint64 *ptr = (quint64 *)(_data+offset);\n  return qFromLittleEndian<quint64>(*ptr);\n}\nvoid\nCodeplug::Element::setUInt64_be(unsigned offset, uint64_t value) {\n  if ((offset+8) > _size) {\n    logFatal() << \"Cannot set int64 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  quint64 *ptr = (quint64 *)(_data+offset);\n  (*ptr) = qToBigEndian<quint64>(value);\n}\nvoid\nCodeplug::Element::setUInt64_le(unsigned offset, uint64_t value) {\n  if ((offset+8) > _size) {\n    logFatal() << \"Cannot set int64 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  quint64 *ptr = (quint64 *)(_data+offset);\n  (*ptr) = qToLittleEndian<quint64>(value);\n}\n\nuint8_t\nCodeplug::Element::getBCD2(unsigned offset) const {\n  if ((offset+1) > _size) {\n    logFatal() << \"Cannot get BCD2 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint8_t val = getUInt8(offset);\n  return (val & 0xf) + ((val>>4) & 0xf)*10;\n}\nvoid\nCodeplug::Element::setBCD2(unsigned offset, uint8_t val) {\n  if ((offset+1) > _size) {\n    logFatal() << \"Cannot get BCD2 at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint8_t a  = (val / 10) % 10;\n  uint8_t b  = (val /  1) % 10;\n  setUInt8(offset, (a << 4) + b);\n}\n\nuint16_t\nCodeplug::Element::getBCD4_be(unsigned offset) const {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot get BCD4 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t val = getUInt16_be(offset);\n  return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000;\n}\nvoid\nCodeplug::Element::setBCD4_be(unsigned offset, uint16_t val) {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot set BCD4 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t a  = (val / 1000) % 10;\n  uint32_t b  = (val /  100) % 10;\n  uint32_t c  = (val /   10) % 10;\n  uint32_t d  = (val /    1) % 10;\n  setUInt16_be(offset, (a << 12) + (b << 8) + (c << 4) + d);\n}\nuint16_t\nCodeplug::Element::getBCD4_le(unsigned offset) const {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot get BCD4 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t val = getUInt16_le(offset);\n  return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000;\n}\nvoid\nCodeplug::Element::setBCD4_le(unsigned offset, uint16_t val) {\n  if ((offset+2) > _size) {\n    logFatal() << \"Cannot set BCD4 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t a  = (val / 1000) % 10;\n  uint32_t b  = (val /  100) % 10;\n  uint32_t c  = (val /   10) % 10;\n  uint32_t d  = (val /    1) % 10;\n  setUInt16_le(offset, (a << 12) + (b << 8) + (c << 4) + d);\n}\n\nuint32_t\nCodeplug::Element::getBCD8_be(unsigned offset) const {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot get BCD8 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t val = getUInt32_be(offset);\n  return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000 +\n      ((val>>16) & 0xf)*10000 + ((val>>20) & 0xf)*100000 + ((val>>24) & 0xf)*1000000 +\n      ((val>>28) & 0xf)*10000000;\n}\nvoid\nCodeplug::Element::setBCD8_be(unsigned offset, uint32_t val) {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot set BCD8 (be) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t a  = (val / 10000000) % 10;\n  uint32_t b  = (val /  1000000) % 10;\n  uint32_t c  = (val /   100000) % 10;\n  uint32_t d  = (val /    10000) % 10;\n  uint32_t e  = (val /     1000) % 10;\n  uint32_t f  = (val /      100) % 10;\n  uint32_t g  = (val /       10) % 10;\n  uint32_t h  = (val /        1) % 10;\n  setUInt32_be(offset, (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h);\n}\nuint32_t\nCodeplug::Element::getBCD8_le(unsigned offset) const {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot get BCD8 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return 0;\n  }\n\n  uint32_t val = getUInt32_le(offset);\n  return (val & 0xf) + ((val>>4) & 0xf)*10 + ((val>>8) & 0xf)*100 + ((val>>12) & 0xf)*1000 +\n      ((val>>16) & 0xf)*10000 + ((val>>20) & 0xf)*100000 + ((val>>24) & 0xf)*1000000 +\n      ((val>>28) & 0xf)*10000000;\n}\nvoid\nCodeplug::Element::setBCD8_le(unsigned offset, uint32_t val) {\n  if ((offset+4) > _size) {\n    logFatal() << \"Cannot set BCD8 (le) at \" << QString::number(offset, 16) << \": Overflow.\";\n    return;\n  }\n\n  uint32_t a  = (val / 10000000) % 10;\n  uint32_t b  = (val /  1000000) % 10;\n  uint32_t c  = (val /   100000) % 10;\n  uint32_t d  = (val /    10000) % 10;\n  uint32_t e  = (val /     1000) % 10;\n  uint32_t f  = (val /      100) % 10;\n  uint32_t g  = (val /       10) % 10;\n  uint32_t h  = (val /        1) % 10;\n  setUInt32_le(offset, (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h);\n}\n\nQString\nCodeplug::Element::readASCII(unsigned offset, unsigned maxlen, uint8_t eos) const {\n  QString txt;\n  uint8_t *ptr = (uint8_t *)(_data+offset);\n  for (unsigned i=0; (i<maxlen)&&(ptr[i])&&(eos!=ptr[i]); i++) {\n    txt.append(QChar::fromLatin1(ptr[i]));\n  }\n  return txt;\n}\nvoid\nCodeplug::Element::writeASCII(unsigned offset, const QString &txt, unsigned maxlen, uint8_t eos) {\n  uint8_t *ptr = (uint8_t *)(_data+offset);\n  for (unsigned i=0; i<maxlen; i++) {\n    if (i < unsigned(txt.length()))\n      ptr[i] = txt.at(i).toLatin1();\n    else\n      ptr[i] = eos;\n  }\n}\n\nQString\nCodeplug::Element::readUnicode(unsigned offset, unsigned maxlen, uint16_t eos) const {\n  QString txt;\n  uint16_t *ptr = (uint16_t *)(_data+offset);\n  for (unsigned i=0; (i<maxlen)&&(eos!=ptr[i]); i++) {\n    txt.append(QChar(ptr[i]));\n  }\n  return txt;\n}\nvoid\nCodeplug::Element::writeUnicode(unsigned offset, const QString &txt, unsigned maxlen, uint16_t eos) {\n  uint16_t *ptr = (uint16_t *)(_data+offset);\n  for (unsigned i=0; i<maxlen; i++) {\n    if (i < unsigned(txt.length()))\n      ptr[i] = txt.at(i).unicode();\n    else\n      ptr[i] = eos;\n  }\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of Codeplug::BitmapElement\n * ********************************************************************************************* */\nCodeplug::BitmapElement::BitmapElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nvoid\nCodeplug::BitmapElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nCodeplug::BitmapElement::isEncoded(unsigned int idx) const {\n  unsigned int byte = idx/8, bit = idx%8;\n  return (_data[byte] & (1 << bit));\n}\n\nvoid\nCodeplug::BitmapElement::setEncoded(unsigned int idx, bool enable) {\n  unsigned int byte = idx/8, bit = idx%8;\n  if (enable)\n    _data[byte] |= (1 << bit);\n  else\n    _data[byte] &= ~(1 << bit);\n}\n\nvoid\nCodeplug::BitmapElement::enableFirst(unsigned int n) {\n  unsigned int byte = n/8, bit=n%8;\n  memset(_data, 0xff, byte);\n  for (unsigned int i=0; i<bit; i++) {\n    _data[byte] |= (1<<i);\n  }\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of Codeplug::InvertedBitmapElement\n * ********************************************************************************************* */\nCodeplug::InvertedBitmapElement::InvertedBitmapElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nvoid\nCodeplug::InvertedBitmapElement::clear() {\n  memset(_data, 0xff, _size);\n}\n\nbool\nCodeplug::InvertedBitmapElement::isEncoded(unsigned int idx) const {\n  unsigned int byte = idx/8, bit = idx%8;\n  return 0 == (_data[byte] & (1 << bit));\n}\n\nvoid\nCodeplug::InvertedBitmapElement::setEncoded(unsigned int idx, bool enable) {\n  unsigned int byte = idx/8, bit = idx%8;\n  if (enable)\n    _data[byte] &= ~(1 << bit);\n  else\n    _data[byte] |= (1 << bit);\n}\n\nvoid\nCodeplug::InvertedBitmapElement::enableFirst(unsigned int n) {\n  unsigned int byte = n/8, bit=n%8;\n  memset(_data, 0x00, byte);\n  for (unsigned int i=0; i<bit; i++) {\n    _data[byte] &= ~(1<<i);\n  }\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of CodePlug::Context\n * ********************************************************************************************* */\nCodeplug::Context::Context(Config *config)\n  : _config(config), _satellites(nullptr), _tables()\n{\n  // Add tables for common elements\n  addTable(&DMRRadioID::staticMetaObject);\n  addTable(&DigitalContact::staticMetaObject);\n  addTable(&DTMFContact::staticMetaObject);\n  addTable(&RXGroupList::staticMetaObject);\n  addTable(&Channel::staticMetaObject);\n  addTable(&Zone::staticMetaObject);\n  addTable(&ScanList::staticMetaObject);\n  addTable(&DMRAPRSSystem::staticMetaObject);\n  addTable(&FMAPRSSystem::staticMetaObject);\n  addTable(&RoamingChannel::staticMetaObject);\n  addTable(&RoamingZone::staticMetaObject);\n  addTable(&SMSTemplate::staticMetaObject);\n  addTable(&BasicEncryptionKey::staticMetaObject);\n  addTable(&ARC4EncryptionKey::staticMetaObject);\n  addTable(&AESEncryptionKey::staticMetaObject);\n}\n\nConfig  *\nCodeplug::Context::config() const {\n  return _config;\n}\n\nSatelliteDatabase *\nCodeplug::Context::satellites() const {\n  return _satellites;\n}\n\n\nbool\nCodeplug::Context::hasTable(const QMetaObject *obj, bool exact) const {\n  // Find a matching table\n  if (_tables.contains(obj->className()))\n    return true;\n  if (exact)\n    return false;\n  if (obj->superClass())\n    return hasTable(obj->superClass(), false);\n  return false;\n}\n\nCodeplug::Context::Table &\nCodeplug::Context::getTable(const QMetaObject *obj) {\n  if (_tables.contains(obj->className()))\n    return _tables[obj->className()];\n  return getTable(obj->superClass());\n}\n\nbool\nCodeplug::Context::addTable(const QMetaObject *obj) {\n  if (hasTable(obj, true))\n    return false;\n  _tables.insert(obj->className(), Table());\n  return true;\n}\n\nbool\nCodeplug::Context::remTable(const QMetaObject *obj, bool exact) {\n  if (! hasTable(obj, exact))\n    return false;\n  if (_tables.contains(obj->className()))\n    return _tables.remove(obj->className());\n  return remTable(obj->superClass());\n}\n\n\nConfigItem *\nCodeplug::Context::obj(const QMetaObject *elementType, unsigned idx, bool exact) {\n  if (! hasTable(elementType, exact))\n    return nullptr;\n  return getTable(elementType).objects.value(idx, nullptr);\n}\n\nint\nCodeplug::Context::index(ConfigItem *obj) {\n  if (nullptr == obj)\n    return -1;\n  if (! hasTable(obj->metaObject()))\n    return -1;\n  return getTable(obj->metaObject()).indices.value(obj, -1);\n}\n\nbool\nCodeplug::Context::add(ConfigItem *obj, unsigned idx) {\n  if (! hasTable(obj->metaObject()))\n    return false;\n  if (! getTable(obj->metaObject()).indices.contains(obj))\n    getTable(obj->metaObject()).indices.insert(obj, idx);\n  if (! getTable(obj->metaObject()).objects.contains(idx))\n    getTable(obj->metaObject()).objects.insert(idx, obj);\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of CodePlug\n * ********************************************************************************************* */\nCodeplug::Codeplug(QObject *parent)\n  : DFUFile(parent)\n{\n\t// pass...\n}\n\nCodeplug::~Codeplug() {\n\t// pass...\n}\n\nConfig *\nCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  return ConfigCopy::copy(config, err)->as<Config>();\n}\n\nbool\nCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  Q_UNUSED(config); Q_UNUSED(err);\n  return true;\n}\n"
  },
  {
    "path": "lib/codeplug.hh",
    "content": "#ifndef CODEPLUG_HH\n#define CODEPLUG_HH\n\n#include <QObject>\n#include <QHash>\n#include \"dfufile.hh\"\n#include \"transferflags.hh\"\n\nclass Config;\nclass ConfigItem;\nclass SatelliteDatabase;\n\n\n/** This class defines the interface all device-specific code-plugs must implement.\n * Device-specific codeplugs are derived from the common configuration and implement the\n * construction/parsing of the device specific binary configuration. */\nclass Codeplug: public DFUFile\n{\n  Q_OBJECT\n\npublic:\n  /** Certain flags passed to CodePlug::encode to control the transfer and encoding of the\n   * codeplug. */\n  class Flags: public TransferFlags {\n  public:\n    /** Default constructor, enables code-plug update and disables automatic GPS/APRS and roaming. */\n    Flags();\n\n  public:\n    /** If @c true, the codeplug will first be downloaded from the device, updated from the\n     * abstract config and then written back to the device. This maintains the user-settings\n     * made within the device or manufacturer CPS. If @c false, the code-plug gets overridden\n     * entirely using some default settings. Default @c true. */\n    bool updateCodeplug() const;\n    /** Sets if the codeplug gets updated. */\n    void setUpdateCodeplug(bool enable);\n\n    /** If @c true enables GPS when there is a GPS or APRS system defined that is used by any\n     * channel. This may cause automatic transmissions, hence the default is @c false. */\n    bool autoEnableGPS() const;\n    /** Sets if the GPS gets enabled automatically. */\n    void setAutoEnableGPS(bool enable);\n\n    /** If @c true enables automatic roaming when there is a roaming zone defined that is used by any\n     * channel. This may cause automatic transmissions, hence the default is @c false. */\n    bool autoEnableRoaming() const;\n    /** Sets if roaming gets enabled automatically. */\n    void setAutoEnableRoaming(bool enable);\n\n  protected:\n    bool _updateCodeplug;\n    bool _autoEnableGPS;\n    bool _autoEnableRoaming;\n  };\n\n\n  /** Represents the abstract base class of all codeplug elements.\n   *\n   * That is a memory region within the codeplug that encodes a specific element. E.g., channels,\n   * contacts, zones, etc. This class provides some helper methods to access specific members of\n   * the element.\n   *\n   * @since 0.9.0 */\n  class Element\n  {\n  protected:\n    /** Base class for Offsets. */\n    struct Offset {\n      /** Some type to specify a bit offset. That is the byte-offset and bit of that byte. */\n      struct Bit {\n        /** The byte offset. */\n        const unsigned int byte;\n        /** The bit within the byte. */\n        const unsigned int bit;\n\n        /** Implements a simple increment. */\n        inline Bit operator+ (unsigned int bits) const {\n          unsigned int tmp = 8 * byte + (7-bit) + bits;\n          return {tmp/8, (7 - (tmp % 8))};\n        }\n\n        /** Implements a simple increment. */\n        inline Bit operator- (unsigned int bits) const {\n          unsigned int tmp = 8 * byte + (7-bit) - bits;\n          return {tmp/8, (7 - (tmp % 8))};\n        }\n      };\n    };\n\n  public:\n    /** Base class for Limits. */\n    struct Limit {\n      /** Holds a range of values [min, max]. */\n      template <class T> struct Range {\n        /// Lower bound.\n        const T min;\n        /// Upper bound.\n        const T max;\n        /// Limits the value to the range.\n        inline T limit(const T &value) const {\n          return std::min(max, std::max(min, value));\n        }\n        /** Checks if value is in range. */\n        inline bool in(const T &value) const {\n          return (value <= max) && (value >= min);\n        }\n        /** Maps a value from this range to the given range. */\n        inline T mapTo(const Range<T> &other, const T &value) const {\n          T myD = max-min, oD = other.max-other.min;\n          return ((limit(value)-min)*oD)/myD + other.min;\n        }\n      };\n    };\n\n  protected:\n    /** Hidden constructor.\n     * @param ptr Specifies the pointer to the element within the codeplug.\n     * @param size Specifies the size of the element in bytes. */\n    Element(uint8_t *ptr, size_t size);\n\n  public:\n    /** Copy constructor. */\n    Element(const Element &other);\n    /** Destructor. */\n    virtual ~Element();\n\n    /** Copy assignment. */\n    Element &operator=(const Element &other);\n\n    /** Returns @c true if the pointer is not null. */\n    virtual bool isValid() const;\n    /** Abstract method to reset the element within the codeplug. Any device specific element\n     * should implement this method. */\n    virtual void clear();\n\n    /** Fills memsets the entire element to the given value. */\n    bool fill(uint8_t value, unsigned offset=0, int size=-1);\n\n    /** Reads a specific bit at the given byte-offset. */\n    bool getBit(const Offset::Bit &offset) const;\n    /** Reads a specific bit at the given byte-offset. */\n    bool getBit(unsigned offset, unsigned bit) const;\n    /** Sets a specific bit at the given byte-offset. */\n    void setBit(const Offset::Bit &offset, bool value=true);\n    /** Sets a specific bit at the given byte-offset. */\n    void setBit(unsigned offset, unsigned bit, bool value=true);\n    /** Clears a specific bit at the given byte-offset. */\n    void clearBit(unsigned offset, unsigned bit);\n    /** Clears a specific bit. */\n    void clearBit(const Offset::Bit &offset);\n\n    /** Reads a 2bit unsigned integer at the given bit-offset. */\n    uint8_t getUInt2(const Offset::Bit &offset) const;\n    /** Reads a 2bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt2(unsigned offset, unsigned bit) const;\n    /** Stores a 2bit unsigned integer at the given bit-offset. */\n    void setUInt2(const Offset::Bit &offset, uint8_t value);\n    /** Stores a 2bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt2(unsigned offset, unsigned bit, uint8_t value);\n\n    /** Reads a 3bit unsigned integer at the given bit-offset. */\n    uint8_t getUInt3(const Offset::Bit &offset) const;\n    /** Reads a 3bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt3(unsigned offset, unsigned bit) const;\n    /** Stores a 3bit unsigned integer at the given bit-offset. */\n    void setUInt3(const Offset::Bit &offset, uint8_t value);\n    /** Stores a 3bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt3(unsigned offset, unsigned bit, uint8_t value);\n\n    /** Reads a 4bit unsigned integer at the given bit-offset. */\n    uint8_t getUInt4(const Offset::Bit &offset) const;\n    /** Reads a 4bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt4(unsigned offset, unsigned bit) const;\n    /** Stores a 4bit unsigned integer at the given bit-offset. */\n    void setUInt4(const Offset::Bit &offset, uint8_t value);\n    /** Stores a 4bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt4(unsigned offset, unsigned bit, uint8_t value);\n\n    /** Reads a 5bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt5(const Offset::Bit &offset) const;\n    /** Reads a 5bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt5(unsigned offset, unsigned bit) const;\n    /** Stores a 5bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt5(const Offset::Bit &offset, uint8_t value);\n    /** Stores a 5bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt5(unsigned offset, unsigned bit, uint8_t value);\n\n    /** Reads a 6bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt6(const Offset::Bit &offset) const;\n    /** Reads a 6bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt6(unsigned offset, unsigned bit) const;\n    /** Stores a 6bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt6(const Offset::Bit &offset, uint8_t value);\n    /** Stores a 6bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt6(unsigned offset, unsigned bit, uint8_t value);\n\n    /** Reads a 8bit unsigned integer at the given byte- and bit-offset. */\n    uint8_t getUInt8(unsigned offset) const;\n    /** Reads a 8bit unsigned integer at the given byte- and bit-offset. */\n    void setUInt8(unsigned offset, uint8_t value);\n    /** Reads a 8bit signed integer at the given byte- and bit-offset. */\n    int8_t getInt8(unsigned offset) const;\n    /** Reads a 8bit signed integer at the given byte- and bit-offset. */\n    void setInt8(unsigned offset, int8_t value);\n\n    /** Reads a 16bit big-endian unsigned integer at the given byte-offset. */\n    uint16_t getUInt16_be(unsigned offset) const;\n    /** Reads a 16bit little-endian unsigned integer at the given byte-offset. */\n    uint16_t getUInt16_le(unsigned offset) const;\n    /** Stores a 16bit big-endian unsigned integer at the given byte-offset. */\n    void setUInt16_be(unsigned offset, uint16_t value);\n    /** Stores a 16bit little-endian unsigned integer at the given byte-offset. */\n    void setUInt16_le(unsigned offset, uint16_t value);\n\n    /** Reads a 24bit big-endian unsigned integer at the given byte-offset. */\n    uint32_t getUInt24_be(unsigned offset) const;\n    /** Reads a 24bit little-endian unsigned integer at the given byte-offset. */\n    uint32_t getUInt24_le(unsigned offset) const;\n    /** Stores a 24bit big-endian unsigned integer at the given byte-offset. */\n    void setUInt24_be(unsigned offset, uint32_t value);\n    /** Stores a 24bit little-endian unsigned integer at the given byte-offset. */\n    void setUInt24_le(unsigned offset, uint32_t value);\n\n    /** Reads a 32bit big-endian unsigned integer at the given byte-offset. */\n    uint32_t getUInt32_be(unsigned offset) const;\n    /** Reads a 32bit little-endian unsigned integer at the given byte-offset. */\n    uint32_t getUInt32_le(unsigned offset) const;\n    /** Stores a 32bit big-endian unsigned integer at the given byte-offset. */\n    void setUInt32_be(unsigned offset, uint32_t value);\n    /** Stores a 32bit little-endian unsigned integer at the given byte-offset. */\n    void setUInt32_le(unsigned offset, uint32_t value);\n\n    /** Reads a 64bit big-endian unsigned integer at the given byte-offset. */\n    uint64_t getUInt64_be(unsigned offset) const;\n    /** Reads a 64bit little-endian unsigned integer at the given byte-offset. */\n    uint64_t getUInt64_le(unsigned offset) const;\n    /** Stores a 64bit big-endian unsigned integer at the given byte-offset. */\n    void setUInt64_be(unsigned offset, uint64_t value);\n    /** Stores a 64bit little-endian unsigned integer at the given byte-offset. */\n    void setUInt64_le(unsigned offset, uint64_t value);\n\n    /** Reads a 2-digit (1-byte/8bit) BDC value in big-endian at the given byte-offset. */\n    uint8_t getBCD2(unsigned offset) const;\n    /** Stores a 2-digit (1-byte/8bit) BDC value in big-endian at the given byte-offset. */\n    void setBCD2(unsigned offset, uint8_t value);\n\n    /** Reads a 4-digit (2-byte/16bit) BDC value in big-endian at the given byte-offset. */\n    uint16_t getBCD4_be(unsigned offset) const;\n    /** Stores a 4-digit (2-byte/16bit) BDC value in big-endian at the given byte-offset. */\n    void setBCD4_be(unsigned offset, uint16_t value);\n    /** Reads a 4-digit (2-byte/16bit) BDC value in little-endian at the given byte-offset. */\n    uint16_t getBCD4_le(unsigned offset) const;\n    /** Stores a 4-digit (1-byte/16bit) BDC value in little-endian at the given byte-offset. */\n    void setBCD4_le(unsigned offset, uint16_t value);\n\n    /** Reads a 8-digit (4-byte/32bit) BDC value in big-endian at the given byte-offset. */\n    uint32_t getBCD8_be(unsigned offset) const;\n    /** Stores a 8-digit (4-byte/32bit) BDC value in big-endian at the given byte-offset. */\n    void setBCD8_be(unsigned offset, uint32_t value);\n    /** Reads a 8-digit (4-byte/32bit) BDC value in little-endian at the given byte-offset. */\n    uint32_t getBCD8_le(unsigned offset) const;\n    /** Stores a 8-digit (4-byte/32bit) BDC value in little-endian at the given byte-offset. */\n    void setBCD8_le(unsigned offset, uint32_t value);\n\n    /** Reads up to @c maxlen ASCII chars at the given byte-offset using @c eos as the string termination char. */\n    QString readASCII(unsigned offset, unsigned maxlen, uint8_t eos=0x00) const;\n    /** Stores up to @c maxlen ASCII chars at the given byte-offset using @c eos as the string termination char.\n     * The stored string gets padded with @c eos to @c maxlen. */\n    void writeASCII(unsigned offset, const QString &txt, unsigned maxlen, uint8_t eos=0x00);\n\n    /** Reads up to @c maxlen unicode chars at the given byte-offset using @c eos as the string termination char. */\n    QString readUnicode(unsigned offset, unsigned maxlen, uint16_t eos=0x0000) const;\n    /** Stores up to @c maxlen unicode chars at the given byte-offset using @c eos as the string termination char.\n     * The stored string gets padded with @c eos to @c maxlen. */\n    void writeUnicode(unsigned offset, const QString &txt, unsigned maxlen, uint16_t eos=0x0000);\n\n  protected:\n    /** Holds the pointer to the element. */\n    uint8_t *_data;\n    /** Holds the size of the element. */\n    size_t _size;\n  };\n\n  /** Represents the base class for bitmaps in codeplugs. */\n  class BitmapElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Clears the bitmap, disables all channels. */\n    void clear();\n\n    /** Returns @c true if the given index is valid. */\n    virtual bool isEncoded(unsigned int idx) const ;\n    /** Enables/disables the specified index. */\n    virtual void setEncoded(unsigned int idx, bool enable);\n    /** Enables the first n elements. */\n    virtual void enableFirst(unsigned int n);\n  };\n\n\n  /** Represents the base class for inverted bitmaps in codeplugs. */\n  class InvertedBitmapElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    InvertedBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Clears the bitmap, disables all channels. */\n    void clear();\n\n    /** Returns @c true if the given index is valid. */\n    virtual bool isEncoded(unsigned int idx) const ;\n    /** Enables/disables the specified index. */\n    virtual void setEncoded(unsigned int idx, bool enable);\n    /** Enables the first n elements. */\n    virtual void enableFirst(unsigned int n);\n  };\n\n\n  /** Base class for all codeplug contexts.\n   * Each device specific codeplug may extend this class to allow for device specific elements to\n   * be indexed in a separate index. By default tables for @c DigitalContact, @c RXGroupList,\n   * @c Channel, @c Zone and @c ScanList are defined. For any other type, an additional table must\n   * be defined first using @c addTable.\n   * @since 0.9.0 */\n  class Context\n  {\n  public:\n    /** Empty constructor. */\n    explicit Context(Config *config);\n\n    /** Returns the reference to the config object. */\n    Config *config() const;\n\n    /** Returns a reference to the satellite database. */\n    SatelliteDatabase *satellites() const;\n\n    /** Resolves the given index for the specifies element type.\n     * @returns @c nullptr if the index is not defined or the type is unknown. */\n    ConfigItem *obj(const QMetaObject *elementType, unsigned idx, bool exact=false);\n    /** Returns the index for the given object.\n     * @returns -1 if no index is associated with the object or its type is unknown. */\n    int index(ConfigItem *obj);\n    /** Associates the given object with the given index. */\n    bool add(ConfigItem *obj, unsigned idx);\n\n    /** Adds a table for the given type. */\n    bool addTable(const QMetaObject *obj);\n    /** Returns @c true if a table is defined for the given type. */\n    bool hasTable(const QMetaObject *obj, bool exact=false) const;\n    /** Deletes a table. */\n    bool remTable(const QMetaObject *obj, bool exact=false);\n\n    /** Returns the object associated by the given index and type. */\n    template <class T>\n    T* get(unsigned idx) {\n      return this->obj(&(T::staticMetaObject), idx)->template as<T>();\n    }\n\n    /** Returns @c true, if the given index is defined for the specified type. */\n    template <class T>\n    bool has(unsigned idx) {\n      return nullptr != this->obj(&(T::staticMetaObject), idx)->template as<T>();\n    }\n\n    /** Returns the number of elements for the specified type. */\n    template <class T>\n    unsigned int count(bool exact=true) {\n      if (! hasTable(&T::staticMetaObject, exact))\n        return 0;\n      return getTable(&T::staticMetaObject).indices.size();\n    }\n\n  protected:\n    /** Internal used table type to associate objects and indices. */\n    class Table {\n    public:\n      /** The index->object map. */\n      QHash<unsigned, ConfigItem *> objects;\n      /** The object->index map. */\n      QHash<ConfigItem *, unsigned> indices;\n    };\n\n  protected:\n    /** Returns a reference to the table for the given type. */\n    Table &getTable(const QMetaObject *obj);\n\n  protected:\n    /** A weak reference to the config object. */\n    Config *_config;\n    /** A weak reference to the satellite database. */\n    SatelliteDatabase *_satellites;\n    /** Table of tables. */\n    QHash<QString, Table> _tables;\n  };\n\nprotected:\n  /** Hidden default constructor. */\n  explicit Codeplug(QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~Codeplug();\n\n  /** Indexes all elements of the codeplug.\n   * This method must be implemented by any device or vendor specific codeplug to map config\n   * objects to indices used within the binary codeplug to address each element (e.g., channels,\n   * contacts etc.). */\n  virtual bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const = 0;\n\n  /** Decodes a binary codeplug to the given abstract configuration @c config.\n   * This must be implemented by the device-specific codeplug. */\n  virtual bool decode(Config *config, const ErrorStack &err=ErrorStack()) = 0;\n  /** Returns a post-processed configuration of the decoded config. By default, the passed\n   * config is returned. */\n  virtual bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n\n  /** Returns a prepared configuration for this particular radio. All unsupported features are\n   *  removed from the copy. The default implementation only copies the config. */\n  virtual Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n  /** Encodes a given abstract configuration (@c config) to the device specific binary code-plug.\n   * This must be implemented by the device-specific codeplug. */\n  virtual bool encode(Config *config, const Flags &flags=Flags(), const ErrorStack &err=ErrorStack()) = 0;\n};\n\n#endif // CODEPLUG_HH\n"
  },
  {
    "path": "lib/commercial_extension.cc",
    "content": "#include \"commercial_extension.hh\"\n#include \"encryptionextension.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of CommercialExtension\n * ********************************************************************************************* */\nCommercialExtension::CommercialExtension(QObject *parent)\n  : ConfigExtension(parent), _encryptionKeys(new EncryptionKeys(this))\n{\n  // pass...\n}\n\nConfigItem *\nCommercialExtension::clone() const {\n  CommercialExtension *ext = new CommercialExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nEncryptionKeys *\nCommercialExtension::encryptionKeys() const {\n  return _encryptionKeys;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of CommercialChannelExtension\n * ********************************************************************************************* */\nCommercialChannelExtension::CommercialChannelExtension(QObject *parent)\n  : ConfigExtension(parent), _encryptionKey()\n{\n  // pass...\n}\n\nConfigItem *\nCommercialChannelExtension::clone() const {\n  CommercialChannelExtension *ex = new CommercialChannelExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\nEncryptionKeyReference *\nCommercialChannelExtension::encryptionKeyRef() {\n  return &_encryptionKey;\n}\n\nEncryptionKey *\nCommercialChannelExtension::encryptionKey() {\n  return _encryptionKey.as<EncryptionKey>();\n}\n\nvoid\nCommercialChannelExtension::setEncryptionKey(EncryptionKey *key) {\n  if (_encryptionKey.as<EncryptionKey>() == key)\n    return;\n  _encryptionKey.set(key);\n  emit modified(this);\n}\n\n\n"
  },
  {
    "path": "lib/commercial_extension.hh",
    "content": "#ifndef COMMERCIALEXTENSION_HH\n#define COMMERCIALEXTENSION_HH\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"encryptionextension.hh\"\n\n/** Implements the generic extension for the codeplug to represent some commercial features of DMR.\n * @ingroup conf */\nclass CommercialExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** Read only property returning holding the list of encryption keys. */\n  Q_PROPERTY(EncryptionKeys* encryptionKeys READ encryptionKeys)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit CommercialExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the list of encryption keys. */\n  EncryptionKeys *encryptionKeys() const;\n\nprotected:\n  /** Owns the instance of the encryption key list. */\n  EncryptionKeys *_encryptionKeys;\n};\n\n\n/** Implements the generic extension for all channels configuring commercial features of DMR.\n * @ingroup conf */\nclass CommercialChannelExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** Holds a reference to the associated encryption key. */\n  Q_PROPERTY(EncryptionKeyReference* encryptionKey READ encryptionKeyRef)\n\npublic:\n  /** Empty constructor. */\n  Q_INVOKABLE explicit CommercialChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the reference to the encryption key. */\n  EncryptionKeyReference *encryptionKeyRef();\n  /** Returns the referenced encryption key. */\n  EncryptionKey *encryptionKey();\n  /** References the given encryption key. */\n  void setEncryptionKey(EncryptionKey *key);\n\nprotected:\n  /** The actual reference to the the encryption key. */\n  EncryptionKeyReference _encryptionKey;\n};\n\n\n#endif // COMMERCIALEXTENSION_HH\n"
  },
  {
    "path": "lib/config.cc",
    "content": "#include \"config.hh\"\n#include \"config.h\"\n\n#include \"rxgrouplist.hh\"\n#include \"channel.hh\"\n#include \"csvreader.hh\"\n#include \"userdatabase.hh\"\n#include \"logger.hh\"\n\n#include <QTextStream>\n#include <QDateTime>\n#include <QFile>\n#include <QMetaProperty>\n#include <cmath>\n\n\n/* ********************************************************************************************* *\n * Implementation of Config\n * ********************************************************************************************* */\nConfig::Config(QObject *parent)\n  : ConfigItem(parent), _modified(false), _settings(new RadioSettings(this)),\n    _radioIDs(new RadioIDList(this)), _contacts(new ContactList(this)),\n    _rxGroupLists(new RXGroupLists(this)), _channels(new ChannelList(this)),\n    _zones(new ZoneList(this)), _scanlists(new ScanLists(this)),\n    _gpsSystems(new PositionReportingSystems(this)),\n    _roamingChannels(new RoamingChannelList(this)), _roamingZones(new RoamingZoneList(this)),\n    _tytExtension(nullptr), _commercialExtension(new CommercialExtension(this)),\n    _smsExtension(new SMSExtension(this))\n{\n  connect(_settings, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n  connect(_radioIDs, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_radioIDs, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_radioIDs, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_contacts, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_contacts, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_rxGroupLists, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_rxGroupLists, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_rxGroupLists, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_channels, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_channels, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_channels, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_zones, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_zones, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_zones, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_scanlists, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_scanlists, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_scanlists, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_gpsSystems, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_gpsSystems, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_gpsSystems, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_roamingChannels, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_roamingChannels, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_roamingChannels, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n  connect(_roamingZones, SIGNAL(elementAdded(int)), this, SLOT(onConfigModified()));\n  connect(_roamingZones, SIGNAL(elementRemoved(int)), this, SLOT(onConfigModified()));\n  connect(_roamingZones, SIGNAL(elementModified(int)), this, SLOT(onConfigModified()));\n\n  connect(_commercialExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n  connect(_smsExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n}\n\nbool\nConfig::copy(const ConfigItem &other) {\n  const Config *conf = other.as<Config>();\n  if ((nullptr==conf) || (! ConfigItem::copy(other)))\n    return false;\n\n  _settings->copy(*conf->settings());\n  _radioIDs->copy(*conf->radioIDs());\n  _contacts->copy(*conf->contacts());\n  _rxGroupLists->copy(*conf->rxGroupLists());\n  _channels->copy(*conf->channelList());\n  _zones->copy(*conf->zones());\n  _scanlists->copy(*conf->scanlists());\n  _gpsSystems->copy(*conf->posSystems());\n  _roamingChannels->copy(*conf->roamingChannels());\n  _roamingZones->copy(*conf->roamingZones());\n\n  return true;\n}\n\nConfigItem *\nConfig::clone() const {\n  Config *conf = new Config();\n  if (! conf->copy(*this)) {\n    conf->deleteLater();\n    return nullptr;\n  }\n  return conf;\n}\n\nbool\nConfig::isModified() const {\n  return _modified;\n}\nvoid\nConfig::setModified(bool modified) {\n  _modified = modified;\n  emit this->modified(this);\n}\n\nbool\nConfig::toYAML(QTextStream &stream, const ErrorStack &err) {\n  ConfigItem::Context context;\n  // Label all codeplug elements\n  if (! this->label(context, err))\n    return false;\n  // Serialize into YAML\n  YAML::Node doc = serialize(context, err);\n  if (doc.IsNull())\n    return false;\n  // Print YAML\n  YAML::Emitter emitter;\n  emitter << YAML::BeginDoc << doc << YAML::EndDoc;\n  stream << QString(emitter.c_str());\n  return true;\n}\n\nbool\nConfig::populate(YAML::Node &node, const Context &context, const ErrorStack &err)\n{\n  node[\"version\"] = VERSION_STRING;\n\n  if ((node[\"settings\"]= _settings->serialize(context, err)).IsNull())\n    return false;\n\n  if ((node[\"radioIDs\"] = _radioIDs->serialize(context, err)).IsNull())\n    return false;\n\n  if ((node[\"contacts\"] = _contacts->serialize(context, err)).IsNull())\n    return false;\n\n  if ((node[\"groupLists\"] = _rxGroupLists->serialize(context, err)).IsNull())\n    return false;\n\n  if ((node[\"channels\"] = _channels->serialize(context, err)).IsNull())\n    return false;\n\n  if ((node[\"zones\"] = _zones->serialize(context, err)).IsNull())\n    return false;\n\n  if (_scanlists->count()) {\n    if ((node[\"scanLists\"] = _scanlists->serialize(context, err)).IsNull())\n      return false;\n  }\n\n  if (_gpsSystems->count()) {\n    if ((node[\"positioning\"] = _gpsSystems->serialize(context, err)).IsNull())\n      return false;\n  }\n\n  if (_roamingChannels->count()) {\n    if ((node[\"roamingChannels\"] = _roamingChannels->serialize(context, err)).IsNull())\n      return false;\n  }\n\n  if (_roamingZones->count()) {\n    if ((node[\"roamingZones\"] = _roamingZones->serialize(context, err)).IsNull())\n      return false;\n  }\n\n  if (! ConfigItem::populate(node, context, err))\n    return false;\n\n  return true;\n}\n\nRadioSettings *\nConfig::settings() const {\n  return _settings;\n}\n\nRadioIDList *\nConfig::radioIDs() const {\n  return _radioIDs;\n}\n\nContactList *\nConfig::contacts() const {\n  return _contacts;\n}\n\nRXGroupLists *\nConfig::rxGroupLists() const {\n  return _rxGroupLists;\n}\n\nChannelList *\nConfig::channelList() const {\n  return _channels;\n}\n\nZoneList *\nConfig::zones() const {\n  return _zones;\n}\n\nScanLists *\nConfig::scanlists() const {\n  return _scanlists;\n}\n\nPositionReportingSystems *\nConfig::posSystems() const {\n  return _gpsSystems;\n}\n\nRoamingChannelList *\nConfig::roamingChannels() const {\n  return _roamingChannels;\n}\n\nRoamingZoneList *\nConfig::roamingZones() const {\n  return _roamingZones;\n}\n\nbool\nConfig::requiresRoaming() const {\n  // Check is roaming should be enabled\n  bool chHasRoaming = false;\n  for (int i=0; i<channelList()->count(); i++) {\n    const DMRChannel *digi = channelList()->channel(i)->as<const DMRChannel>();\n    if (nullptr == digi)\n      continue;\n    if (nullptr != digi->roaming()) {\n      chHasRoaming = true;\n      break;\n    }\n  }\n  return chHasRoaming;\n}\n\nbool\nConfig::requiresGPS() const {\n  // Check is GPS should be enabled\n  bool chHasGPS = false;\n  for (int i=0; i<channelList()->count(); i++) {\n    Channel *ch = channelList()->channel(i);\n    // For analog channels if APRS system is set or\n    // for digital channels if any positioning system is set\n    if ( (ch->is<FMChannel>() && ch->as<FMChannel>()->aprs()) ||\n         (ch->is<DMRChannel>() && ch->as<DMRChannel>()->aprs()) ) {\n      chHasGPS = true;\n      break;\n    }\n  }\n  return chHasGPS;\n}\n\nvoid\nConfig::clear() {\n  ConfigItem::clear();\n\n  // Reset settings\n  _settings->clear();\n\n  // Reset lists\n  _radioIDs->clear();\n  _contacts->clear();\n  _rxGroupLists->clear();\n  _channels->clear();\n  _zones->clear();\n  _scanlists->clear();\n  _gpsSystems->clear();\n  _roamingChannels->clear();\n  _roamingZones->clear();\n\n  // Clear extensions\n  commercialExtension()->clear();\n  smsExtension()->clear();\n  setTyTExtension(nullptr);\n\n  onConfigModified();\n}\n\nconst Config *\nConfig::config() const {\n  return this;\n}\n\n\nCommercialExtension *\nConfig::commercialExtension() const {\n  return _commercialExtension;\n}\n\nSMSExtension *\nConfig::smsExtension() const {\n  return _smsExtension;\n}\n\nTyTConfigExtension *\nConfig::tytExtension() const {\n  return _tytExtension;\n}\nvoid\nConfig::setTyTExtension(TyTConfigExtension *ext) {\n  if (_tytExtension == ext)\n    return;\n  if (_tytExtension)\n    _tytExtension->deleteLater();\n  _tytExtension = ext;\n  if (_tytExtension) {\n    _tytExtension->setParent(this);\n    connect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n    onConfigModified();\n  }\n}\n\nvoid\nConfig::onConfigModified() {\n  _modified = true;\n  emit modified(this);\n}\n\nbool\nConfig::readCSV(const QString &filename, QString &errorMessage) {\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly))\n    return false;\n  QTextStream stream(&file);\n  return readCSV(stream, errorMessage);\n}\n\nbool\nConfig::readCSV(QTextStream &stream, QString &errorMessage)\n{\n  return CSVReader::read(this, stream, errorMessage);\n}\n\n\nbool\nConfig::readYAML(const QString &filename, const ErrorStack &err) {\n  YAML::Node node;\n  try {\n    QFile file(filename);\n    if (! file.open(QIODevice::ReadOnly)) {\n      errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n      errMsg(err) << \"Cannot read YAML codeplug from file '\" << filename << \"'.\";\n      return false;\n    }\n    QByteArray content = file.readAll();\n    node = YAML::Load(content.constData());\n  } catch (const YAML::Exception &exc) {\n    errMsg(err) << \"Cannot read YAML codeplug from file '\"<< filename\n                << \"': \" << QString::fromStdString(exc.msg) << \".\";\n    return false;\n  }\n\n  if (! node) {\n    errMsg(err) << \"Cannot read YAML codeplug from file '\" << filename << \"'.\";\n    return false;\n  }\n\n  clear();\n  ConfigItem::Context context;\n\n  if (! parse(node, context, err))\n    return false;\n\n  if (! link(node, context, err))\n    return false;\n\n  setModified(false);\n  emit modified(this);\n\n  return true;\n}\n\n\nbool\nConfig::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err)\n{\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot read configuration\"\n                << \": Element is not a map.\";\n    return false;\n  }\n\n  if (node[\"version\"] && node[\"version\"].IsScalar()) {\n    ctx.setVersion(QString::fromStdString(node[\"version\"].as<std::string>()));\n    logDebug() << \"Using format version \" << ctx.version() << \".\";\n  } else {\n    logWarn() << \"No version string set, assuming \" << VERSION_STRING << \".\";\n    ctx.setVersion(VERSION_STRING);\n  }\n\n  if (node[\"settings\"] && (! _settings->parse(node[\"settings\"], ctx, err)))\n    return false;\n  if (node[\"radioIDs\"] && (! _radioIDs->parse(node[\"radioIDs\"], ctx, err)))\n    return false;\n  if (node[\"contacts\"] && (! _contacts->parse(node[\"contacts\"], ctx, err)))\n    return false;\n  if (node[\"groupLists\"] && (! _rxGroupLists->parse(node[\"groupLists\"], ctx, err)))\n    return false;\n  if (node[\"channels\"] && (! _channels->parse(node[\"channels\"], ctx, err)))\n    return false;\n  if (node[\"zones\"] && (! _zones->parse(node[\"zones\"], ctx, err)))\n    return false;\n  if (node[\"scanLists\"] && (! _scanlists->parse(node[\"scanLists\"], ctx, err)))\n    return false;\n  if (node[\"positioning\"] && (! _gpsSystems->parse(node[\"positioning\"], ctx, err)))\n    return false;\n  if (node[\"roamingChannels\"] && (! _roamingChannels->parse(node[\"roamingChannels\"], ctx, err)))\n    return false;\n  if (node[\"roamingZones\"] && (! _roamingZones->parse(node[\"roamingZones\"], ctx, err)))\n    return false;\n  /** @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0.*/\n  else if (node[\"roaming\"] && (! _roamingZones->parse(node[\"roaming\"], ctx, err)))\n    return false;\n\n  // also parses extensions\n  if (! ConfigItem::parse(node, ctx, err))\n    return false;\n\n  return true;\n}\n\nbool\nConfig::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  // radio IDs must be linked before settings, as they may refer to the default DMR ID\n\n  if (node[\"radioIDs\"] && (! _radioIDs->link(node[\"radioIDs\"], ctx, err)))\n    return false;\n\n  if (node[\"settings\"])\n    if (!_settings->link(node[\"settings\"], ctx, err))\n      return false;\n\n  if (node[\"contacts\"] && (! _contacts->link(node[\"contacts\"], ctx, err)))\n    return false;\n  if (node[\"groupLists\"] && (! _rxGroupLists->link(node[\"groupLists\"], ctx, err)))\n    return false;\n  if (node[\"channels\"] && (! _channels->link(node[\"channels\"], ctx, err)))\n    return false;\n  if (node[\"zones\"] && (! _zones->link(node[\"zones\"], ctx, err)))\n    return false;\n  if (node[\"scanLists\"] && (! _scanlists->link(node[\"scanLists\"], ctx, err)))\n    return false;\n  if (node[\"positioning\"] && (! _gpsSystems->link(node[\"positioning\"], ctx, err)))\n    return false;\n  if (node[\"roamingZones\"] && (! _roamingZones->link(node[\"roamingZones\"], ctx, err)))\n    return false;\n  /** @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0.*/\n  else if (node[\"roaming\"] && (! _roamingZones->link(node[\"roaming\"], ctx, err)))\n    return false;\n\n  // also links extensions\n  if (! ConfigItem::link(node, ctx, err))\n    return false;\n\n  return true;\n}\n"
  },
  {
    "path": "lib/config.h.in",
    "content": "#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@\n#define VERSION_MINOR @PROJECT_VERSION_MINOR@\n#define VERSION_PATCH @PROJECT_VERSION_PATCH@\n#define VERSION_STRING @PROJECT_VERSION_STRING@\n#define LOCALE_DIRECTORY \"@LOCALE_DIRECTORY@\"\n"
  },
  {
    "path": "lib/config.hh",
    "content": "/** @defgroup conf Common codeplug configuration\n * This module collects all classes that represent the general configuration for all DMR codeplugs.\n *\n * To this end, it aims at covering the important features for ham radio applications.\n * All features that are more related to \"professional\" applications and are specific to each radio,\n * are implemented by so-called extensions, see @c ConfigExtension. The central class for the\n * abstract configuration is @c Config, this class represents a complete configuration a.k.a.\n * codeplug of a radio. It contains all the information being programmed into the radio irrespective\n * of the model and manufacturer.\n *\n * The entire configuration (abstract, device independent codeplug) consists of a tree of\n * @c ConfigItem instances. This class forms the base-class of all elements in the configuration\n * (excluding lists etc.). Each @c ConfigItem may have a set of properties. These properties are\n * used to implement the majority of the common functionality concerning the abstract codeplug.\n * These are\n *   - Copying & cloning of elements of the configuration.\n *   - Labeling of codeplug objects (everything that has an ID for cross referencing).\n *   - Serialization into YAML (all properties are serialized into YAML automatically if not\n *     prevented by marking the property as not @c SCRIPTABLE).\n *   - Parsing of YAML codeplugs (automatic property parsing can be disabled on a per-property\n *     basis by marking it as not @c SCRIPTABLE).\n *   - Generic editing of the properties in the GUI.\n *   .\n * To this end, the creation of codeplug extensions is pretty easy, as only the properties for the\n * extension must be defined. The rest is taken care of by the default implementation of the\n * @c ConfigItem::copy, @c ConfigItem::clone, @c ConfigItem::label, @c ConfigItem::serialize,\n * @c ConfigItem::parse and @c ConfigItem::link methods. It is not necessary to override any of\n * these methods if there is a one-to-one mapping between the property and its YAML representation.\n *\n * Frequently, however, it is necessary to represent a property in a different way in YAML. This\n * is usually true if a property may hold different specializations of a common type. For example\n * the channel list may hold analog and digital channels. In YAML, the type is specified explicitly\n * as an enclosing map. This structure is not a one-to-one representation of the actual property\n * (the channel list) to the YAML format. In these cases, the @c ConfigItem::parse and\n * @c ConfigItem::link method might be overridden to implement this.\n */\n\n#ifndef CONFIG_HH\n#define CONFIG_HH\n\n#include <QTextStream>\n\n#include \"configobject.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"channel.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingchannel.hh\"\n#include \"roamingzone.hh\"\n#include \"radioid.hh\"\n#include \"radiosettings.hh\"\n\n#include \"commercial_extension.hh\"\n#include \"smsextension.hh\"\n#include \"tyt_extensions.hh\"\n\n// Forward declaration\nclass UserDatabase;\n\n\n/** The config class, representing the codeplug configuration.\n *\n * It contains the description of the contacts, channels, zones, etc. of the codeplug\n * configuration.\n *\n * @ingroup conf */\nclass Config : public ConfigItem\n{\n\tQ_OBJECT\n\n  /** The global radio settings. */\n  Q_PROPERTY(RadioSettings* settings READ settings SCRIPTABLE false)\n  /** The list of radio IDs. */\n  Q_PROPERTY(RadioIDList* radioIDs READ radioIDs SCRIPTABLE false)\n  /** The list of contacts. */\n  Q_PROPERTY(ContactList* contacts READ contacts SCRIPTABLE false)\n  /** The list of group lists. */\n  Q_PROPERTY(RXGroupLists* groupLists READ rxGroupLists SCRIPTABLE false)\n  /** The list of channels. */\n  Q_PROPERTY(ChannelList* channels READ channelList SCRIPTABLE false)\n  /** The list of zones. */\n  Q_PROPERTY(ZoneList* zones READ zones SCRIPTABLE false)\n  /** The list of scan lists. */\n  Q_PROPERTY(ScanLists* scanLists READ scanlists SCRIPTABLE false)\n  /** The list of positioning systems. */\n  Q_PROPERTY(PositionReportingSystems* positioning READ posSystems SCRIPTABLE false)\n  /** The list of roaming channels. */\n  Q_PROPERTY(RoamingChannelList* roamingChannels READ roamingChannels SCRIPTABLE false)\n  /** The list of roaming zones. */\n  Q_PROPERTY(RoamingZoneList* roamingZones READ roamingZones SCRIPTABLE false)\n\n  /** Represents the config extension for encryption keys. */\n  Q_PROPERTY(CommercialExtension* commercial READ commercialExtension)\n  /** Represents the extended SMS settings. */\n  Q_PROPERTY(SMSExtension *sms READ smsExtension)\n  /** Represents the config extension for TyT devices. */\n  Q_PROPERTY(TyTConfigExtension* tytExtension READ tytExtension WRITE setTyTExtension)\n\npublic:\n  /** Constructs an empty configuration. */\n  Q_INVOKABLE explicit Config(QObject *parent = nullptr);\n\n  bool copy(const ConfigItem &other);\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the config was modified, @see modified. */\n  bool isModified() const;\n  /** Sets the modified flag. */\n  void setModified(bool modified);\n\n  /** Returns the radio wide settings. */\n  RadioSettings *settings() const;\n  /** Returns the list of radio IDs. */\n  RadioIDList *radioIDs() const;\n  /** Returns the list of contacts. */\n\tContactList *contacts() const;\n  /** Returns the list of RX group lists. */\n  RXGroupLists *rxGroupLists() const;\n  /** Returns the list of channels. */\n  ChannelList *channelList() const;\n  /** Returns the list of zones. */\n  ZoneList *zones() const;\n  /** Returns the list of scanlists. */\n  ScanLists *scanlists() const;\n  /** Returns the list of positioning systems. */\n  PositionReportingSystems *posSystems() const;\n  /** Returns the list of roaming channels. */\n  RoamingChannelList *roamingChannels() const;\n  /** Returns the list of roaming zones. */\n  RoamingZoneList *roamingZones() const;\n\n  /** Returns @c true if one of the digital channels has a roaming zone assigned. */\n  bool requiresRoaming() const;\n  /** Returns @c true if one of the channels has a GPS or APRS system assigned. */\n  bool requiresGPS() const;\n\n  /** Clears the complete configuration. */\n  void clear();\n\n  const Config *config() const;\n\n  /** Returns the commercial extension. */\n  CommercialExtension *commercialExtension() const;\n  /** Returns the SMS settings extension. */\n  SMSExtension *smsExtension() const;\n\n  /** Returns the TyT settings extension.\n   * If this extension is not set, returns @c nullptr. */\n  TyTConfigExtension *tytExtension() const;\n  /** Sets the TyT settings extension. */\n  void setTyTExtension(TyTConfigExtension *ext);\n\n\npublic:\n  /** Imports a configuration from the given file. */\n  bool readCSV(const QString &filename, QString &errorMessage);\n  /** Imports a configuration from the given text stream in text format. */\n  bool readCSV(QTextStream &stream, QString &errorMessage);\n\n  /** Imports a configuration from the given YAML file. */\n  bool readYAML(const QString &filename, const ErrorStack &err=ErrorStack());\n\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Serializes the configuration into the given stream as text. */\n  bool toYAML(QTextStream &stream, const ErrorStack &err=ErrorStack());\n\nprotected:\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected slots:\n  /** Iternal callback. */\n  void onConfigModified();\n\nprotected:\n  /** If @c true, the configuration was modified. */\n  bool _modified;\n  /** Radio wide settings. */\n  RadioSettings *_settings;\n  /** The list of radio IDs. */\n  RadioIDList *_radioIDs;\n  /** The list of contacts. */\n\tContactList *_contacts;\n  /** The list of RX group lists. */\n  RXGroupLists *_rxGroupLists;\n  /** The list of channels. */\n  ChannelList *_channels;\n  /** The list of zones. */\n  ZoneList *_zones;\n  /** The list of scan lists. */\n  ScanLists *_scanlists;\n  /** The list of GPS Systems. */\n  PositionReportingSystems *_gpsSystems;\n  /** The list of roaming channels. */\n  RoamingChannelList *_roamingChannels;\n  /** The list of roaming zones. */\n  RoamingZoneList *_roamingZones;\n  /** Owns the TyT settings extension. */\n  TyTConfigExtension *_tytExtension;\n  /** Owns the commercial extension. */\n  CommercialExtension *_commercialExtension;\n  /** Owns the SMS settings extension. */\n  SMSExtension *_smsExtension;\n};\n\n#endif // CONFIG_HH\n"
  },
  {
    "path": "lib/configcopyvisitor.cc",
    "content": "#include \"configcopyvisitor.hh\"\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"roamingzone.hh\"\n#include \"logger.hh\"\n\n/* ********************************************************************************************* *\n * Implementation of ConfigCloneVisitor\n * ********************************************************************************************* */\nConfigCloneVisitor::ConfigCloneVisitor(QHash<ConfigObject *, ConfigObject *> &map)\n  : Visitor(), _map(map)\n{\n  // pass...\n}\n\n\nbool\nConfigCloneVisitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) {\n  if (prop.isFlagType() || prop.isEnumType() || (QMetaType::Bool == prop.typeId())\n      || (QMetaType::Int == prop.typeId()) || (QMetaType::UInt == prop.typeId())\n      || (QMetaType::Double == prop.typeId()) || (QMetaType::QString == prop.typeId())\n      || (QMetaType::fromType<Frequency>() == prop.metaType())\n      || (QMetaType::fromType<Interval>() == prop.metaType())\n      || (QMetaType::fromType<Level>() == prop.metaType())\n      || (QMetaType::fromType<SelectiveCall>() == prop.metaType())\n      || (QMetaType::fromType<QGeoCoordinate>() == prop.metaType())) {\n    if ((! prop.isReadable()) && (!prop.isWritable())) {\n      logDebug() << \"Skip property \" << prop.name()\n                 << \" of item \" << item->metaObject()->className() << \": Not readable or writable.\";\n      return true;\n    }\n\n    if (! Visitor::processProperty(item, prop, err))\n      return false;\n\n    // Get clone\n    ConfigItem *clone = qobject_cast<ConfigItem*>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Unexpected element on the stack. Found object of type \" <<\n                     _stack.back()->metaObject()->className() << \", expected ConfigItem.\";\n      return false;\n    }\n    // Find the property\n    int pidx = clone->metaObject()->indexOfProperty(prop.name());\n    if (0 > pidx) {\n      errMsg(err) << \"Cannot set property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n    // Set it\n    if (! clone->metaObject()->property(pidx).write(clone, prop.read(item))) {\n      errMsg(err) << \"Cannot set property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n    return true;\n  } else if (propIsInstance<ConfigObjectReference>(prop)) {\n    // Get clone\n    ConfigItem *clone = qobject_cast<ConfigItem*>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Unexpected element on the stack. Found object of type \" <<\n                     _stack.back()->metaObject()->className() << \", expected ConfigItem.\";\n      return false;\n    }\n    // Set reference to same object, will be replaced later.\n    ConfigObjectReference *ref = prop.read(item).value<ConfigObjectReference *>();\n    ConfigObjectReference *cloneRef = prop.read(clone).value<ConfigObjectReference*>();\n    cloneRef->set(ref->as<ConfigObject>());\n    return true;\n  } else if (propIsInstance<ConfigItem>(prop)) {\n    if (nullptr == prop.read(item).value<ConfigItem *>())\n      return true;\n    // If writeable, simply traverse config item\n    if (prop.isWritable()) {\n      // Clone item in property recursively\n      if(! this->processItem(prop.read(item).value<ConfigItem *>(), err))\n        return false;\n      // Get cloned item from stack\n      ConfigItem *newItem = dynamic_cast<ConfigItem *>(_stack.back()); _stack.pop_back();\n      // Also, get item from stack, who owns the clones item\n      ConfigItem *clone = dynamic_cast<ConfigItem *>(_stack.back());\n      int pidx = clone->metaObject()->indexOfProperty(prop.name());\n      // Find the property\n      if (0 > pidx) {\n        errMsg(err) << \"Cannot set property \" << prop.name() << \" on element on stack.\";\n        return false;\n      }\n      // Set it\n      if (! clone->metaObject()->property(pidx).write(clone, QVariant::fromValue(newItem))) {\n        errMsg(err) << \"Cannot set property \" << prop.name() << \" on element on stack.\";\n        return false;\n      }\n      return true;\n    }\n\n    // If property is not writeable, the parent element (top of stack), owns and creates the item\n    // -> needs to be put on the stack\n    ConfigItem *clone = dynamic_cast<ConfigItem *>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Unexpected element on the stack. Found object of type \" <<\n                     _stack.back()->metaObject()->className() << \", expected ConfigItem.\";\n      return false;\n    }\n\n    int pidx = clone->metaObject()->indexOfProperty(prop.name());\n    if (0 > pidx) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    QVariant value = clone->metaObject()->property(pidx).read(clone);\n    if (!value.isValid()) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    _stack.push_back(value.value<ConfigItem *>());\n    if (! Visitor::processItem(prop.read(item).value<ConfigItem *>(), err)) {\n      errMsg(err) << \"Cannot process property \" << prop.name() << \".\";\n      return false;\n    }\n    _stack.pop_back();\n    return true;\n  } else if (ConfigObjectList *lst = prop.read(item).value<ConfigObjectList *>()) {\n    // Lists are always owned by the item. So dig up the list and put it on the stack\n    ConfigItem *clone = dynamic_cast<ConfigItem *>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Unexpected element on the stack. Found object of type \" <<\n                     _stack.back()->metaObject()->className() << \", expected ConfigItem.\";\n      return false;\n    }\n\n    int pidx = clone->metaObject()->indexOfProperty(prop.name());\n    if (0 > pidx) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    QVariant value = clone->metaObject()->property(pidx).read(clone);\n    if (!value.isValid()) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    _stack.push_back(value.value<ConfigObjectList *>());\n    if (! processList(lst, err)) {\n      errMsg(err) << \"Cannot process object list in property \" << prop.name() << \".\";\n      return false;\n    }\n    _stack.pop_back();\n    return true;\n  } else if (ConfigObjectRefList *refs = prop.read(item).value<ConfigObjectRefList *>()) {\n    // Lists are always owned by the item. So dig up the parent and get the list\n    ConfigItem *clone = dynamic_cast<ConfigItem *>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Unexpected element on the stack. Found object of type \" <<\n                     _stack.back()->metaObject()->className() << \", expected ConfigItem.\";\n      return false;\n    }\n\n    int pidx = clone->metaObject()->indexOfProperty(prop.name());\n    if (0 > pidx) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    QVariant value = clone->metaObject()->property(pidx).read(clone);\n    if (!value.isValid()) {\n      errMsg(err) << \"Cannot read property \" << prop.name() << \" on element on stack.\";\n      return false;\n    }\n\n    _stack.push_back(value.value<ConfigObjectRefList *>());\n    if (! processList(refs, err)) {\n      errMsg(err) << \"Cannot process reference list in property \" << prop.name() << \".\";\n      return false;\n    }\n    _stack.pop_back();\n    return true;\n  } else {\n    if (! this->processUnknownType(item, prop, err)) {\n      errMsg(err) << \"While processing property '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"' of unknown type.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nConfigCloneVisitor::processItem(ConfigItem *item, const ErrorStack &err) {\n  // Call default constructor.\n  QObject *obj = item->metaObject()->newInstance();\n  if (nullptr == obj) {\n    errMsg(err) << \"Cannot construct new instance for item of type \"\n                << item->metaObject()->className() << \".\";\n    return false;\n  }\n  // Put new item on stack\n  _stack.push_back(obj);\n\n  // If item is an object -> store in map\n  if (item->is<ConfigObject>())\n    _map[item->as<ConfigObject>()] = dynamic_cast<ConfigObject*>(obj);\n\n  // then traverse by type ...\n  if (item->is<Channel>())\n    return processChannel(item->as<Channel>(), err);\n\n  // .. or use default.\n  return Visitor::processItem(item, err);\n}\n\n\nbool\nConfigCloneVisitor::processChannel(Channel *item, const ErrorStack &err) {\n  if (! Visitor::processItem(item, err))\n    return false;\n\n  if (item->defaultPower())\n    qobject_cast<Channel*>(_stack.back())->setDefaultPower();\n\n  return true;\n}\n\n\nbool\nConfigCloneVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) {\n  if (ConfigObjectRefList *refs = dynamic_cast<ConfigObjectRefList*>(list)) {\n    ConfigObjectRefList *clone = dynamic_cast<ConfigObjectRefList*>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Cannot clone reference list, no ref list found on the stack. Got \"\n                  << _stack.back()->metaObject()->className() << \".\";\n      return false;\n    }\n    // Just copy references, they will be replaced later\n    for (int i=0; i<refs->count(); i++) {\n      clone->add(refs->get(i));\n    }\n    return true;\n  }\n\n  if (ConfigObjectList *olist = dynamic_cast<ConfigObjectList*>(list)) {\n    // First, get object list from stack\n    ConfigObjectList *clone = dynamic_cast<ConfigObjectList*>(_stack.back());\n    if (nullptr == clone) {\n      errMsg(err) << \"Cannot clone list, no list found on the stack. Got \"\n                  << _stack.back()->metaObject()->className() << \".\";\n      return false;\n    }\n\n    // just traverse list\n    if (! Visitor::processList(olist, err))\n      return false;\n\n    // Take generated items from stack\n    for (int i=0; i<olist->count(); i++) {\n      clone->add(dynamic_cast<ConfigObject *>(_stack.back()),0); _stack.pop_back();\n    }\n\n    return true;\n  }\n\n  errMsg(err) << \"Unexpected list type \" << list->metaObject()->className() << \".\";\n  return false;\n}\n\nConfigItem *\nConfigCloneVisitor::takeResult(const ErrorStack &err) {\n  if (1 != _stack.size()) {\n    errMsg(err) << \"Cannot take result, result stack does not contain one element, got \"\n                << _stack.size() << \".\";\n    return nullptr;\n  }\n  ConfigItem *item = dynamic_cast<ConfigItem*>(_stack.back());\n  if (nullptr == item) {\n    errMsg(err) << \"Cannot take result, last element on stack is not a ConfigItem, got \"\n                << _stack.back()->metaObject()->className() << \".\";\n    return nullptr;\n  }\n  _stack.pop_back();\n  return item;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of FixReferencesVisitor\n * ********************************************************************************************* */\nFixReferencesVisistor::FixReferencesVisistor(QHash<ConfigObject *, ConfigObject *> &map, bool keepUnknown)\n  : Visitor(), _map(map), _keepUnknown(keepUnknown)\n{\n  // Populate with default singleton instances.\n  map[SelectedChannel::get()] = SelectedChannel::get();\n  map[DefaultRadioID::get()]  = DefaultRadioID::get();\n  map[DefaultRoamingZone::get()] = DefaultRoamingZone::get();\n}\n\nbool\nFixReferencesVisistor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) {\n  // First, traverse normally\n  if (! Visitor::processProperty(item, prop, err))\n    return false;\n\n  if (ConfigObjectReference *ref = prop.read(item).value<ConfigObjectReference *>()) {\n    if (ref->isNull())\n      return true;\n    ConfigObject *obj = ref->as<ConfigObject>();\n    if ((! _keepUnknown) && (! _map.contains(obj))) {\n      errMsg(err) << \"Cannot fix reference to object '\" << obj->name()\n                  << \"' of type \" << obj->metaObject()->className()\n                  << \": Not mapped/cloned yet.\";\n      return false;\n    }\n    if (_map.contains(obj))\n      ref->set(_map[obj]);\n    return true;\n  }\n\n  return true;\n}\n\nbool\nFixReferencesVisistor::processList(AbstractConfigObjectList *list, const ErrorStack &err) {\n  // First traverse list\n  if (! Visitor::processList(list, err))\n    return false;\n  if (ConfigObjectRefList *rlist = dynamic_cast<ConfigObjectRefList*>(list)) {\n    QSet<ConfigObject*> del;\n    // Resolve all references.\n    for (int i=0; i<rlist->count(); i++) {\n      if ((! _keepUnknown) && (! _map.contains(rlist->get(i)))) {\n        errMsg(err) << \"Cannot fix reference to object '\" << rlist->get(i)->name()\n                    << \"' of type \" << rlist->get(i)->metaObject()->className()\n                    << \": Not mapped/cloned yet.\";\n        return false;\n      }\n      // If the reference cannot be replaced (i.e., the replacement is already in the list):\n      if (_map.contains(rlist->get(i)) && (0 > rlist->replace(_map[rlist->get(i)], i)))\n        del.insert(rlist->get(i));\n    }\n    // Now remove all references that could not be replaced\n    foreach (ConfigObject *obj, del) {\n      rlist->del(obj);\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigCopy\n * ********************************************************************************************* */\nConfigItem *\nConfigCopy::copy(ConfigItem *original, const ErrorStack &err) {\n  QHash<ConfigObject*, ConfigObject*> map;\n  ConfigCloneVisitor cloner(map);\n  if (! cloner.processItem(original, err)) {\n    errMsg(err) << \"Cannot clone item of type \" << original->metaObject()->className() << \".\";\n    return nullptr;\n  }\n  ConfigItem *clone = cloner.takeResult();\n  FixReferencesVisistor fixer(map, true);\n  if (! fixer.processItem(clone, err)) {\n    errMsg(err) << \"Cannot fix references in item of type \"\n                << clone->metaObject()->className() << \".\";\n    delete clone;\n    return nullptr;\n  }\n  return clone;\n}\n"
  },
  {
    "path": "lib/configcopyvisitor.hh",
    "content": "#ifndef CONFIGCOPYVISITOR_HH\n#define CONFIGCOPYVISITOR_HH\n\n#include \"visitor.hh\"\n\nclass ConfigObject;\nclass Channel;\n\n/** This visitor traverses the the given configuration and clones it. All references are still\n *  pointing to the originals.\n * @ingroup conf */\nclass ConfigCloneVisitor : public Visitor\n{\npublic:\n  /** Constructor.\n   * @param map Specifies the mapping table to be filled with the created clones. */\n  ConfigCloneVisitor(QHash<ConfigObject *, ConfigObject*> &map);\n\n  bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack());\n  bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack());\n\n  /** Specialized handler for channels, must traverse the channel object. */\n  virtual bool processChannel(Channel *item, const ErrorStack &err=ErrorStack());\n\n  /** Extracts the cloned item. */\n  ConfigItem *takeResult(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Stack of the current object. */\n  QList<QObject *> _stack;\n  /** Reference to the translation table original -> cloned object. */\n  QHash<ConfigObject *, ConfigObject*> &_map;\n};\n\n\n/** Replaces references using a specified map.\n * This can be considered a second step in copying an entire codeplug, first all objects are cloned\n * and in a second step, all references are fixed using this class.\n * @ingroup conf */\nclass FixReferencesVisistor: public Visitor\n{\npublic:\n  /** Constructor. */\n  FixReferencesVisistor(QHash<ConfigObject *, ConfigObject*> &map, bool keepUnknown=false);\n\n  bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Reference to the translation table original -> cloned object. */\n  QHash<ConfigObject *, ConfigObject*> &_map;\n  /** If false, an unmapped reference is an error. */\n  bool _keepUnknown;\n};\n\n/** Just a name space to hold the copy function. */\nclass ConfigCopy {\npublic:\n  /** Copies the given item. */\n  static ConfigItem *copy(ConfigItem *original, const ErrorStack &err=ErrorStack());\n};\n\n#endif // CONFIGCOPYVISITOR_HH\n"
  },
  {
    "path": "lib/configlabelingvisitor.cc",
    "content": "#include \"configlabelingvisitor.hh\"\n\nConfigLabelingVisitor::ConfigLabelingVisitor(ConfigItem::Context &context)\n  : Visitor(), _context(context)\n{\n  // pass..\n}\n\nbool\nConfigLabelingVisitor::processItem(ConfigItem *item, const ErrorStack &err)\n{\n  // First, check if item is an ConfigObject\n  if (item->is<ConfigObject>()) {\n    ConfigObject *obj = item->as<ConfigObject>();\n    QString prefix = obj->idPrefix();\n\n    // If no prefix is set -> skip.\n    if (prefix.isEmpty())\n      return Visitor::processItem(item, err);\n\n    // Find unused ID\n    unsigned n=1;\n    QString id=QString(\"%1%2\").arg(prefix).arg(n);\n    while (_context.contains(id))\n      id=QString(\"%1%2\").arg(prefix).arg(++n);\n\n    // Add to context\n    if (! _context.add(id, obj)) {\n      if (_context.contains(obj))\n        errMsg(err) << \"Object already in context with id '\" << _context.getId(obj) << \"'.\";\n      errMsg(err) << \"Cannot add element '\" << id << \"' to context.\";\n      return false;\n    }\n  }\n\n  return Visitor::processItem(item, err);\n}\n"
  },
  {
    "path": "lib/configlabelingvisitor.hh",
    "content": "#ifndef CONFIGLABELINGVISITOR_HH\n#define CONFIGLABELINGVISITOR_HH\n\n#include \"visitor.hh\"\n#include \"configobject.hh\"\n\n/** A visitor to label the entire configuration.\n * That is, assigning unique labels to each @c ConfigObject within the configuration.\n * @ingroup conf */\nclass ConfigLabelingVisitor: protected Visitor\n{\npublic:\n  /** Use the static method @c label to label the configuration. */\n  ConfigLabelingVisitor(ConfigItem::Context &context);\n\n  bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack());\n\n  /** Labels the configuration and stores the labels in the given context. */\n  static bool label(Config *config, ConfigItem::Context &context);\n\nprotected:\n  /** Holds a weak reference to the parser/serializer context.\n   * That is, the id<->obj table. */\n  ConfigItem::Context &_context;\n};\n\n#endif // CONFIGLABELINGVISITOR_HH\n"
  },
  {
    "path": "lib/configmergevisitor.cc",
    "content": "#include \"configmergevisitor.hh\"\n#include \"configcopyvisitor.hh\"\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"config.hh\"\n#include \"channel.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigMergeVisitor\n * ********************************************************************************************* */\nConfigMergeVisitor::ConfigMergeVisitor(Config* destination, QHash<ConfigObject*, ConfigObject*>& translation,\n    ItemStrategy itemStrategy, SetStrategy setStrategy)\n  : Visitor(), _destination(destination), _translation(translation),\n    _itemStrategy(itemStrategy), _setStrategy(setStrategy)\n{\n  // pass...\n}\n\n\nbool\nConfigMergeVisitor::processItem(ConfigItem *item, const ErrorStack &err) {\n\n  // Dispatch by type\n  if (item->is<RadioID>())\n    return processRadioID(item->as<RadioID>(), err);\n  else if (item->is<Contact>())\n    return processContact(item->as<Contact>(), err);\n  else if (item->is<RXGroupList>())\n    return processGroupList(item->as<RXGroupList>(), err);\n  else if (item->is<Channel>())\n    return processChannel(item->as<Channel>(), err);\n  else if (item->is<Zone>())\n    return processZone(item->as<Zone>(), err);\n  else if (item->is<ScanList>())\n    return processScanList(item->as<ScanList>(), err);\n  else if (item->is<PositionReportingSystem>())\n    return processPositioningSystem(item->as<PositionReportingSystem>(), err);\n  else if (item->is<RoamingChannel>())\n    return processRoamingChannel(item->as<RoamingChannel>(), err);\n  else if (item->is<RoamingZone>())\n    return processRoamingZone(item->as<RoamingZone>(), err);\n\n  return Visitor::processItem(item, err);\n}\n\n\nbool\nConfigMergeVisitor::processRadioID(RadioID *item, const ErrorStack &err) {\n  if (0 == _destination->radioIDs()->findItemsByName(item->name()).count())\n    return addObject(_destination->radioIDs(), nullptr, item, err);\n\n  RadioID *present = _destination->radioIDs()->findItemsByName(item->name())\n      .first()->as<RadioID>();\n\n  if (ItemStrategy::Ignore == _itemStrategy)\n    return ignoreObject(_destination->radioIDs(), present, item, err);\n\n  if (ItemStrategy::Override == _itemStrategy)\n    return replaceObject(_destination->radioIDs(), present, item, err);\n\n  if (ItemStrategy::Duplicate == _itemStrategy)\n    return duplicateObject(_destination->radioIDs(), present, item, err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processChannel(Channel *item, const ErrorStack &err) {\n  if (0 == _destination->channelList()->findItemsByName(item->name()).count())\n    return addObject(_destination->channelList(), nullptr, item, err);\n\n  Channel *present = _destination->channelList()->findItemsByName(item->name())\n      .first()->as<Channel>();\n\n  if (ItemStrategy::Ignore == _itemStrategy)\n    return ignoreObject(_destination->channelList(), present, item, err);\n\n  if (ItemStrategy::Override == _itemStrategy)\n    return replaceObject(_destination->channelList(), present, item, err);\n\n  if (ItemStrategy::Duplicate == _itemStrategy)\n    return duplicateObject(_destination->channelList(), present, item, err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processContact(Contact *item, const ErrorStack &err) {\n  if (0 == _destination->contacts()->findItemsByName(item->name()).count())\n    return addObject(_destination->contacts(), nullptr, item, err);\n\n  Contact *present = _destination->contacts()->findItemsByName(item->name())\n      .first()->as<Contact>();\n\n  if (ItemStrategy::Ignore == _itemStrategy)\n    return ignoreObject(_destination->contacts(), present, item, err);\n\n  if (ItemStrategy::Override == _itemStrategy)\n    return replaceObject(_destination->contacts(), present, item, err);\n\n  if (ItemStrategy::Duplicate == _itemStrategy)\n    return duplicateObject(_destination->contacts(), present, item, err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processPositioningSystem(PositionReportingSystem *item, const ErrorStack &err) {\n  if (0 == _destination->posSystems()->findItemsByName(item->name()).count())\n    return addObject(_destination->posSystems(), nullptr, item, err);\n\n  PositionReportingSystem *present = _destination->posSystems()->findItemsByName(item->name())\n      .first()->as<PositionReportingSystem>();\n\n  if (ItemStrategy::Ignore == _itemStrategy)\n    return ignoreObject(_destination->posSystems(), present, item, err);\n\n  if (ItemStrategy::Override == _itemStrategy)\n    return replaceObject(_destination->posSystems(), present, item, err);\n\n  if (ItemStrategy::Duplicate == _itemStrategy)\n    return duplicateObject(_destination->posSystems(), present, item, err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processRoamingChannel(RoamingChannel *item, const ErrorStack &err) {\n  if (0 == _destination->roamingChannels()->findItemsByName(item->name()).count())\n    return addObject(_destination->roamingChannels(), nullptr, item, err);\n\n  RoamingChannel *present = _destination->roamingChannels()->findItemsByName(item->name())\n      .first()->as<RoamingChannel>();\n\n  if (ItemStrategy::Ignore == _itemStrategy)\n    return ignoreObject(_destination->roamingChannels(), present, item, err);\n\n  if (ItemStrategy::Override == _itemStrategy)\n    return replaceObject(_destination->roamingChannels(), present, item, err);\n\n  if (ItemStrategy::Duplicate == _itemStrategy)\n    return duplicateObject(_destination->roamingChannels(), present, item, err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processGroupList(RXGroupList *item, const ErrorStack &err) {\n  if (0 == _destination->rxGroupLists()->findItemsByName(item->name()).count())\n    return addObject(_destination->rxGroupLists(), nullptr, item, err);\n\n  RXGroupList *present = _destination->rxGroupLists()->findItemsByName(item->name())\n      .first()->as<RXGroupList>();\n\n  if (SetStrategy::Ignore == _setStrategy)\n    return ignoreObject(_destination->rxGroupLists(), present, item, err);\n\n  if (SetStrategy::Override == _setStrategy)\n    return replaceObject(_destination->rxGroupLists(), present, item, err);\n\n  if (SetStrategy::Duplicate == _setStrategy)\n    return duplicateObject(_destination->rxGroupLists(), present, item, err);\n\n  if (SetStrategy::Merge == _setStrategy)\n    return mergeList(present->contacts(), item->contacts(), err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processZone(Zone *item, const ErrorStack &err) {\n  if (0 == _destination->zones()->findItemsByName(item->name()).count())\n    return addObject(_destination->zones(), nullptr, item, err);\n\n  Zone *present = _destination->zones()->findItemsByName(item->name())\n      .first()->as<Zone>();\n\n  if (SetStrategy::Ignore == _setStrategy)\n    return ignoreObject(_destination->zones(), present, item, err);\n\n  if (SetStrategy::Override == _setStrategy)\n    return replaceObject(_destination->zones(), present, item, err);\n\n  if (SetStrategy::Duplicate == _setStrategy)\n    return duplicateObject(_destination->zones(), present, item, err);\n\n  if (SetStrategy::Merge == _setStrategy)\n    return mergeList(present->A(), item->A(), err) &&\n        mergeList(present->B(), item->B(), err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processScanList(ScanList *item, const ErrorStack &err) {\n  if (0 == _destination->scanlists()->findItemsByName(item->name()).count())\n    return addObject(_destination->scanlists(), nullptr, item, err);\n\n  ScanList *present = _destination->scanlists()->findItemsByName(item->name())\n      .first()->as<ScanList>();\n\n  if (SetStrategy::Ignore == _setStrategy)\n    return ignoreObject(_destination->scanlists(), present, item, err);\n\n  if (SetStrategy::Override == _setStrategy)\n    return replaceObject(_destination->scanlists(), present, item, err);\n\n  if (SetStrategy::Duplicate == _setStrategy)\n    return duplicateObject(_destination->scanlists(), present, item, err);\n\n  if (SetStrategy::Merge == _setStrategy)\n    return mergeList(present->channels(), item->channels(), err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::processRoamingZone(RoamingZone *item, const ErrorStack &err) {\n  if (0 == _destination->roamingZones()->findItemsByName(item->name()).count())\n    return addObject(_destination->roamingZones(), nullptr, item, err);\n\n  RoamingZone *present = _destination->roamingZones()->findItemsByName(item->name())\n      .first()->as<RoamingZone>();\n\n  if (SetStrategy::Ignore == _setStrategy)\n    return ignoreObject(_destination->roamingZones(), present, item, err);\n\n  if (SetStrategy::Override == _setStrategy)\n    return replaceObject(_destination->roamingZones(), present, item, err);\n\n  if (SetStrategy::Duplicate == _setStrategy)\n    return duplicateObject(_destination->roamingZones(), present, item, err);\n\n  if (SetStrategy::Merge == _setStrategy)\n    return mergeList(present->channels(), item->channels(), err);\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::addObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) {\n  Q_UNUSED(present)\n\n  ConfigObject *newObject = ConfigCopy::copy(merging, err)->as<ConfigObject>();\n  if (nullptr == newObject) {\n    errMsg(err) << \"Cannot copy item '\" << merging->name() << \"'.\";\n    return false;\n\n  }\n  logDebug() << \"Add object '\" << merging->name() << \"'.\";\n\n  list->add(newObject);\n  _translation[merging] = newObject;\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::ignoreObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) {\n  Q_UNUSED(list); Q_UNUSED(err)\n\n  logDebug() << \"Ignore object '\" << merging->name() << \"'.\";\n  _translation[merging] = present;\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::replaceObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) {\n  ConfigObject *newObject = ConfigCopy::copy(merging, err)->as<ConfigObject>();\n  if (nullptr == newObject) {\n    errMsg(err) << \"Cannot copy item '\" << merging->name() << \"'.\";\n    return false;\n  }\n  logDebug() << \"Replace object '\" << present->name() << \"'.\";\n\n  list->replace(newObject, list->indexOf(present));\n  _translation[merging] = newObject;\n  _translation[present] = newObject;\n\n  return true;\n}\n\n\nbool\nConfigMergeVisitor::duplicateObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err) {\n  Q_UNUSED(present)\n\n  ConfigObject *newItem = ConfigCopy::copy(merging, err)->as<ConfigObject>();\n  if (nullptr == newItem) {\n    errMsg(err) << \"Cannot copy item '\" << merging->name() << \"'.\";\n    return false;\n  }\n  logDebug() << \"Copy object '\" << merging->name() << \"'.\";\n\n  newItem->setName(newItem->name()+\" (copy)\");\n  list->add(newItem);\n  _translation[merging] = newItem;\n\n  return true;\n}\n\nbool\nConfigMergeVisitor::mergeList(ConfigObjectRefList *present, ConfigObjectRefList *merging, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (int i=0; i<merging->count(); i++) {\n    ConfigObject *mergingItem = merging->get(i);\n    mergingItem = _translation.value(mergingItem, mergingItem)->as<ConfigObject>();\n    if (! present->has(mergingItem)) {\n      present->add(mergingItem);\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigMerge\n * ********************************************************************************************* */\nbool\nConfigMerge::mergeInto(Config *destination, Config *source,\n                       ConfigMergeVisitor::ItemStrategy itemStrategy,\n                       ConfigMergeVisitor::SetStrategy setStrategy,\n                       const ErrorStack &err)\n{\n  QHash<ConfigObject *, ConfigObject *> referenceTable;\n  ConfigMergeVisitor mergeVisitor(destination, referenceTable, itemStrategy, setStrategy);\n  if (! mergeVisitor.process(source, err)) {\n    errMsg(err) << \"Cannot merge configurations.\";\n    return false;\n  }\n\n  FixReferencesVisistor linkVisitor(referenceTable, true);\n  if (! linkVisitor.process(destination, err)) {\n    errMsg(err) << \"Cannot fix references in merged configuration.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nConfig *\nConfigMerge::merge(Config *destination, Config *source,\n                   ConfigMergeVisitor::ItemStrategy itemStrategy,\n                   ConfigMergeVisitor::SetStrategy setStrategy,\n                   const ErrorStack &err)\n{\n  Config *copy = ConfigCopy::copy(destination, err)->as<Config>();\n  if (nullptr == copy) {\n    errMsg(err) << \"Cannot merge configurations.\";\n    return nullptr;\n  }\n\n  if (! mergeInto(copy, source, itemStrategy, setStrategy, err)) {\n    delete copy;\n    return nullptr;\n  }\n\n  return copy;\n}\n"
  },
  {
    "path": "lib/configmergevisitor.hh",
    "content": "#ifndef CONFIGMERGEVISITOR_HH\n#define CONFIGMERGEVISITOR_HH\n\n#include \"visitor.hh\"\n\nclass ConfigObject;\nclass RadioID;\nclass Channel;\nclass Contact;\nclass PositionReportingSystem;\nclass RoamingChannel;\nclass ConfigObjectRefList;\nclass RXGroupList;\nclass Zone;\nclass ScanList;\nclass RoamingZone;\n\n\n/** Implements a visitor to merge the visited config into a given config.\n * The destination configuration object is passed to the constructor. This allows to merge several\n * configurations into one.\n * @ingroup conf */\nclass ConfigMergeVisitor : public Visitor\n{\npublic:\n  /** Possible strategies to merge atomic items. That is, Channels, Contacts & PositioningSystems,\n   * if there are two items with the same name. */\n  enum class ItemStrategy {\n    Ignore,    ///< Ignore the source item.\n    Override,  ///< Override the destination item.\n    Duplicate  ///< Add a new item with a modified name.\n  };\n\n  /** Possible strategies to merge sets of references. That is, GroupLists, Zones & ScanLists,\n   * if there are two sets with the same name. */\n  enum class SetStrategy {\n    Ignore,    ///< Ignore the source set.\n    Override,  ///< Override the destination item.\n    Duplicate, ///< Add a new set with a modified name.\n    Merge      ///< Merge sets.\n  };\n\npublic:\n  /** Constructor of the merge visitor.\n   * @param destination Specifies the destination configuration, the additional configurations are\n   *        merged into.\n   * @param translation Specifies the translation table to fill of objects being replaced.\n   * @param itemStrategy Specifies the item merge strategy.\n   * @param setStrategy Specifies the set merge strategy. */\n  ConfigMergeVisitor(Config *destination, QHash<ConfigObject *, ConfigObject *> &translation,\n                     ItemStrategy itemStrategy=ItemStrategy::Ignore,\n                     SetStrategy setStrategy=SetStrategy::Ignore);\n\n\n  bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Handles a RadioID object of the source configuration. */\n  bool processRadioID(RadioID *item, const ErrorStack &err = ErrorStack());\n  /** Handles a Channel object of the source configuration. */\n  bool processChannel(Channel *item, const ErrorStack &err = ErrorStack());\n  /** Handles a Contact object of the source configuration. */\n  bool processContact(Contact *item, const ErrorStack &err = ErrorStack());\n  /** Handles a PositioningSystem object of the source configuration. */\n  bool processPositioningSystem(PositionReportingSystem *item, const ErrorStack &err = ErrorStack());\n  /** Handles a RoamingChannel object of the source configuration. */\n  bool processRoamingChannel(RoamingChannel *item, const ErrorStack &err = ErrorStack());\n  /** Handles a RXGroupList object of the source configuration. */\n  bool processGroupList(RXGroupList *item, const ErrorStack &err = ErrorStack());\n  /** Handles a Zone object of the source configuration. */\n  bool processZone(Zone *item, const ErrorStack &err = ErrorStack());\n  /** Handles a ScanList object of the source configuration. */\n  bool processScanList(ScanList *item, const ErrorStack &err = ErrorStack());\n  /** Handles a RoamingZone object of the source configuration. */\n  bool processRoamingZone(RoamingZone *item, const ErrorStack &err = ErrorStack());\n\n  /** Adds a copy of the @c merging object to the given list, containing the colliding @c present\n   *  object. Also updates the translation table to bend references to the merging object to that copy.  */\n  bool addObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack());\n  /** Does not add the @c merging object, but fixes the translation table to bend references to the merging object. */\n  bool ignoreObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack());\n  /** Replaces the @c present object in the given list by a copy of @c merging object. Also fixes\n   *  the translation table to bend references to both the removed @c present object as well as the\n   *  @c merging object. */\n  bool replaceObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack());\n  /** Adds a copy of the @c merging object to the given list. */\n  bool duplicateObject(AbstractConfigObjectList *list, ConfigObject *present, ConfigObject *merging, const ErrorStack &err = ErrorStack());\n  /** Merges two reference lists. That is, @c merging gets merged into the @c present list. */\n  bool mergeList(ConfigObjectRefList *present, ConfigObjectRefList *merging, const ErrorStack &err = ErrorStack());\n\nprotected:\n  /** The destination configuration. */\n  Config *_destination;\n  /** Translation table for fixing references. */\n  QHash<ConfigObject *, ConfigObject *> &_translation;\n  /** The item merge strategy. */\n  ItemStrategy _itemStrategy;\n  /** The set merge strategy. */\n  SetStrategy _setStrategy;\n};\n\n\n/** Just a namespace to provide merging functions.\n * @ingroup conf */\nclass ConfigMerge\n{\npublic:\n  /** Merges the given @c source into the given @c destination using the specified strategies to\n   * handle conflicts. Here the @c destination codeplug gets modified, even on errors. */\n  static bool mergeInto(Config *destination, Config *source,\n                        ConfigMergeVisitor::ItemStrategy itemStrategy=ConfigMergeVisitor::ItemStrategy::Ignore,\n                        ConfigMergeVisitor::SetStrategy setStrategy=ConfigMergeVisitor::SetStrategy::Ignore,\n                        const ErrorStack &err = ErrorStack());\n\n  /** Merges the given @c source into a copy of the given @c destination, using the specified\n   * strategies to handle conflicts. Here the @c destination codeplug does not get modified at\n   * all. */\n  static Config *merge(Config *destination, Config *source,\n                       ConfigMergeVisitor::ItemStrategy itemStrategy=ConfigMergeVisitor::ItemStrategy::Ignore,\n                       ConfigMergeVisitor::SetStrategy setStrategy=ConfigMergeVisitor::SetStrategy::Ignore,\n                       const ErrorStack &err = ErrorStack());\n};\n\n#endif // CONFIGMERGEVISITOR_HH\n"
  },
  {
    "path": "lib/configobject.cc",
    "content": "#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"logger.hh\"\n#include \"frequency.hh\"\n#include \"interval.hh\"\n#include \"level.hh\"\n#include \"signaling.hh\"\n#include \"gnsssettings.hh\"\n\n#include <QMetaProperty>\n#include <QMetaEnum>\n\n\n// Helper function to extract key names for a QMetaEnum\ninline QStringList enumKeys(const QMetaEnum &e) {\n  QStringList lst;\n  for (int i=0; i<e.keyCount(); i++)\n    lst.push_back(e.key(i));\n  return lst;\n}\n\ninline bool isInstanceOf(QObject *obj, const QStringList &typeNames) {\n  const QMetaObject *type = obj->metaObject();\n  while (type) {\n    if (typeNames.contains(type->className()))\n      return true;\n    type = type->superClass();\n  }\n  return false;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigObject::Context\n * ********************************************************************************************* */\nQHash<QString, QList<QPair<QString, QVariant>>> ConfigObject::Context::_tags =\n    QHash<QString, QList<QPair<QString, QVariant>>>();\n\nConfigItem::Context::Context()\n  : _version(), _objects(), _ids()\n{\n  // pass...\n}\n\nConfigItem::Context::~Context() {\n  // pass...\n}\n\nconst QString &\nConfigItem::Context::version() const {\n  return _version;\n}\nvoid\nConfigItem::Context::setVersion(const QString &ver) {\n  _version = ver;\n}\n\nbool\nConfigItem::Context::contains(ConfigObject *obj) const {\n  return _ids.contains(obj);\n}\n\nbool\nConfigItem::Context::contains(const QString &id) const {\n  return _objects.contains(id);\n}\n\nQString\nConfigItem::Context::getId(ConfigObject *obj) const {\n  return _ids.value(obj, \"\");\n}\n\nConfigObject *\nConfigItem::Context::getObj(const QString &id) const {\n  return _objects.value(id, nullptr);\n}\n\nbool\nConfigItem::Context::add(const QString &id, ConfigObject *obj) {\n  if (_objects.contains(id) || _ids.contains(obj))\n    return false;\n  _objects.insert(id, obj);\n  _ids.insert(obj, id);\n  return true;\n}\n\nbool\nConfigItem::Context::tagIsSet(const QString &className, const QString &property, const QString &tag) {\n  QString qname = className+\"::\"+property;\n  if (! _tags.contains(qname))\n    return false;\n  for(auto pair: _tags[qname]) {\n    if (pair.first == tag)\n      return true;\n  }\n  return false;\n}\n\nbool\nConfigItem::Context::hasTag(const QString &className, const QString &property, QVariant value) {\n  QString qname = className+\"::\"+property;\n  if (! _tags.contains(qname))\n    return false;\n  for(auto pair: _tags[qname]) {\n    if (QPartialOrdering::Equivalent == QVariant::compare(pair.second, value))\n      return true;\n  }\n  return false;\n}\n\nQVariant\nConfigItem::Context::tagGetValue(const QString &className, const QString &property, const QString &tag) {\n  QString qname = className+\"::\"+property;\n  if (! _tags.contains(qname))\n    return QVariant();\n  for(auto pair: _tags[qname]) {\n    if (pair.first == tag)\n      return pair.second;\n  }\n  return QVariant();\n}\n\nQString\nConfigItem::Context::getTag(const QString &className, const QString &property, QVariant value) {\n  QString qname = className+\"::\"+property;\n  if (! _tags.contains(qname))\n    return QString();\n  for(auto pair: _tags[qname]) {\n    if (QPartialOrdering::Equivalent == QVariant::compare(pair.second, value))\n      return pair.first;\n  }\n  return QString();\n}\n\nvoid\nConfigItem::Context::setTag(const QString &className, const QString &property, const QString &tag, QVariant value) {\n  //logDebug() << \"Register tag \" << tag << \" for \" << property << \" in \" << className << \".\";\n  QString qname = className+\"::\"+property;\n  if (! _tags.contains(qname)) {\n    _tags[qname] = QList<QPair<QString, QVariant>>();\n    _tags[qname].append({tag, value});\n    return;\n  }\n\n  for (auto it=_tags[qname].begin(); it != _tags[qname].end(); it++) {\n    if (tag == it->first) {\n      *it = {tag, value};\n      return;\n    }\n  }\n  _tags[qname].append({tag, value});\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigItem\n * ********************************************************************************************* */\nConfigItem::ConfigItem(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nbool\nConfigItem::copy(const ConfigItem &other) {\n  // check if other has the same type\n  if (strcmp(other.metaObject()->className(), metaObject()->className())) {\n    logError() << metaObject()->className() << \" cannot copy from \"\n               << other.metaObject()->className();\n    return false;\n  }\n\n  // clear this instance\n  this->clear();\n\n  // Iterate over all properties\n  const QMetaObject *meta = metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    // This property\n    QMetaProperty prop = meta->property(p);\n    // the same property over at other\n    QMetaProperty oprop = other.metaObject()->property(p);\n\n    // Should never happen\n    if (! prop.isValid()) {\n      logWarn() << \"Invalid property \" << prop.name() << \". This should not happen.\";\n      continue;\n    }\n\n    // true if the property is a basic type\n    bool isBasicType = ( prop.isEnumType() || prop.isFlagType() || (QMetaType::Bool==prop.typeId()) ||\n                         (QMetaType::Int==prop.typeId()) || (QMetaType::UInt==prop.typeId()) ||\n                         (QMetaType::Double==prop.typeId()) || (QMetaType::QString==prop.typeId()) ||\n                         (QMetaType::fromType<Frequency>()==prop.metaType()) ||\n                         (QMetaType::fromType<Interval>()==prop.metaType()) ||\n                         (QMetaType::fromType<Level>()==prop.metaType()) ||\n                         (QMetaType::fromType<SelectiveCall>()==prop.metaType()) ||\n                         (QMetaType::fromType<QGeoCoordinate>()==prop.metaType()));\n\n    // If a basic type -> simply copy value\n    if (isBasicType && prop.isWritable() && (prop.typeId()==oprop.typeId())) {\n      if (! prop.write(this, oprop.read(&other))) {\n        logError() << \"Cannot set property '\" << prop.name() << \"' of \"\n                   << this->metaObject()->className() << \".\";\n        return false;\n      }\n    } else if (ConfigObjectReference *ref = prop.read(this).value<ConfigObjectReference *>()) {\n      if (! ref->copy(oprop.read(&other).value<ConfigObjectReference*>())) {\n        logError() << \"Cannot copy object reference '\" << prop.name() << \"' of \"\n                   << this->metaObject()->className() << \".\";\n        return false;\n      }\n    } else if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      if (! lst->copy(*oprop.read(&other).value<ConfigObjectList*>())) {\n        logError() << \"Cannot copy object list '\" << prop.name() << \"' of \"\n                   << this->metaObject()->className() << \".\";\n        return false;\n      }\n    } else if (ConfigObjectRefList *lst = prop.read(this).value<ConfigObjectRefList *>()) {\n      if (! lst->copy(*oprop.read(&other).value<ConfigObjectRefList*>())) {\n        logError() << \"Cannot copy reference list '\" << prop.name() << \"' of \"\n                   << this->metaObject()->className() << \".\";\n        return false;\n      }\n    } else if (propIsInstance<ConfigItem>(prop)) {\n      // If the item is owned by this item\n      if (prop.isWritable()) {\n        // If the owned item is writeable -> clone if set in other\n        if (oprop.read(&other).isNull()) {\n          if ((! prop.read(&other).isNull()) && (! prop.write(this, QVariant(prop.metaType())))) {\n            logError() << \"Cannot delete item '\" << prop.name() << \"' of \"\n                       << this->metaObject()->className() << \".\";\n            return false;\n          }\n        } else {\n          // Clone element form other item\n          ConfigItem *cl = oprop.read(&other).value<ConfigItem*>()->clone();\n          if (nullptr == cl) {\n            logError() << \"Cannot clone item '\" << oprop.name() << \"' of \"\n                       << other.metaObject()->className() << \".\";\n            return false;\n          }\n          // Write clone\n          if (! prop.write(this, QVariant(prop.metaType(), &cl))) {\n            logError() << \"Cannot replace item '\" << prop.name() << \"' of \"\n                       << this->metaObject()->className() << \".\";\n            cl->deleteLater();\n            return false;\n          }\n        }\n      } else if (! oprop.read(&other).isNull()) {\n        // If the owned item is not writable (must be present) -> copy from other if set\n        if (! prop.read(this).value<ConfigItem*>()->copy(*oprop.read(&other).value<ConfigItem*>())) {\n          logError() << \"Cannot copy fixed item '\" << prop.name() << \"' of \"\n                     << this->metaObject()->className() << \".\";\n          return false;\n        }\n      }\n    }\n  }\n\n  emit modified(this);\n  return true;\n}\n\nint\nConfigItem::compare(const ConfigItem &other) const {\n  // Check if other has the same type\n  if (strcmp(other.metaObject()->className(), metaObject()->className()))\n    return strcmp(metaObject()->className(), other.metaObject()->className());\n\n  // Compare by properties\n  const QMetaObject *meta = metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    // This property\n    QMetaProperty prop = meta->property(p);\n    // the same property over at other\n    QMetaProperty oprop = other.metaObject()->property(p);\n\n    // Should never happen\n    if (! prop.isValid())\n      continue;\n\n    // Handle comparison of basic types\n    if (prop.isEnumType() || prop.isFlagType() || (QMetaType::Bool == prop.typeId()) ||\n        (QMetaType::Int == prop.typeId()) || (QMetaType::UInt == prop.typeId())) {\n      int a=prop.read(this).toInt(), b=oprop.read(&other).toInt();\n      if (a<b)\n        return -1;\n      if (a>b)\n        return 1;\n      continue;\n    }\n\n    if (QMetaType::Double == prop.typeId()) {\n      double a=prop.read(this).toDouble(), b=oprop.read(&other).toDouble();\n      if (a<b)\n        return -1;\n      if (a>b)\n        return 1;\n      continue;\n    }\n\n    if (QMetaType::QString == prop.typeId()) {\n      int cmp = QString::compare(prop.read(this).toString(), oprop.read(&other).toString());\n      if (cmp)\n        return cmp;\n      continue;\n    }\n\n    if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n      Frequency a = prop.read(this).value<Frequency>(),\n        b = oprop.read(&other).value<Frequency>();\n      if (a<b)\n        return -1;\n      if (b<a)\n        return 1;\n      continue;\n    }\n\n    if (QMetaType::fromType<Interval>() == prop.metaType()) {\n      Interval a = prop.read(this).value<Interval>(), b = oprop.read(&other).value<Interval>();\n      if (a<b)\n        return -1;\n      if (b<a)\n        return 1;\n      continue;\n    }\n\n    if (QMetaType::fromType<Level>() == prop.metaType()) {\n      Level a = prop.read(this).value<Level>(), b = oprop.read(&other).value<Level>();\n      if (a<b)\n        return -1;\n      if (b<a)\n        return 1;\n      continue;\n    }\n\n    if (ConfigObjectReference *ref = prop.read(this).value<ConfigObjectReference *>()) {\n      int cmp = ref->compare(*oprop.read(&other).value<ConfigObjectReference*>());\n      if (cmp)\n        return cmp;\n      continue;\n    }\n\n    if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      int cmp = lst->compare(*oprop.read(&other).value<ConfigObjectList*>());\n      if (cmp)\n        return cmp;\n      continue;\n    }\n\n    if (propIsInstance<ConfigObjectRefList>(prop)) {\n      ConfigObjectRefList *lst = prop.read(this).value<ConfigObjectRefList *>();\n      int cmp = lst->compare(*oprop.read(&other).value<ConfigObjectRefList*>());\n      if (cmp)\n        return cmp;\n      continue;\n    }\n\n    if (propIsInstance<ConfigItem>(prop)) {\n      // If the owned item is writeable -> clone if set in other\n      if (prop.read(this).isNull() && !oprop.read(&other).isNull())\n        return -1;\n      if (!prop.read(this).isNull() && oprop.read(&other).isNull())\n        return 1;\n      if (prop.read(this).isNull() && oprop.read(&other).isNull())\n        continue;\n      int cmp = prop.read(this).value<ConfigItem*>()->compare(*oprop.read(&other).value<ConfigItem*>());\n      if (cmp)\n        return cmp;\n      continue;\n    }\n  }\n\n  return 0;\n}\n\n\nbool\nConfigItem::label(ConfigObject::Context &context, const ErrorStack &err) {\n  // Label properties owning config objects, that is of type ConfigObject or ConfigObjectList\n  const QMetaObject *meta = metaObject();\n\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid())\n      continue;\n    if (prop.read(this).value<ConfigObjectList *>()) {\n      ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>();\n      if (! lst->label(context, err))\n        return false;\n    } else if (prop.read(this).value<ConfigItem *>()) {\n      ConfigItem *obj = prop.read(this).value<ConfigItem *>();\n      if (! obj->label(context, err))\n        return false;\n    }\n  }\n\n  return true;\n}\n\n\nYAML::Node\nConfigItem::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node;\n  if (! populate(node, context, err))\n    return YAML::Node();\n  return node;\n}\n\nvoid\nConfigItem::clear() {\n  emit beginClear();\n\n  // Delete or clear all object owned by properties, that is ConfigObjectList and ConfigObject\n  const QMetaObject *meta = metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid())\n      continue;\n    if (propIsInstance<ConfigItem>(prop) && prop.isWritable()) {\n      prop.write(this, QVariant(prop.metaType()));\n    } else if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      lst->clear();\n    }\n  }\n\n  emit endClear();\n}\n\nbool\nConfigItem::populate(YAML::Node &node, const Context &context, const ErrorStack &err){\n  // Serialize all properties\n  const QMetaObject *meta = metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid())\n      continue;\n    if (! prop.isScriptable())\n      continue;\n\n    // Handle tags:\n    if (context.hasTag(prop.enclosingMetaObject()->className(), prop.name(), prop.read(this))) {\n      YAML::Node tag(YAML::NodeType::Scalar);\n      tag.SetTag(context.getTag(prop.enclosingMetaObject()->className(),\n                                prop.name(), prop.read(this)).toStdString());\n      node[prop.name()] = tag;\n      continue;\n    }\n\n    // Dispatch by type\n    if (prop.isFlagType()) {\n      QMetaEnum e = prop.enumerator();\n      QStringList keys = QString::fromLatin1(e.valueToKeys(prop.read(this).toInt())).split(\"|\");\n      YAML::Node list;\n      for(auto key: keys)\n        list.push_back(key.toStdString());\n      node[prop.name()] = list;\n    } else if (prop.isEnumType()) {\n      QMetaEnum e = prop.enumerator();\n      QVariant value = prop.read(this);\n      const char *key = e.valueToKey(value.toInt());\n      if (nullptr == key) {\n        errMsg(err) << \"Cannot map value \" << value.toUInt()\n                    << \" to enum \" << e.name()\n                    << \". Ignore attribute '\" << prop.name()\n                    << \"' but this points to an incompatibility in some codeplug. \"\n                    << \"Consider reporting it to https://github.com/hmatuschek/qdmr/issues.\";\n        continue;\n      }\n      node[prop.name()] = key;\n    } else if (QMetaType::Bool == prop.typeId()) {\n      node[prop.name()] = this->property(prop.name()).toBool();\n    } else if (QMetaType::Int == prop.typeId()) {\n      node[prop.name()] = this->property(prop.name()).toInt();\n    } else if (QMetaType::UInt == prop.typeId()) {\n      node[prop.name()] = this->property(prop.name()).toUInt();\n    } else if (QMetaType::Double == prop.typeId()) {\n      node[prop.name()] = this->property(prop.name()).toDouble();\n    } else if (QMetaType::QString == prop.typeId()) {\n      node[prop.name()] = this->property(prop.name()).toString().toStdString();\n    } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n      node[prop.name()] = this->property(prop.name()).value<Frequency>();\n    } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n      node[prop.name()] = this->property(prop.name()).value<Interval>();\n    } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n      node[prop.name()] = this->property(prop.name()).value<Level>();\n    } else if (QMetaType::fromType<SelectiveCall>() == prop.metaType()) {\n      node[prop.name()] = this->property(prop.name()).value<SelectiveCall>();\n    } else if (QMetaType::fromType<QGeoCoordinate>() == prop.metaType()) {\n      node[prop.name()] = this->property(prop.name()).value<QGeoCoordinate>();\n    } else if (ConfigObjectReference *ref = prop.read(this).value<ConfigObjectReference *>()) {\n      ConfigObject *obj = ref->as<ConfigObject>();\n      if (nullptr == obj)\n        continue;\n      if (context.hasTag(prop.enclosingMetaObject()->className(),\n                         prop.name(), QVariant::fromValue(obj))) {\n        YAML::Node tag(YAML::NodeType::Scalar);\n        tag.SetTag(context.getTag(prop.enclosingMetaObject()->className(),\n                                  prop.name(), QVariant::fromValue(obj)).toStdString());\n        node[prop.name()] = tag;\n        continue;\n      } else if (! context.contains(obj)) {\n        errMsg(err) << \"Cannot reference object of type \" << obj->metaObject()->className()\n                    << \" object not labeled.\";\n        return false;\n      }\n      node[prop.name()] = context.getId(obj).toStdString();\n    } else if (ConfigObjectRefList *refs = prop.read(this).value<ConfigObjectRefList *>()) {\n      //logDebug() << \"Serialize obj ref list w/ \" << refs->count() << \" elements.\" ;\n      YAML::Node list = YAML::Node(YAML::NodeType::Sequence);\n      list.SetStyle(YAML::EmitterStyle::Flow);\n      for (int i=0; i<refs->count(); i++) {\n        ConfigObject *obj = refs->get(i);\n        if (! context.contains(obj)) {\n          errMsg(err) << \"Cannot reference object of type \" << obj->metaObject()->className()\n                      << \" object not labeled.\";\n          return false;\n        }\n        list.push_back(context.getId(obj).toStdString());\n      }\n      node[prop.name()] = list;\n    } else if (propIsInstance<ConfigItem>(prop)) {\n      ConfigItem *obj = prop.read(this).value<ConfigItem *>();\n      // Serialize config objects in-place.\n      if (obj)\n        node[prop.name()] = obj->serialize(context);\n    } else if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      // Serialize config object lists in-place.\n      node[prop.name()] = lst->serialize(context);\n    } else {\n      logDebug() << \"Unhandled property \" << prop.name()\n                 << \" of unknown type \" << prop.typeName() << \".\";\n    }\n  }\n\n  return true;\n}\n\nConfigItem *\nConfigItem::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(node); Q_UNUSED(ctx);\n\n  if (QMetaType::UnknownType == prop.userType()) {\n    errMsg(err) << \"Cannot instantiate child of unregistered type \" << prop.typeName() << \".\";\n    return nullptr;\n  }\n\n  QMetaType type(prop.userType());\n  if (! (QMetaType::PointerToQObject & type.flags())) {\n    errMsg(err) << \"Cannot instantiate child of non-qobject pointer type \" << prop.typeName() << \".\";\n    return nullptr;\n  }\n\n  const QMetaObject *propType = type.metaObject();\n  /*logDebug() << \"Try to instantiate child of type \" << prop.typeName()\n             << \" for element \" << prop.name()\n             << \" in \" << this->metaObject()->className() << \".\";*/\n\n  ConfigItem *item = qobject_cast<ConfigItem *>(\n        propType->newInstance(Q_ARG(QObject *,this)));\n  if (! item) {\n    errMsg(err) << \"Could not instantiate \" << propType->className() << \".\";\n  }\n  return item;\n}\n\nbool\nConfigItem::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node) {\n    errMsg(err) << \"YAML node is null!\";\n    return false;\n  }\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse element: Expected object.\";\n    return false;\n  }\n\n  const QMetaObject *meta = this->metaObject();\n  for (int p=QObject::staticMetaObject.propertyOffset(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n\n    if ((! prop.isValid()) || (! prop.isScriptable()))\n      continue;\n\n    if (prop.isRequired() && !node[prop.name()]) {\n      errMsg(err) << \"Required property '\" << prop.name() << \"' not set!\";\n      return false;\n    }\n\n    // Tags are handled during linking\n    if (node[prop.name()] && (! node[prop.name()].IsNull()) && node[prop.name()].IsScalar()\n        && (node[prop.name()].Scalar().empty()) && (! node[prop.name()].Tag().empty()))\n      continue;\n\n    if (prop.isFlagType()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check enum key\n      if (! node[prop.name()].IsSequence()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected list of enum key.\";\n        return false;\n      }\n      QMetaEnum e = prop.enumerator();\n      QByteArray keys;\n      for (auto i: node[prop.name()]) {\n        if (!keys.isEmpty())\n          keys.append(\"|\");\n        keys.append(i.as<std::string>());\n      }\n      bool ok=true; int value = e.keysToValue(keys.constData(), &ok);\n      if (! ok) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Unknown key-combination '\" << keys << \"' for enum '\" << prop.name()\n                    << \"'. Expected one of \" << enumKeys(e).join(\", \") << \".\";\n        return false;\n      }\n      // finally set property\n      prop.write(this, value);\n    } else if (prop.isEnumType()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check enum key\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected enum key.\";\n        return false;\n      }\n      QMetaEnum e = prop.enumerator();\n      std::string key = node[prop.name()].as<std::string>();\n      bool ok=true; int value = e.keyToValue(key.c_str(), &ok);\n      if (! ok) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Unknown key '\" << key.c_str() << \"' for enum '\" << prop.name()\n                    << \"'. Expected one of \" << enumKeys(e).join(\", \") << \".\";\n        return false;\n      }\n      // finally set property\n      prop.write(this, value);\n    } else if (QMetaType::Bool == prop.typeId()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected boolean value.\";\n        return false;\n      }\n      prop.write(this, node[prop.name()].as<bool>());\n    } else if (QMetaType::Int == prop.typeId()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected integer value.\";\n        return false;\n      }\n      prop.write(this, node[prop.name()].as<int>());\n    } else if (QMetaType::UInt == prop.typeId()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected unsigned integer value.\";\n        return false;\n      }\n      prop.write(this, node[prop.name()].as<unsigned>());\n    } else if (QMetaType::Double == prop.typeId()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected floating point value.\";\n        return false;\n      }\n      prop.write(this, node[prop.name()].as<double>());\n    } else if (QMetaType::QString == prop.typeId()) {\n      // If property is not set -> skip\n      if ( (!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected string.\";\n        return false;\n      }\n      prop.write(this, QString::fromStdString(node[prop.name()].as<std::string>()));\n    } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n      // If property is not set -> skip\n      if ((! node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected frequency.\";\n        return false;\n      }\n      Frequency f = node[prop.name()].as<Frequency>();\n      prop.write(this, QVariant::fromValue(f));\n    } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected interval.\";\n        return false;\n      }\n      prop.write(this, QVariant::fromValue(node[prop.name()].as<Interval>()));\n    } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n      // If property is not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull() || (!prop.isWritable()))\n        continue;\n      // parse & check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected level.\";\n        return false;\n      }\n      prop.write(this, QVariant::fromValue(node[prop.name()].as<Level>()));\n    } else if (QMetaType::fromType<SelectiveCall>() == prop.metaType()) {\n      // If property is not set -> skip\n      if ((! node[prop.name()]) || (node[prop.name()].IsNull()) || (!prop.isWritable())) {\n        prop.write(this, QVariant::fromValue(SelectiveCall()));\n        continue;\n      }\n      // parse & check type\n      if ((! node[prop.name()].IsMap()) || (1 != node[prop.name()].size())) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected selective call.\";\n        return false;\n      }\n      prop.write(this, QVariant::fromValue(node[prop.name()].as<SelectiveCall>()));\n    } else if (QMetaType::fromType<QGeoCoordinate>() == prop.metaType()) {\n      // If property is not set -> skip\n      if ((! node[prop.name()]) || (node[prop.name()].IsNull()) || (!prop.isWritable())) {\n        prop.write(this, QVariant::fromValue(QGeoCoordinate()));\n        continue;\n      }\n      // parse & check type\n      if ((! node[prop.name()].IsMap()) || (2 > node[prop.name()].size())) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected geo coordinate.\";\n        return false;\n      }\n      QGeoCoordinate value = node[prop.name()].as<QGeoCoordinate>(QGeoCoordinate());\n      qDebug() << \"Write to property\" << prop.name() << \"value\" << value;\n      prop.write(this, QVariant::fromValue(value));\n    } else if (prop.read(this).value<ConfigObjectReference *>()) {\n      // references are linked later\n      continue;\n    } else if (prop.read(this).value<ConfigObjectRefList *>()) {\n      // reference lists are linked later\n      continue;\n    } else if (propIsInstance<ConfigItem>(prop)) {\n      if ((!node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n      // check type\n      if (! node[prop.name()].IsMap()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse '\" << prop.name() << \"' of '\" << meta->className()\n                    << \"': Expected instance of '\"\n                    << QMetaType(prop.typeId()).metaObject()->className() << \"'.\";\n        return false;\n      }\n      // Get object\n      ConfigItem *obj = prop.read(this).value<ConfigItem*>();\n\n      // If not set and writable -> allocate and set\n      if ((nullptr == obj) && prop.isWritable()) {\n        if (nullptr == (obj = this->allocateChild(prop, node[prop.name()], ctx))) {\n          errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                      << \": Cannot allocate \" << prop.name() << \" of \" << meta->className() << \".\";\n          return false;\n        }\n        // Set property\n        if (! prop.write(this, QVariant::fromValue(obj))) {\n          if (nullptr == obj->parent())\n            obj->deleteLater();\n          errMsg(err) << \"Cannot set writable property '\" << prop.name()\n                      << \"' in \" << this->metaObject()->className() << \".\";\n          return false;\n        }\n      }\n\n      // parse instance\n      YAML::Node propNode = node[prop.name()];\n      if (obj && (! obj->parse(propNode, ctx, err))) {\n        errMsg(err) << propNode.Mark().line << \":\" << propNode.Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className() << \".\";\n        if (nullptr == obj->parent())\n          obj->deleteLater();\n        return false;\n      }\n    } else if (prop.read(this).value<ConfigObjectList *>()) {\n      if ((!node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n      // check type\n      if (! node[prop.name()].IsSequence()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot parse \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected instance of '\"\n                    << QMetaType(prop.typeId()).metaObject()->className() << \"'.\";\n        return false;\n      }\n      // Get list\n      ConfigObjectList *lst = prop.read(this).value<ConfigObjectList*>();\n      // If not set and writable -> allocate and set\n      if ((nullptr == lst) && prop.isWritable()) {\n        if (nullptr == (lst = this->allocateChild(prop, node[prop.name()], ctx)->as<ConfigObjectList>())) {\n          errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                      << \": Cannot allocate list \" << prop.name() << \" of \" << meta->className() << \".\";\n          return false;\n        }\n        // Set property\n        if (! prop.write(this, QVariant::fromValue(lst))) {\n          if (nullptr == lst->parent())\n            lst->deleteLater();\n          errMsg(err) << \"Cannot set writable property '\" << prop.name()\n                      << \"' in \" << this->metaObject()->className() << \".\";\n          return false;\n        }\n      }\n\n      // Allocate elements\n      ConfigObject *obj = nullptr;\n      for (YAML::const_iterator it=node[prop.name()].begin(); it!=node[prop.name()].end(); it++) {\n        // allocate element\n        if (nullptr == (obj = lst->allocateChild(*it, ctx, err)->as<ConfigObject>())) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot allocate child of list.\";\n          return false;\n        }\n        // parse element\n        if (obj && (! obj->parse(*it, ctx))) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot parse object of type \" << obj->metaObject()->className();\n          if (nullptr == obj->parent())\n            obj->deleteLater();\n          return false;\n        }\n        // add element to list\n        if (-1 == lst->add(obj)) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot add element '\" << obj->name() << \"' of type \"\n                      << obj->metaObject()->className() << \"' to list.\";\n          if (nullptr == obj->parent())\n            obj->deleteLater();\n          return false;\n        }\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nConfigItem::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  const QMetaObject *meta = this->metaObject();\n\n  for (int p=QObject::staticMetaObject.propertyOffset(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid())\n      continue;\n\n    if (! prop.isScriptable()) {\n      //logDebug() << \"Do not link property '\" << prop.name() << \"': Marked as not scriptable.\";\n      continue;\n    }\n\n    if (ConfigObjectReference *ref = prop.read(this).value<ConfigObjectReference *>()) {\n      // If not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n      // check type\n      if (! node[prop.name()].IsScalar()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected id.\";\n        return false;\n      }\n      // handle tags\n      QString tag = QString::fromStdString(node[prop.name()].Tag());\n      if ((!node[prop.name()].Scalar().size()) && (!tag.isEmpty())) {\n        if (! ref->set(ctx.tagGetValue(prop.enclosingMetaObject()->className(), prop.name(), tag).value<ConfigObject *>())) {\n          errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                      << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                      << \": Unknown tag \" << tag << \".\";\n          return false;\n        }\n        continue;\n      }\n      // set reference\n      QString id = QString::fromStdString(node[prop.name()].as<std::string>());\n      if (! ctx.contains(id)) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link reference to '\" << id << \"', element not defined.\";\n        return false;\n      }\n      if (! ref->set(ctx.getObj(id))) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Cannot set reference.\";\n        return false;\n      }\n      /*logDebug() << \"Linked reference \" << prop.name() << \"='\" << id\n                 << \"' to \" << ctx.getObj(id)->metaObject()->className()\n                 << \" '\" << ctx.getObj(id)->name() << \"'.\";*/\n    } else if (ConfigObjectRefList *lst = prop.read(this).value<ConfigObjectRefList *>()) {\n      // If not set -> skip\n      if ((!node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n      // check type\n      if (! node[prop.name()].IsSequence()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected sequence.\";\n        return false;\n      }\n      for (YAML::const_iterator it=node[prop.name()].begin(); it!=node[prop.name()].end(); it++) {\n        if (! it->IsScalar()) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                      << \": Expected ID string.\";\n          return false;\n        }\n        // check for tags\n        QString tag = QString::fromStdString(it->Tag());\n        if ((!it->Scalar().size()) && (!tag.isEmpty())) {\n          if (0 > lst->add(ctx.tagGetValue(prop.enclosingMetaObject()->className(), prop.name(), tag).value<ConfigObject*>())) {\n            errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                        << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                        << \": Cannot add reference for tag '\" << tag << \"'.\";\n            return false;\n          }\n          continue;\n        }\n        QString id = QString::fromStdString(it->as<std::string>());\n        if (! ctx.contains(id)) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                      << \": Reference '\" << id << \"' not defined.\";\n          return false;\n        }\n        if (0 > lst->add(ctx.getObj(id))) {\n          errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                      << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                      << \": Cannot add reference to '\" << id << \"' to list.\";\n          return false;\n        }\n      }\n    } else if (ConfigItem *obj = prop.read(this).value<ConfigItem *>()) {\n      // If not set -> skip\n      if ((! node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n\n      // check type\n      if (! node[prop.name()].IsMap()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected object.\";\n        return false;\n      }\n\n      if (! obj->link(node[prop.name()], ctx, err)) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className() << \".\";\n        return false;\n      }\n    } else if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      // If not set -> skip\n      if ((! node[prop.name()]) || node[prop.name()].IsNull())\n        continue;\n\n      // check type\n      if (! node[prop.name()].IsSequence()) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Expected sequence.\";\n        return false;\n      }\n\n      if (! lst->link(node[prop.name()], ctx, err)) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className() << \".\";\n        return false;\n      }\n    } else {\n      YAML::Node element = node[prop.name()];\n      // If not set, not scalar or not tagged -> skip\n      if ((!element) || element.IsNull() || (! element.IsScalar()) || (! element.Scalar().empty())\n          || element.Tag().empty())\n        continue;\n      // Handle tagged values\n      QString tag = QString::fromStdString(node[prop.name()].Tag());\n      if (! prop.write(this, ctx.tagGetValue(prop.enclosingMetaObject()->className(), prop.name(), tag))) {\n        errMsg(err) << node[prop.name()].Mark().line << \":\" << node[prop.name()].Mark().column\n                    << \": Cannot link \" << prop.name() << \" of \" << meta->className()\n                    << \": Unknown tag \" << tag << \".\";\n        return false;\n      }\n    }\n  }\n\n  return true;\n}\n\nconst Config *\nConfigItem::config() const {\n  if (nullptr == parent())\n    return nullptr;\n  if (ConfigItem *item = qobject_cast<ConfigItem*>(parent()))\n    return item->config();\n  if (ConfigObjectList *lst = qobject_cast<ConfigObjectList*>(parent()))\n    return lst->config();\n  return nullptr;\n}\n\nvoid\nConfigItem::findItemsOfTypes(const QStringList &typeNames, QSet<ConfigItem *> &items) const {\n  // Do not check yourself\n  const QMetaObject *meta = metaObject();\n\n  // Visit all properties\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid())\n      continue;\n    if (! prop.isReadable())\n      continue;\n\n    if (ConfigItem *obj = prop.read(this).value<ConfigItem *>()) {\n      if (isInstanceOf(obj, typeNames))\n        items.insert(obj);\n      obj->findItemsOfTypes(typeNames, items);\n    } else if (ConfigObjectList *lst = prop.read(this).value<ConfigObjectList *>()) {\n      lst->findItemsOfTypes(typeNames, items);\n    }\n  }\n}\n\nbool\nConfigItem::hasDescription() const {\n  const QMetaObject *meta = metaObject();\n  return (0 <= meta->indexOfClassInfo(\"description\"));\n}\n\nbool\nConfigItem::hasLongDescription() const {\n  const QMetaObject *meta = metaObject();\n  return (0 <= meta->indexOfClassInfo(\"longDescription\"));\n}\n\nbool\nConfigItem::hasDescription(const QMetaProperty &prop) const {\n  if (! prop.isValid())\n    return false;\n  QString infoName = QString(\"%1Description\").arg(prop.name());\n  const QMetaObject *meta = metaObject();\n  return (0 <= meta->indexOfClassInfo(infoName.toLocal8Bit().constData()));\n}\n\nbool\nConfigItem::hasLongDescription(const QMetaProperty &prop) const {\n  if (! prop.isValid())\n    return false;\n  QString infoName = QString(\"%1LongDescription\").arg(prop.name());\n  const QMetaObject *meta = metaObject();\n  return (0 <= meta->indexOfClassInfo(infoName.toLocal8Bit().constData()));\n}\n\nQString\nConfigItem::description() const {\n  if (! hasDescription())\n    return metaObject()->className();\n  const QMetaObject *meta = metaObject();\n  return meta->classInfo(meta->indexOfClassInfo(\"description\")).value();\n}\n\nQString\nConfigItem::longDescription() const {\n  if (! hasLongDescription())\n    return QString();\n  const QMetaObject *meta = metaObject();\n  return meta->classInfo(meta->indexOfClassInfo(\"longDescription\")).value();\n}\n\nQString\nConfigItem::description(const QMetaProperty &prop) const {\n  if (! hasDescription(prop))\n    return QString();\n  QString infoName = QString(\"%1Description\").arg(prop.name());\n  const QMetaObject *meta = metaObject();\n  return meta->classInfo(meta->indexOfClassInfo(infoName.toLocal8Bit().constData())).value();\n}\n\nQString\nConfigItem::longDescription(const QMetaProperty &prop) const {\n  if (! hasLongDescription(prop))\n    return QString();\n  QString infoName = QString(\"%1LongDescription\").arg(prop.name());\n  const QMetaObject *meta = metaObject();\n  return meta->classInfo(meta->indexOfClassInfo(infoName.toLocal8Bit().constData())).value();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigObject\n * ********************************************************************************************* */\nConfigObject::ConfigObject(QObject *parent)\n  : ConfigItem(parent), _name()\n{\n  // pass...\n}\n\nConfigObject::ConfigObject(const QString &name, QObject *parent)\n  : ConfigItem(parent), _name(name)\n{\n  // pass...\n}\n\nconst QString &\nConfigObject::name() const {\n  return _name;\n}\n\nvoid\nConfigObject::setName(const QString &name) {\n  if (name.simplified().isEmpty() || (_name == name.simplified()))\n    return;\n  _name = name;\n  emit modified(this);\n}\n\nQString\nConfigObject::idPrefix() const {\n  return findIdPrefix(this->metaObject());\n}\n\nbool\nConfigObject::label(ConfigObject::Context &context, const ErrorStack &err) {\n  unsigned n=1;\n  QString prefix = this->idPrefix();\n  QString id=QString(\"%1%2\").arg(prefix).arg(n);\n  while (context.contains(id)) {\n    id=QString(\"%1%2\").arg(prefix).arg(++n);\n  }\n\n  if (! context.add(id, this)) {\n    if (context.contains(this))\n      errMsg(err) << \"Object already in context with id '\" << context.getId(this) << \"'.\";\n    errMsg(err) << \"Cannot add element '\" << id << \"' to context.\";\n    return false;\n  }\n\n  if (! ConfigItem::label(context, err))\n    return false;\n\n  return true;\n}\n\nbool\nConfigObject::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node[\"id\"]) {\n    logWarn() << node.Mark().line << \":\" << node.Mark().column\n              << \": No id specified for \" << metaObject()->className()\n              << \". Object cannot be referenced later.\";\n  } else {\n    QString id = QString::fromStdString(node[\"id\"].as<std::string>());\n    if (! ctx.add(id, this)) {\n      errMsg(err) << node[\"id\"].Mark().line << \":\" << node[\"id\"].Mark().column\n                  << \": Cannot register ID '\" << id << \"'.\";\n      return false;\n    }\n  }\n\n  return ConfigItem::parse(node, ctx, err);\n}\n\nbool\nConfigObject::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  if (context.contains(this))\n    node[\"id\"] = context.getId(this).toStdString();\n  return ConfigItem::populate(node, context, err);\n}\n\nQString\nConfigObject::findIdPrefix(const QMetaObject *meta) {\n  for (int i=meta->classInfoOffset(); i<meta->classInfoCount(); i++) {\n    if (0 == strcmp(\"IdPrefix\", meta->classInfo(i).name())) {\n      return meta->classInfo(i).value();\n    }\n  }\n  if (meta->superClass())\n    return findIdPrefix(meta->superClass());\n  return \"\";\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigExtension\n * ********************************************************************************************* */\nConfigExtension::ConfigExtension(QObject *parent)\n  : ConfigItem(parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AbstractConfigObjectList\n * ********************************************************************************************* */\nAbstractConfigObjectList::AbstractConfigObjectList(const QMetaObject &elementType, QObject *parent)\n  : QObject(parent), _elementTypes(), _items()\n{\n  _elementTypes.append(elementType);\n}\n\nAbstractConfigObjectList::AbstractConfigObjectList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent)\n  : QObject(parent), _elementTypes(elementTypes), _items()\n{\n  // pass...\n}\n\nbool\nAbstractConfigObjectList::copy(const AbstractConfigObjectList &other) {\n  this->clear();\n  _elementTypes = other._elementTypes;\n  foreach (ConfigObject *item, other._items)\n    add(item);\n  return true;\n}\n\nint\nAbstractConfigObjectList::count() const {\n  return _items.count();\n}\n\nint\nAbstractConfigObjectList::indexOf(ConfigObject *obj) const {\n  return _items.indexOf(obj);\n}\n\nvoid\nAbstractConfigObjectList::clear() {\n  for (int i=(count()-1); i>=0; i--) {\n    _items.pop_back();\n    emit elementRemoved(i);\n  }\n}\n\nconst Config *\nAbstractConfigObjectList::config() const {\n  if (nullptr == parent())\n    return nullptr;\n  if (ConfigItem *item = qobject_cast<ConfigItem *>(parent()))\n    return item->config();\n  return nullptr;\n}\n\nvoid\nAbstractConfigObjectList::findItemsOfTypes(const QStringList &typeNames, QSet<ConfigItem *> &items) const {\n  foreach (ConfigObject *obj, _items) {\n    if (isInstanceOf(obj, typeNames))\n      items.insert(obj);\n    obj->findItemsOfTypes(typeNames, items);\n  }\n}\n\nQList<ConfigObject *>\nAbstractConfigObjectList::findItemsByName(const QString name) const {\n  QList<ConfigObject *> items;\n  foreach (ConfigObject *obj, _items) {\n    if (obj->name() == name)\n      items.append(obj);\n  }\n  return items;\n}\n\nbool\nAbstractConfigObjectList::has(ConfigObject *obj) const {\n  return 0 <= indexOf(obj);\n}\n\nConfigObject *\nAbstractConfigObjectList::get(int idx) const {\n  return _items.value(idx, nullptr);\n}\n\nint\nAbstractConfigObjectList::add(ConfigObject *obj, int row, bool unique) {\n  // Ignore nullptr\n  if (nullptr == obj)\n    return -1;\n  // If already in list -> ignore\n  if (unique && (0 <= indexOf(obj)))\n    return -1;\n  if (-1 == row)\n    row = _items.size();\n  // Check type\n  bool matchesType = false;\n  foreach (const QMetaObject &type, _elementTypes) {\n    if (obj->inherits(type.className())) {\n      matchesType = true;\n      break;\n    }\n  }\n  if (! matchesType) {\n    logError() << \"Cannot add element of type \" << obj->metaObject()->className()\n               << \" to list, expected instances of \" << classNames().join(\", \");\n    return -1;\n  }\n  _items.insert(row, obj);\n  // Otherwise connect to object\n  connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onElementDeleted(QObject*)));\n  connect(obj, SIGNAL(modified(ConfigItem*)), this, SLOT(onElementModified(ConfigItem*)));\n  emit elementAdded(row);\n  return row;\n}\n\nint\nAbstractConfigObjectList::replace(ConfigObject *obj, int row, bool unique) {\n  // Ignore nullptr\n  if (nullptr == obj)\n    return -1;\n  // Check index\n  if (row >= count())\n    return -1;\n  // Check if self-replacement\n  if (row == indexOf(obj))\n    return indexOf(obj);\n  // If already in list -> ignore\n  if (unique && (0 <= indexOf(obj)))\n    return -1;\n  // Check type\n  bool matchesType = false;\n  foreach (const QMetaObject &type, _elementTypes) {\n    if (obj->inherits(type.className())) {\n      matchesType = true;\n      break;\n    }\n  }\n  if (! matchesType) {\n    logError() << \"Cannot add element of type \" << obj->metaObject()->className()\n               << \" to list, expected instances of \" << classNames().join(\", \");\n    return -1;\n  }\n\n  // Remove present element\n  ConfigObject *oldobj = _items.at(row);\n  _items.remove(row, 1);\n  emit elementRemoved(row);\n  disconnect(oldobj, nullptr, this, nullptr);\n\n  _items.insert(row, obj);\n  // connect to object\n  connect(obj, SIGNAL(destroyed(QObject*)), this, SLOT(onElementDeleted(QObject*)));\n  connect(obj, SIGNAL(modified(ConfigItem*)), this, SLOT(onElementModified(ConfigItem*)));\n  emit elementAdded(row);\n\n  return row;\n}\n\nbool\nAbstractConfigObjectList::take(ConfigObject *obj) {\n  // Ignore nullptr\n  if (nullptr == obj)\n    return false;\n  int idx = indexOf(obj);\n  if (0 > idx)\n    return false;\n  _items.remove(idx, 1);\n  emit elementRemoved(idx);\n  // Otherwise disconnect from\n  disconnect(obj, nullptr, this, nullptr);\n  return true;\n}\n\nbool\nAbstractConfigObjectList::del(ConfigObject *obj) {\n  return take(obj);\n}\n\nbool\nAbstractConfigObjectList::moveUp(int row) {\n  if ((row <= 0) || (row>=count()))\n    return false;\n  std::swap(_items[row-1], _items[row]);\n  return true;\n}\n\nbool\nAbstractConfigObjectList::moveUp(int first, int last) {\n  if ((first <= 0) || (last>=count()))\n    return false;\n  for (int row=first; row<=last; row++)\n    std::swap(_items[row-1], _items[row]);\n  return true;\n}\n\nbool\nAbstractConfigObjectList::moveDown(int row) {\n  if ((row >= (count()-1)) || (0 > row))\n    return false;\n  std::swap(_items[row+1], _items[row]);\n  return true;\n}\n\nbool\nAbstractConfigObjectList::moveDown(int first, int last) {\n  if ((last >= (count()-1)) || (0 > first))\n    return false;\n  for (int row=last; row>=first; row--)\n    std::swap(_items[row+1], _items[row]);\n  return true;\n}\n\n\nbool\nAbstractConfigObjectList::move(int source, int count, int destination) {\n  if ((0 == count) || (source == destination))\n    return true;\n\n  if ((source+count)>_items.size())\n    return false;\n\n  if (source > destination) {\n    // move up\n    for (int take=source, put=destination, i=0; i<count; i++, take++, put++)\n      _items.insert(put, _items.takeAt(take));\n  } else {\n    // move down\n    for (int i=0; i<count; i++)\n      _items.insert(destination-1, _items.takeAt(source));\n  }\n\n  for (int i=0; i<count; i++)\n    emit elementModified(destination+i);\n\n  return true;\n}\n\n\nconst QList<QMetaObject> &\nAbstractConfigObjectList::elementTypes() const {\n  return _elementTypes;\n}\n\nQStringList\nAbstractConfigObjectList::classNames() const {\n  QStringList cls;\n  foreach (const QMetaObject &type, _elementTypes) {\n    cls.append(type.className());\n  }\n  return cls;\n}\n\nvoid\nAbstractConfigObjectList::onElementModified(ConfigItem *obj) {\n  int idx = indexOf(obj->as<ConfigObject>());\n  if (0 <= idx)\n    emit elementModified(idx);\n}\n\nvoid\nAbstractConfigObjectList::onElementDeleted(QObject *obj) {\n  // Use reinterpret cast here as the obj may already be destroyed and this all RTTI freed.\n  // We just use the pointer address to remove the element here.\n  int idx = indexOf(reinterpret_cast<ConfigObject *>(obj));\n  if (0 <= idx) {\n    _items.remove(idx);\n    emit elementRemoved(idx);\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigObjectList\n * ********************************************************************************************* */\nConfigObjectList::ConfigObjectList(const QMetaObject &elementType, QObject *parent)\n  : AbstractConfigObjectList(elementType, parent)\n{\n  // pass...\n}\n\nConfigObjectList::ConfigObjectList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent)\n  : AbstractConfigObjectList(elementTypes, parent)\n{\n  // pass...\n}\n\nbool\nConfigObjectList::label(ConfigItem::Context &context, const ErrorStack &err) {\n  foreach (ConfigItem *obj, _items) {\n    if (! obj->label(context, err))\n      return false;\n  }\n  return true;\n}\n\nYAML::Node\nConfigObjectList::serialize(const ConfigItem::Context &context, const ErrorStack &err) {\n  YAML::Node list(YAML::NodeType::Sequence);\n  foreach (ConfigItem *obj, _items) {\n    YAML::Node node = obj->serialize(context, err);\n    if (node.IsNull())\n      return node;\n    list.push_back(node);\n  }\n  return list;\n}\n\nbool\nConfigObjectList::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if (!node.IsSequence()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse list: Expected list.\";\n    return false;\n  }\n\n  for (YAML::Node::const_iterator it=node.begin(); it!=node.end(); it++) {\n    // Create object for node\n    ConfigItem *element = allocateChild(*it, ctx);\n    if ((nullptr == element) || (!element->is<ConfigObject>())) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column << \": Cannot parse list.\";\n      return false;\n    }\n    if (! element->parse(*it, ctx, err)) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column << \": Cannot parse list.\";\n      element->deleteLater();\n      return false;\n    }\n    if (0 > add(element->as<ConfigObject>())) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot add element to list.\";\n      element->deleteLater();\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nConfigObjectList::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if (!node.IsSequence()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse list: Expected list.\";\n    return false;\n  }\n  int i = 0;\n  YAML::Node::const_iterator it=node.begin();\n  for (; it!=node.end(); it++, i++) {\n    // Create object for node\n    ConfigItem *element = get(i);\n    if (nullptr == element) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot link list, \" << i << \"-th element not present.\";\n      return false;\n    }\n    if (! element->link(*it, ctx, err)) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot link list.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nint ConfigObjectList::add(ConfigObject *obj, int row, bool unique) {\n  if (0 <= (row = AbstractConfigObjectList::add(obj, row, unique)))\n    obj->setParent(this);\n  return row;\n}\n\nbool\nConfigObjectList::take(ConfigObject *obj) {\n  if (AbstractConfigObjectList::take(obj))\n    obj->setParent(nullptr);\n  return true;\n}\n\nbool\nConfigObjectList::del(ConfigObject *obj) {\n  if (AbstractConfigObjectList::del(obj))\n    delete obj;\n  return true;\n}\n\nvoid\nConfigObjectList::clear() {\n  QVector<ConfigObject *> items = _items;\n  AbstractConfigObjectList::clear();\n  for (int i=0; i<items.count(); i++)\n    items[i]->deleteLater();\n}\n\nbool\nConfigObjectList::copy(const AbstractConfigObjectList &other) {\n  clear();\n  _elementTypes = other.elementTypes();\n  for (int i=0; i<other.count(); i++)\n    add(other.get(i)->clone()->as<ConfigObject>());\n  return true;\n}\n\nint\nConfigObjectList::compare(const ConfigObjectList &other) const {\n  if (count() < other.count())\n    return -1;\n  if (count() > other.count())\n    return 1;\n  for (int i=0; i<count(); i++) {\n    int cmp = this->get(i)->compare(*other.get(i));\n    if (cmp) return cmp;\n  }\n\n  return 0;\n}\n\n/* ********************************************************************************************* *\n * Implementation of ConfigObjectRefList\n * ********************************************************************************************* */\nConfigObjectRefList::ConfigObjectRefList(const QMetaObject &elementType, QObject *parent)\n  : AbstractConfigObjectList(elementType, parent)\n{\n  // pass...\n}\n\nConfigObjectRefList::ConfigObjectRefList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent)\n  : AbstractConfigObjectList(elementTypes, parent)\n{\n  // pass...\n}\n\nbool\nConfigObjectRefList::label(ConfigItem::Context &context, const ErrorStack &err) {\n  Q_UNUSED(context); Q_UNUSED(err);\n  // pass...\n  return true;\n}\n\nYAML::Node\nConfigObjectRefList::serialize(const ConfigItem::Context &context, const ErrorStack &err) {\n  YAML::Node list(YAML::NodeType::Sequence);\n  foreach (ConfigObject *obj, _items) {\n    if (! context.contains(obj)) {\n      errMsg(err) << \"Cannot serialized ref list: Object '\" << obj->name() << \"' not in context!\";\n      return YAML::Node();\n    }\n    list.push_back(context.getId(obj).toStdString());\n  }\n  return list;\n}\n\nint\nConfigObjectRefList::compare(const ConfigObjectRefList &other) const {\n  if (count() < other.count()) return -1;\n  if (count() > other.count()) return 1;\n  for (int i=0; i<count(); i++) {\n    int cmp = get(i)->compare(*other.get(i));\n    if (cmp) return cmp;\n  }\n  return 0;\n}\n\n"
  },
  {
    "path": "lib/configobject.hh",
    "content": "#ifndef CONFIGOBJECT_HH\n#define CONFIGOBJECT_HH\n\n#include <QObject>\n#include <QString>\n#include <QHash>\n#include <QVector>\n#include <QMetaProperty>\n\n#include <yaml-cpp/yaml.h>\n\n#include \"errorstack.hh\"\n\n// Forward declaration\nclass Config;\nclass ConfigObject;\nclass ConfigExtension;\n\n/** Helper function to test property type. */\ntemplate <class T>\nbool propIsInstance(const QMetaProperty &prop) {\n  if (QMetaType::UnknownType == prop.typeId())\n    return false;\n  QMetaType type = prop.metaType();\n  if (! (QMetaType::PointerToQObject & type.flags()))\n    return false;\n  return type.metaObject()->inherits(&T::staticMetaObject);\n}\n\n\n/** Base class for all configuration objects (channels, zones, contacts, etc).\n *\n * @ingroup conf */\nclass ConfigItem : public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** Parse context for config objects. During serialization, each config object gets an\n   * ID assigned. When reading the config, these IDs must be matched back to the corresponding\n   * objects. This is done using this context. */\n  class Context\n  {\n  public:\n    /** Empty constructor. */\n    Context();\n    /** Destructor. */\n    virtual ~Context();\n\n    /** Returns the read version string. */\n    const QString &version() const;\n    /** Sets the version string. */\n    void setVersion(const QString &ver);\n\n    /** Returns @c true, if the context contains the given object. */\n    virtual bool contains(ConfigObject *obj) const;\n    /** Returns @c true, if the context contains the given ID. */\n    virtual bool contains(const QString &id) const;\n\n    /** Returns ID of the given object. */\n    virtual QString getId(ConfigObject *obj) const;\n    /** Returns the object for the given ID. */\n    virtual ConfigObject *getObj(const QString &id) const;\n\n    /** Associates the given object with the given ID. */\n    virtual bool add(const QString &id, ConfigObject *);\n\n    /** Returns @c true if the property of the class has the specified tag associated. */\n    static bool tagIsSet(const QString &className, const QString &property, const QString &tag);\n    /** Returns @c true if the property of the class has the specified object as a tag associated. */\n    static bool hasTag(const QString &className, const QString &property, QVariant value);\n    /** Returns the object associated with the tag for the property of the class. */\n    static QVariant tagGetValue(const QString &className, const QString &property, const QString &tag);\n    /** Returns the tag associated with the object for the property of the class. */\n    static QString getTag(const QString &className, const QString &property, QVariant value);\n    /** Associates the given object with the tag for the property of the given class. */\n    static void setTag(const QString &className, const QString &property, const QString &tag, QVariant value);\n\n  protected:\n    /** The version string. */\n    QString _version;\n    /** ID->OBJ look-up table. */\n    QHash<QString, ConfigObject *> _objects;\n    /** OBJ->ID look-up table. */\n    QHash<ConfigObject*, QString> _ids;\n    /** Set of tag-value pairs for all properties. */\n    static QHash<QString, QList<QPair<QString, QVariant>>> _tags;\n  };\n\nprotected:\n  /** Hidden constructor.\n   * @param parent Specifies the QObject parent. */\n  explicit ConfigItem(QObject *parent = nullptr);\n\npublic:\n  /** Copies the given item into this one.\n   * @returns @c true if copying was successful and false otherwise. The two items must be of the\n   *          same type (obviously). */\n  virtual bool copy(const ConfigItem &other);\n\n  /** Clones this item. */\n  virtual ConfigItem *clone() const = 0;\n\n  /** Compares the items.\n   *\n   * This method returns 0 if the two items are equivalent and -1, 1 otherwise. The established\n   * order is somewhat arbitrary.\n   *\n   * @returns 0 if the two items are equivalent, -1 or 1 otherwise.*/\n  virtual int compare(const ConfigItem &other) const;\n\npublic:\n  /** Recursively labels the config object.\n   * Does not assign a label if the @c idBase passed to the constructor is empty. */\n  virtual bool label(Context &context, const ErrorStack &err=ErrorStack());\n  /** Recursively serializes the configuration to YAML nodes.\n   * The complete configuration must be labeled first. */\n  virtual YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\n  /** Allocates an instance for the given property on the given YAML node.\n   * This is usually done automatically based on the meta-type of the property. To be able to\n   * instantiate the item, its default constructor must be Q_INVOKABLE. */\n  virtual ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                                    const Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Parses the given YAML node, updates the given object and updates the given context (IDs). */\n  virtual bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links the given object to the rest of the codeplug using the given context. */\n  virtual bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Clears the config object. */\n  virtual void clear();\n\n  /** Returns the config, the item belongs to or @c nullptr if not part of a config. */\n  virtual const Config *config() const;\n  /** Searches the config tree to find all instances of the given type names. */\n  virtual void findItemsOfTypes(const QStringList &typeNames, QSet<ConfigItem*> &items) const;\n\n  /** Returns @c true if this object is of class @c Object. */\n  template <class Object>\n  bool is() const {\n    return nullptr != qobject_cast<const Object*>(this);\n  }\n\n  /** Casts this object to the given type. */\n  template <class Object>\n  const Object *as() const {\n    return qobject_cast<const Object*>(this);\n  }\n\n  /** Casts this object to the given type. */\n  template <class Object>\n  Object *as() {\n    return qobject_cast<Object *>(this);\n  }\n\n  /** Returns @c true if there is a class info \"description\" for this instance. */\n  bool hasDescription() const;\n  /** Returns @c true if there is a class info \"longDescription\" for this instance. */\n  bool hasLongDescription() const;\n  /** Returns @c true if there is a class info \"[PropertyName]Description\" for the given property. */\n  bool hasDescription(const QMetaProperty &prop) const;\n  /** Returns @c true if there is a class info \"[PropertyName]LongDescription\" for the given property. */\n  bool hasLongDescription(const QMetaProperty &prop) const;\n  /** Returns the description of this instance if set by a class info. */\n  QString description() const;\n  /** Returns the long description of this instance if set by a class info. */\n  QString longDescription() const;\n  /** Returns the description of property if set by a class info. */\n  QString description(const QMetaProperty &prop) const;\n  /** Returns the long description of property if set by a class info. */\n  QString longDescription(const QMetaProperty &prop) const;\n\nprotected:\n  /** Recursively serializes the configuration to YAML nodes.\n   * The complete configuration must be labeled first. */\n  virtual bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\nsignals:\n  /** Gets emitted once the config object is modified.\n   * The instance passed is the modified item, this event is passed up the config tree. */\n  void modified(ConfigItem *obj);\n  /** Gets emitted before clearing the item. */\n  void beginClear();\n  /** Gets emitted after clearing the item. */\n  void endClear();\n};\n\n\n/** Base class of all labeled and named objects.\n * @ingroup config */\nclass ConfigObject: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The name of the object. */\n  Q_PROPERTY(QString name READ name WRITE setName)\n\n  /** Specifies the prefix for every ID assigned to every object during serialization. */\n  Q_CLASSINFO(\"IdPrefix\", \"obj\")\n\nprotected:\n  /** Hidden constructor.\n   * @param parent Specifies the QObject parent. */\n  ConfigObject(QObject *parent = nullptr);\n\n  /** Hidden constructor.\n   * @param name Name of the object.\n   * @param parent Specifies the QObject parent. */\n  ConfigObject(const QString &name, QObject *parent = nullptr);\n\npublic:\n  /** Returns the name of the object. */\n  virtual const QString &name() const;\n  /** Sets the name of the object. */\n  virtual void setName(const QString &name);\n\npublic:\n  /** Returns the ID prefix for this object. */\n  QString idPrefix() const;\n  bool label(Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  virtual bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\n  /** Helper to find the @c IdPrefix class info in the class hierarchy. */\n  static QString findIdPrefix(const QMetaObject* meta);\n\nprotected:\n  /** Holds the name of the object. */\n  QString _name;\n};\n\n\n/** Base class of all device/vendor specific configuration extensions.\n * This class already implements the serialization of all @c QMetaObject\n * properties. */\nclass ConfigExtension : public ConfigItem\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit ConfigExtension(QObject *parent=nullptr);\n};\n\n\n/** Generic list class for config objects.\n * @ingroup config */\nclass AbstractConfigObjectList: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit AbstractConfigObjectList(const QMetaObject &elementTypes=ConfigObject::staticMetaObject, QObject *parent = nullptr);\n  /** Hidden constructor from initializer list. */\n  AbstractConfigObjectList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent=nullptr);\n\npublic:\n  /** Copies all elements from @c other to this list. */\n  virtual bool copy(const AbstractConfigObjectList &other);\n\n  /** Recursively labels the config object. */\n  virtual bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack()) = 0;\n  /** Recursively serializes the configuration to YAML nodes.\n   * The complete configuration must be labeled first. */\n  virtual YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Returns the number of elements in the list. */\n  virtual int count() const;\n  /** Returns the index of the given object within the list. */\n  virtual int indexOf(ConfigObject *obj) const;\n  /** Clears the list. */\n  virtual void clear();\n\n  /** Returns the config object, this list belongs to. */\n  virtual const Config *config() const;\n  /** Searches the config tree to find all instances of the given type names. */\n  virtual void findItemsOfTypes(const QStringList &typeNames, QSet<ConfigItem*> &items) const;\n  /** Searches the list for objects with the given name. */\n  virtual QList<ConfigObject *> findItemsByName(const QString name) const;\n\n  /** Returns @c true, if the list contains the given object. */\n  virtual bool has(ConfigObject *obj) const;\n  /** Returns the list element at the given index or @c nullptr if out of bounds. */\n  virtual ConfigObject *get(int idx) const;\n  /** Adds an element to the list. */\n  virtual int add(ConfigObject *obj, int row=-1, bool unique=true);\n  /** Replaces an element in the list. */\n  virtual int replace(ConfigObject *obj, int row, bool unique=true);\n  /** Removes an element from the list. */\n  virtual bool take(ConfigObject *obj);\n  /** Removes an element from the list (and deletes it if owned). */\n  virtual bool del(ConfigObject *obj);\n\n  /** Moves an object at index @c idx one step up. */\n  virtual bool moveUp(int idx);\n  /** Moves objects at [first, last] one step up. */\n  virtual bool moveUp(int first, int last);\n  /** Moves an object at index @c idx one step down. */\n  virtual bool moveDown(int idx);\n  /** Moves objects [first, last] one step down. */\n  virtual bool moveDown(int first, int last);\n  /** Moves the given source range to the destination index.\n   * The destination index is given before the movement. That is, if elements 0 & 1 are moved to\n   * indices 1 & 2, call @c move(0,2, 2) */\n  virtual bool move(int source, int count, int destination);\n\n  /** Returns the element type for this list. */\n  const QList<QMetaObject> &elementTypes() const;\n  /** Returns a list of all class names. */\n  QStringList classNames() const;\n\nsignals:\n  /** Gets emitted if an element was added to the list. */\n  void elementAdded(int idx);\n  /** Gets emitted if one of the lists elements gets modified. */\n  void elementModified(int idx);\n  /** Gets emitted if one of the lists elements gets deleted. */\n  void elementRemoved(int idx);\n\nprivate slots:\n  /** Internal used callback to handle modified elements. */\n  void onElementModified(ConfigItem *obj);\n  /** Internal used callback to handle deleted elements. */\n  void onElementDeleted(QObject *obj);\n\nprotected:\n  /** Holds the static QMetaObject of the element type. */\n  QList<QMetaObject> _elementTypes;\n  /** Holds the list items. */\n  QVector<ConfigObject *> _items;\n};\n\n\n/** List class for config objects.\n * This list owns the config objects it contains. See @c ConfigObjectRefList for a list of weak\n * references.\n * @ingroup config */\nclass ConfigObjectList: public AbstractConfigObjectList\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit ConfigObjectList(const QMetaObject &elementTypes=ConfigItem::staticMetaObject, QObject *parent = nullptr);\n  /** Hidden constructor from initializer list. */\n  ConfigObjectList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent=nullptr);\n\npublic:\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n  bool take(ConfigObject *obj);\n  bool del(ConfigObject *obj);\n  void clear();\n  bool copy(const AbstractConfigObjectList &other);\n\n  /** Compares the object lists.\n   *\n   * This method returns 0 if the two lists are equivalent and -1, 1 otherwise. The established\n   * order is somewhat arbitrary.\n   *\n   * @returns 0 if the two lists are equivalent, -1 or 1 otherwise.*/\n  virtual int compare(const ConfigObjectList &other) const;\n\n  /** Allocates a member objects for the given YAML node. */\n  virtual ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Parses the list from the YAML node. */\n  virtual bool parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links the list from the given YAML node. */\n  virtual bool link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n\n  bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack());\n  YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack());\n};\n\n\n/** List class for config objects.\n * This list only references the config objects, see @c ConfigObjectList for a list that owns the\n * config objects.\n * @ingroup config */\nclass ConfigObjectRefList: public AbstractConfigObjectList\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit ConfigObjectRefList(const QMetaObject &elementTypes=ConfigObject::staticMetaObject, QObject *parent = nullptr);\n  /** Hidden constructor from initializer list. */\n  ConfigObjectRefList(const std::initializer_list<QMetaObject> &elementTypes, QObject *parent=nullptr);\n\npublic:\n  bool label(ConfigItem::Context &context, const ErrorStack &err=ErrorStack());\n  YAML::Node serialize(const ConfigItem::Context &context, const ErrorStack &err=ErrorStack());\n\n  /** Compares the object ref lists.\n   *\n   * This method returns 0 if the two lists are equivalent and -1, 1 otherwise. The established\n   * order is somewhat arbitrary.\n   *\n   * @returns 0 if the two lists are equivalent, -1 or 1 otherwise.*/\n  virtual int compare(const ConfigObjectRefList &other) const;\n};\n\n\n#endif // CONFIGOBJECT_HH\n"
  },
  {
    "path": "lib/configreference.cc",
    "content": "#include \"configreference.hh\"\n#include \"logger.hh\"\n#include \"contact.hh\"\n#include \"channel.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"radioid.hh\"\n#include \"rxgrouplist.hh\"\n#include \"roamingzone.hh\"\n#include \"zone.hh\"\n#include \"encryptionextension.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of ConfigObjectReference\n * ********************************************************************************************* */\nConfigObjectReference::ConfigObjectReference(const QMetaObject &elementType, QObject *parent)\n  : QObject(parent), _elementTypes(), _object(nullptr)\n{\n  _elementTypes.append(elementType.className());\n}\n\nbool\nConfigObjectReference::isNull() const {\n  return nullptr == _object;\n}\n\nvoid\nConfigObjectReference::clear() {\n  if (_object) {\n    disconnect(_object, SIGNAL(destroyed(QObject*)), this, SLOT(onReferenceDeleted(QObject*)));\n    emit modified();\n  }\n  _object = nullptr;\n}\n\nbool\nConfigObjectReference::set(ConfigObject *object) {\n  if (_object)\n    disconnect(_object, SIGNAL(destroyed(QObject*)), this, SLOT(onReferenceDeleted(QObject*)));\n\n  if (nullptr == object) {\n    _object = nullptr;\n    return true;\n  }\n\n  // Check type\n  bool typeCheck = false;\n  foreach (const QString &cname, _elementTypes) {\n    if (object->inherits(cname.toLatin1().constData())) {\n      typeCheck = true;\n      break;\n    }\n  }\n  if (! typeCheck) {\n    logError() << \"Cannot reference element of type \" << object->metaObject()->className()\n               << \", expected instance of \" << _elementTypes.join(\", \");\n    return false;\n  }\n\n  _object = object;\n  if (_object)\n    connect(_object, &QObject::destroyed,\n            this, &ConfigObjectReference::onReferenceDeleted, Qt::DirectConnection);\n\n  emit modified();\n  return true;\n}\n\nbool\nConfigObjectReference::copy(const ConfigObjectReference *ref) {\n  clear();\n  if (nullptr == ref)\n    return true;\n  return set(ref->_object);\n}\n\nint\nConfigObjectReference::compare(const ConfigObjectReference &other) const {\n  if (isNull() && other.isNull())\n    return 0;\n  if (!isNull() && other.isNull())\n    return 1;\n  if (isNull() && !other.isNull())\n    return -1;\n  return this->_object->compare(*other._object);\n}\n\nbool\nConfigObjectReference::allow(const QMetaObject *elementType) {\n  if (! _elementTypes.contains(elementType->className()))\n    _elementTypes.append(elementType->className());\n  return true;\n}\n\nconst QStringList &\nConfigObjectReference::elementTypeNames() const {\n  return _elementTypes;\n}\n\nvoid\nConfigObjectReference::onReferenceDeleted(QObject *obj) {\n  // Check if destroyed obj is referenced one.\n  if (_object != reinterpret_cast<ConfigObject*>(obj))\n    return;\n  // If it is\n  _object = nullptr;\n  emit modified();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ContactReference\n * ********************************************************************************************* */\nContactReference::ContactReference(const QMetaObject &elementType, QObject *parent)\n  : ConfigObjectReference(elementType, parent)\n{\n  // pass...\n}\n\nContactReference::ContactReference(QObject *parent)\n  : ConfigObjectReference(Contact::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRContactReference\n * ********************************************************************************************* */\nDMRContactReference::DMRContactReference(QObject *parent)\n  : ContactReference(DMRContact::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRContactReference\n * ********************************************************************************************* */\nM17ContactReference::M17ContactReference(QObject *parent)\n  : ContactReference(M17Contact::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRContactRefList\n * ********************************************************************************************* */\nDMRContactRefList::DMRContactRefList(QObject *parent)\n  : ConfigObjectRefList(DMRContact::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelReference\n * ********************************************************************************************* */\nChannelReference::ChannelReference(const QMetaObject &elementType, QObject *parent)\n  : ConfigObjectReference(elementType, parent)\n{\n  // pass...\n}\n\nChannelReference::ChannelReference(QObject *parent)\n  : ConfigObjectReference(Channel::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRChannelReference\n * ********************************************************************************************* */\nDMRChannelReference::DMRChannelReference(QObject *parent)\n  : ChannelReference(DMRChannel::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of FMChannelReference\n * ********************************************************************************************* */\nFMChannelReference::FMChannelReference(QObject *parent)\n  : ChannelReference(FMChannel::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelRefList\n * ********************************************************************************************* */\nChannelRefList::ChannelRefList(const QMetaObject &elementType, QObject *parent)\n  : ConfigObjectRefList(elementType, parent)\n{\n  // pass...\n}\n\nChannelRefList::ChannelRefList(QObject *parent)\n  : ConfigObjectRefList(Channel::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRChannelRefList\n * ********************************************************************************************* */\nDMRChannelRefList::DMRChannelRefList(QObject *parent)\n  : ChannelRefList(DMRChannel::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingChannelRefList\n * ********************************************************************************************* */\nRoamingChannelRefList::RoamingChannelRefList(QObject *parent)\n  : ConfigObjectRefList({RoamingChannel::staticMetaObject}, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ScanListReference\n * ********************************************************************************************* */\nScanListReference::ScanListReference(QObject *parent)\n  : ConfigObjectReference(ScanList::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of PositioningSystemReference\n * ********************************************************************************************* */\nPositioningSystemReference::PositioningSystemReference(const QMetaObject &elementType, QObject *parent)\n  : ConfigObjectReference(elementType, parent)\n{\n  // pass...\n}\n\nPositioningSystemReference::PositioningSystemReference(QObject *parent)\n  : ConfigObjectReference(PositionReportingSystem::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of FMAPRSSystemReference\n * ********************************************************************************************* */\nFMAPRSSystemReference::FMAPRSSystemReference(QObject *parent)\n  : PositioningSystemReference(FMAPRSSystem::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRAPRSSystemReference\n * ********************************************************************************************* */\nDMRAPRSSystemReference::DMRAPRSSystemReference(QObject *parent)\n  : PositioningSystemReference(FMAPRSSystem::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioIDReference\n * ********************************************************************************************* */\nDMRRadioIDReference::DMRRadioIDReference(QObject *parent)\n  : ConfigObjectReference(DMRRadioID::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GroupListReference\n * ********************************************************************************************* */\nGroupListReference::GroupListReference(QObject *parent)\n  : ConfigObjectReference(RXGroupList::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingZoneReference\n * ********************************************************************************************* */\nRoamingZoneReference::RoamingZoneReference(QObject *parent)\n  : ConfigObjectReference(RoamingZone::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of EncryptionKeyReference\n * ********************************************************************************************* */\nEncryptionKeyReference::EncryptionKeyReference(QObject *parent)\n  : ConfigObjectReference(EncryptionKey::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneReference\n * ********************************************************************************************* */\nZoneReference::ZoneReference(QObject *parent)\n  : ConfigObjectReference(Zone::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\n"
  },
  {
    "path": "lib/configreference.hh",
    "content": "#ifndef CONFIGREFERENCE_HH\n#define CONFIGREFERENCE_HH\n\n#include \"configobject.hh\"\n#include <QSet>\n\nclass Channel;\nclass DMRChannel;\nclass FMChannel;\nclass ScanList;\nclass EncryptionKey;\n\n/** Implements a reference to a config object.\n * This class is only used to implement the automatic generation/parsing of the YAML codeplug files.\n * @ingroup conf */\nclass ConfigObjectReference: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  ConfigObjectReference(const QMetaObject &elementType=ConfigObject::staticMetaObject, QObject *parent = nullptr);\n\npublic:\n  /** Returns @c true if the reference is null.\n   * That is, if there is no object referenced. */\n  bool isNull() const;\n\n  /** Resets the reference.\n   * Same as @c set(nullptr). */\n  virtual void clear();\n  /** Sets the reference.\n   * If set to @c nullptr, the reference gets cleared. */\n  virtual bool set(ConfigObject *object);\n  /** Copies the reference from another reference. */\n  virtual bool copy(const ConfigObjectReference *ref);\n  /** Adds a possible type to this reference. */\n  virtual bool allow(const QMetaObject *elementType);\n\n  /** Returns the type names of allowed objects. */\n  const QStringList &elementTypeNames() const;\n\n  /** Returns the reference as the specified type. */\n  template <class Type>\n  Type *as() const {\n    if (nullptr == _object)\n      return nullptr;\n    return _object->as<Type>();\n  }\n\n  /** Returns @c true if the reference is of the specified type. */\n  template <class Type>\n  bool is() const {\n    if (nullptr == _object)\n      return false;\n    return _object->is<Type>();\n  }\n\n  /** Compares the references. */\n  int compare(const ConfigObjectReference &other) const;\n\nsignals:\n  /** Gets emitted if the reference is changed.\n   * This signal is not emitted if the referenced object is modified. */\n  void modified();\n\nprotected slots:\n  /** Internal call back whenever the referenced object gets deleted. */\n  void onReferenceDeleted(QObject *obj);\n\nprotected:\n  /** Holds the static QMetaObject of the possible element types. */\n  QStringList _elementTypes;\n  /** The reference to the object. */\n  ConfigObject *_object;\n};\n\n\n/** Represents a reference to a contact.\n * This class is only used to automate the parsing and generation of the YAML codeplug file.\n * @ingroup config */\nclass ContactReference: public ConfigObjectReference\n{\n  Q_OBJECT\n\nprotected:\n  /** Constructor. */\n  ContactReference(const QMetaObject &elementType, QObject *parent = nullptr);\n\npublic:\n  /** Constructor. */\n  explicit ContactReference(QObject *parent=nullptr);\n};\n\n\n/** Represents a reference to a DMR contact.\n * @ingroup conf*/\nclass DMRContactReference: public ContactReference\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRContactReference(QObject *parent=nullptr);\n};\n\n\n/** Represents a reference to a M17 contact.\n * @ingroup conf*/\nclass M17ContactReference: public ContactReference\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit M17ContactReference(QObject *parent=nullptr);\n};\n\n\n/** List of references to DMR contacts. */\nclass DMRContactRefList: public ConfigObjectRefList\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRContactRefList(QObject *parent=nullptr);\n};\n\n\n/** Represents a reference to a channel.\n * This class is only used to automate the parsing and generation of the YAML codeplug file.\n * @ingroup config */\nclass ChannelReference: public ConfigObjectReference\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  ChannelReference(const QMetaObject &elementType, QObject *parent = nullptr);\n\npublic:\n  /** Constructor. */\n  explicit ChannelReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a DMR channel.\n * @ingroup conf */\nclass DMRChannelReference: public ChannelReference\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRChannelReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a FM channel.\n * @ingroup conf */\nclass FMChannelReference: public ChannelReference\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit FMChannelReference(QObject *parent=nullptr);\n};\n\n\n/** Represents a list of weak references to channels (analog and digital).\n * @ingroup config */\nclass ChannelRefList: public ConfigObjectRefList\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit ChannelRefList(const QMetaObject &elementTypes, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit ChannelRefList(QObject *parent=nullptr);\n};\n\n\n/** Represents a list of references to some DMR channels.\n * @ingroup config */\nclass DMRChannelRefList: public ChannelRefList\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit DMRChannelRefList(QObject *parent=nullptr);\n};\n\n\n/** Represents a list of references to some roaming channels.\n * @ingroup config */\nclass RoamingChannelRefList: public ConfigObjectRefList\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RoamingChannelRefList(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a scan list.\n * @ingroup conf */\nclass ScanListReference: public ConfigObjectReference\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit ScanListReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a positioning system.\n * @ingroup conf */\nclass PositioningSystemReference: public ConfigObjectReference {\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  PositioningSystemReference(const QMetaObject &elementType, QObject *parent = nullptr);\n\npublic:\n  /** Constructor. */\n  explicit PositioningSystemReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to an APRS system.\n * @ingroup conf */\nclass FMAPRSSystemReference: public PositioningSystemReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit FMAPRSSystemReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a GPS system.\n * @ingroup conf */\nclass DMRAPRSSystemReference: public PositioningSystemReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRAPRSSystemReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a radio ID.\n * @ingroup conf */\nclass DMRRadioIDReference: public ConfigObjectReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRRadioIDReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a group list.\n * @ingroup conf */\nclass GroupListReference: public ConfigObjectReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit GroupListReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a roaming zone.\n * @ingroup conf */\nclass RoamingZoneReference: public ConfigObjectReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RoamingZoneReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to an encryption key.\n * @ingroup conf */\nclass EncryptionKeyReference: public ConfigObjectReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit EncryptionKeyReference(QObject *parent=nullptr);\n};\n\n\n/** Implements a reference to a zone.\n * @ingroup conf */\nclass ZoneReference: public ConfigObjectReference {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit ZoneReference(QObject *parent=nullptr);\n};\n\n\n#endif // CONFIGREFERENCE_HH\n"
  },
  {
    "path": "lib/contact.cc",
    "content": "#include \"contact.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include \"opengd77_extension.hh\"\n#include <QRegularExpression>\n\n\n/* ********************************************************************************************* *\n * Implementation of Contact\n * ********************************************************************************************* */\nContact::Contact(QObject *parent)\n  : ConfigObject(parent), _ring(false)\n{\n  // pass...\n}\n\nContact::Contact(const QString &name, bool rxTone, QObject *parent)\n  : ConfigObject(name, parent), _ring(rxTone)\n{\n  // pass...\n}\n\nvoid\nContact::clear() {\n  ConfigObject::clear();\n  _ring = false;\n}\n\nbool\nContact::ring() const {\n  return _ring;\n}\nvoid\nContact::setRing(bool enable) {\n  _ring = enable;\n  emit modified(this);\n}\n\nbool\nContact::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse contact: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node cnt = node.begin()->second;\n  return ConfigObject::parse(cnt, ctx, err);\n}\n\nbool\nContact::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  return ConfigObject::link(node.begin()->second, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AnalogContact\n * ********************************************************************************************* */\nAnalogContact::AnalogContact(QObject *parent)\n  : Contact(parent)\n{\n  // pass...\n}\n\nAnalogContact::AnalogContact(const QString &name, bool ring, QObject *parent)\n  : Contact(name, ring, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DTMFContact\n * ********************************************************************************************* */\nDTMFContact::DTMFContact(QObject *parent)\n  : AnalogContact(parent), _number()\n{\n  // pass...\n}\n\nDTMFContact::DTMFContact(const QString &name, const QString &number, bool rxTone, QObject *parent)\n  : AnalogContact(name, rxTone, parent), _number(number.simplified())\n{\n  // pass...\n}\n\nConfigItem *\nDTMFContact::clone() const {\n  DTMFContact *c = new DTMFContact();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nvoid\nDTMFContact::clear() {\n  Contact::clear();\n  _number.clear();\n}\n\nconst QString &\nDTMFContact::number() const {\n  return _number;\n}\n\nbool\nDTMFContact::setNumber(const QString &number) {\n  if (! validDTMFNumber(number))\n    return false;\n  _number = number.simplified();\n  emit modified(this);\n  return true;\n}\n\nYAML::Node\nDTMFContact::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = AnalogContact::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  YAML::Node type; type[\"dtmf\"] = node;\n  return type;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DigitalContact\n * ********************************************************************************************* */\nDigitalContact::DigitalContact(QObject *parent)\n  : Contact(parent)\n{\n  // pass...\n}\n\nDigitalContact::DigitalContact(const QString &name, bool ring, QObject *parent)\n  : Contact(name, ring, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRContact\n * ********************************************************************************************* */\nDMRContact::DMRContact(QObject *parent)\n  : DigitalContact(parent), _type(PrivateCall), _number(0), _anytone(nullptr), _openGD77(nullptr)\n{\n  // pass...\n}\n\nDMRContact::DMRContact(Type type, const QString &name, unsigned number, bool rxTone, QObject *parent)\n  : DigitalContact(name, rxTone, parent), _type(type), _number(number), _anytone(nullptr), _openGD77(nullptr)\n{\n  // pass...\n}\n\nConfigItem *\nDMRContact::clone() const {\n  DMRContact *c = new DMRContact();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nvoid\nDMRContact::clear() {\n  DigitalContact::clear();\n  _type = PrivateCall;\n  _number = 0;\n  if (_openGD77)\n    _openGD77->deleteLater();\n  _openGD77 = nullptr;\n  if (_anytone)\n    _anytone->deleteLater();\n  _anytone = nullptr;\n}\n\nDMRContact::Type\nDMRContact::type() const {\n  return _type;\n}\n\nvoid\nDMRContact::setType(DMRContact::Type type) {\n  _type = type;\n}\n\nunsigned\nDMRContact::number() const {\n  return _number;\n}\n\nbool\nDMRContact::setNumber(unsigned number) {\n  _number = number;\n  emit modified(this);\n  return true;\n}\n\nYAML::Node\nDMRContact::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = DigitalContact::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  YAML::Node type; type[\"dmr\"] = node;\n  return type;\n}\n\n\nOpenGD77ContactExtension *\nDMRContact::openGD77ContactExtension() const {\n  return _openGD77;\n}\n\nvoid\nDMRContact::setOpenGD77ContactExtension(OpenGD77ContactExtension *ext) {\n  if (_openGD77)\n    _openGD77->deleteLater();\n  _openGD77 = ext;\n  if (_openGD77) {\n    _openGD77->setParent(this);\n    connect(_openGD77, &OpenGD77ContactExtension::modified,\n            [this](ConfigItem*){ emit modified(this); });\n  }\n}\n\nAnytoneContactExtension *\nDMRContact::anytoneExtension() const {\n  return _anytone;\n}\n\nvoid\nDMRContact::setAnytoneExtension(AnytoneContactExtension *ext) {\n  if (_anytone)\n    _anytone->deleteLater();\n  _anytone = ext;\n  if (_anytone) {\n    _anytone->setParent(this);\n    connect(_anytone, &OpenGD77ContactExtension::modified,\n            [this](ConfigItem*){ emit modified(this); });\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of M17Contact\n * ********************************************************************************************* */\nM17Contact::M17Contact(QObject *parent)\n  : DigitalContact(parent), _isBroadcast(false), _call()\n{\n  // pass...\n}\n\nM17Contact::M17Contact(const QString &name, bool ring, const QString &call, QObject *parent)\n  : DigitalContact(name, ring, parent), _isBroadcast(false), _call()\n{\n  _call = normalizeCall(call);\n}\n\nConfigItem *\nM17Contact::clone() const {\n  M17Contact *c = new M17Contact();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nvoid\nM17Contact::clear() {\n  DigitalContact::clear();\n  _isBroadcast = false;\n  _call.clear();\n}\n\nconst QString &\nM17Contact::call() const {\n  return _call;\n}\n\nvoid\nM17Contact::setCall(const QString &call) {\n  _call = normalizeCall(call);\n}\n\nbool\nM17Contact::isBroadcast() const {\n  return _isBroadcast;\n}\n\nvoid\nM17Contact::setBroadcast(bool enable) {\n  _isBroadcast = enable;\n}\n\nYAML::Node\nM17Contact::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = DigitalContact::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  YAML::Node type; type[\"m17\"] = node;\n  return type;\n}\n\nQString\nM17Contact::normalizeCall(const QString call) {\n  QString tmp = call.toUpper();\n  tmp.remove(QRegularExpression(\"[^A-Z0-9/\\\\.\\\\-]\"));\n  return tmp.mid(0, 9);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ContactList\n * ********************************************************************************************* */\nContactList::ContactList(QObject *parent)\n  : ConfigObjectList(Contact::staticMetaObject, parent)\n{\n  // pass...\n}\n\nint\nContactList::add(ConfigObject *obj, int row, bool unique) {\n  if ((nullptr == obj) || (! obj->is<Contact>()))\n    return -1;\n  return ConfigObjectList::add(obj, row, unique);\n}\n\nContact *\nContactList::contact(int idx) const {\n  if ((0>idx) || (idx >= count()))\n    return nullptr;\n  return _items[idx]->as<Contact>();\n}\n\n\nConfigItem *\nContactList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node)\n    return nullptr;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create contact: Expected object with one child.\";\n    return nullptr;\n  }\n\n  QString type = QString::fromStdString(node.begin()->first.as<std::string>());\n  if (\"dmr\" == type) {\n    return new DMRContact();\n  } else if (\"m17\" == type) {\n    return new M17Contact();\n  } else if (\"dtmf\" == type) {\n    return new DTMFContact();\n  }\n\n  errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n              << \": Cannot create contact: Unknown type '\" << type << \"'.\";\n\n  return nullptr;\n}\n"
  },
  {
    "path": "lib/contact.hh",
    "content": "#ifndef CONTACT_HH\n#define CONTACT_HH\n\n#include \"configobject.hh\"\n#include <QVector>\n#include <QAbstractTableModel>\n\n#include \"anytone_extension.hh\"\n#include \"opengd77_extension.hh\"\n\nclass Config;\n\n\n/** Represents the base-class for all contact types, analog (DTMF) or digital (DMR, M17).\n *\n * @ingroup conf */\nclass Contact: public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"cont\")\n\n  /** If @c true and supported by radio, ring on call from this contact. */\n  Q_PROPERTY(bool ring READ ring WRITE setRing)\n\nprotected:\n  /** Default constructor. */\n  explicit Contact(QObject *parent=nullptr);\n  /** Hidden constructor.\n   * @param name   Specifies the name of the contact.\n   * @param ring Specifies whether a ring-tone for this contact is used.\n   * @param parent Specifies the QObject parent. */\n  Contact(const QString &name, bool ring=true, QObject *parent=nullptr);\n\npublic:\n  void clear();\n\npublic:\n  /** Returns @c true if the ring-tone is enabled for this contact. */\n  bool ring() const;\n  /** Enables/disables the ring-tone for this contact. */\n  void setRing(bool enable);\n\n  /** Typecheck contact.\n   * For example, @c contact->is<DigitalContact>() returns @c true if @c contact is a\n   * @c DigitalContact. */\n\ttemplate <class T>\n\tbool is() const {\n\t\treturn 0 != dynamic_cast<const T *>(this);\n\t}\n  /** Typecast contact. */\n\ttemplate<class T>\n\tT *as() {\n\t\treturn dynamic_cast<T *>(this);\n\t}\n  /** Typecast contact. */\n\ttemplate<class T>\n\tconst T *as() const {\n\t\treturn dynamic_cast<const T *>(this);\n\t}\n\npublic:\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Ringtone enabled? */\n  bool _ring;\n};\n\n\n/** Base class for all analog contacts.\n * @ingroup conf */\nclass AnalogContact: public Contact\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit AnalogContact(QObject *parent=nullptr);\n\n  /** Constructor. */\n  AnalogContact(const QString &name, bool rxTone, QObject *parent=nullptr);\n};\n\n\n/** Represents an analog contact, that is a DTMF number.\n * @ingroup conf */\nclass DTMFContact: public AnalogContact\n{\n\tQ_OBJECT\n\n  /** The contact number. */\n  Q_PROPERTY(QString number READ number WRITE setNumber)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit DTMFContact(QObject *parent=nullptr);\n  /** Constructs a DTMF (analog) contact.\n   * @param name   Specifies the contact name.\n   * @param number Specifies the DTMF number (0-9,A,B,C,D,*,#).\n   * @param ring Specifies whether the ring-tone is enabled for this contact.\n   * @param parent Specifies the QObject parent. */\n  DTMFContact(const QString &name, const QString &number, bool ring=false, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the DTMF number of this contact.\n   * The number must consist of 0-9, a-f, * or #. */\n\tconst QString &number() const;\n  /** (Re-)Sets the DTMF number of this contact. */\n\tbool setNumber(const QString &number);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** The DTMF number. */\n\tQString _number;\n};\n\n\n/** Base class for all digital contacts.\n * @ingroup conf */\nclass DigitalContact: public Contact\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit DigitalContact(QObject *parent=nullptr);\n\n  /** Hidden constructor. */\n  DigitalContact(const QString &name, bool ring, QObject *parent=nullptr);\n};\n\n\n/** Represents a digital contact, that is a DMR number.\n * @ingroup conf */\nclass DMRContact: public DigitalContact\n{\n\tQ_OBJECT\n\n  /** The type of the contact. */\n  Q_PROPERTY(Type type READ type WRITE setType)\n  /** The number of the contact. */\n  Q_PROPERTY(unsigned number READ number WRITE setNumber)\n  /** The AnyTone extension to the digital contact. */\n  Q_PROPERTY(AnytoneContactExtension* anytone READ anytoneExtension WRITE setAnytoneExtension)\n  /** The OpenGD77 extension to the digital contact. */\n  Q_PROPERTY(OpenGD77ContactExtension* openGD77 READ openGD77ContactExtension WRITE setOpenGD77ContactExtension)\n\npublic:\n  /** Possible call types for a contact. */\n\ttypedef enum {\n\t\tPrivateCall,   ///< A private call.\n    GroupCall,     ///< A group call.\n    AllCall        ///< An all-call.\n\t} Type;\n  Q_ENUM(Type)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit DMRContact(QObject *parent=nullptr);\n\n  /** Constructs a DMR (digital) contact.\n   * @param type   Specifies the call type (private, group, all-call).\n   * @param name   Specifies the contact name.\n   * @param number Specifies the DMR number for this contact.\n   * @param ring Specifies whether the ring-tone is enabled for this contact.\n   * @param parent Specifies the QObject parent. */\n  DMRContact(Type type, const QString &name, unsigned number, bool ring=false, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the call-type. */\n\tType type() const;\n  /** (Re-)Sets the call-type. */\n  void setType(Type type);\n  /** Returns the DMR number. */\n\tunsigned number() const;\n  /** (Re-)Sets the DMR number of the contact. */\n\tbool setNumber(unsigned number);\n\n  /** Returns the OpenGD77 extension, or @c nullptr if not set. */\n  OpenGD77ContactExtension *openGD77ContactExtension() const;\n  /** Sets the OpenGD77 extension. */\n  void setOpenGD77ContactExtension(OpenGD77ContactExtension *ext);\n\n  /** Returns the AnyTone extension, or @c nullptr if not set. */\n  AnytoneContactExtension *anytoneExtension() const;\n  /** Sets the AnyTone extension. */\n  void setAnytoneExtension(AnytoneContactExtension *ext);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** The call type. */\n\tType _type;\n  /** The DMR number of the contact. */\n\tunsigned _number;\n  /** Owns the AnytoneContactextension to the digital contacts. */\n  AnytoneContactExtension *_anytone;\n  /** Owns the OpenGD77 extensions to the digital contacts. */\n  OpenGD77ContactExtension *_openGD77;\n};\n\n\n/** Represents an M17 contact.\n * @ingroup conf */\nclass M17Contact: public DigitalContact\n{\n  Q_OBJECT\n\n  /** The call of the contact. */\n  Q_PROPERTY(QString call READ call WRITE setCall)\n  /** If @c true, the contact is the M17 broadcast destination. */\n  Q_PROPERTY(bool isBroadcast READ isBroadcast WRITE setBroadcast)\n\npublic:\n  /** Empty constructor. */\n  explicit M17Contact(QObject *parent=nullptr);\n  /** Constructor.\n   * @param name Specifies the name of the contact. Can be descriptive.\n   * @param ring Specifies if a call from this contact rings a bell.\n   * @param call Specifies the call/ID of the contact.\n   * @param parent QObject parent, takes the ownership of this object. */\n  M17Contact(const QString &name, bool ring, const QString &call, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the call/ID of the contact. */\n  const QString &call() const;\n  /** Sets the call/ID of the contact. */\n  void setCall(const QString &call);\n  /** Retunrs @c true, if the contact is the M17 broadcast contact. If set, the call has no effect. */\n  bool isBroadcast() const;\n  /** Sets/Resets the broadcast flag. */\n  void setBroadcast(bool enable);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\n  /** Helper function to normalize a call. */\n  static QString normalizeCall(const QString call);\n\nprotected:\n  /** If true, the contact is the M17 broadcast contact. The call is ignored then. */\n  bool _isBroadcast;\n  /** Holds the normalized call/ID of the contact. */\n  QString _call;\n};\n\n\n/** Represents the list of contacts within the abstract radio configuration.\n *\n * A special feature of this list, is that DTMF and digital contacts can be accessed by their own\n * unique index although they are held within this single list. Most radios manage digital and\n * DTMF contacts in separate lists, hence a means to iterate over and get indices of digital and\n * DTMF contacts only is needed.\n *\n * This class implements the @c QAbstractTableModel, such that the list can be shown with the\n * @c QTableView widget.\n *\n * @ingroup conf */\nclass ContactList: public ConfigObjectList\n{\n\tQ_OBJECT\n\npublic:\n  /** Constructs an empty contact list. */\n\texplicit ContactList(QObject *parent=nullptr);\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\n  /** Returns the number of digital contacts. */\n  [[deprecated(\"Use indexing instead.\")]] int digitalCount() const;\n  /** Returns the number of DTMF contacts. */\n  [[deprecated(\"Use indexing instead.\")]] int dtmfCount() const;\n\n  /** Returns the contact at index @c idx. */\n  Contact *contact(int idx) const;\n  /** Returns the digital contact at index @c idx among digital contacts. */\n  [[deprecated(\"Use indexing instead.\")]] DMRContact *digitalContact(int idx) const;\n  /** Returns the DTMF contact at index @c idx among DTMF contacts. */\n  [[deprecated(\"Use indexing instead.\")]] DTMFContact *dtmfContact(int idx) const;\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n#endif // CONTACT_HH\n"
  },
  {
    "path": "lib/crc32.cc",
    "content": "#include \"crc32.hh\"\n\nstatic const uint32_t _crc_table[256] = {\n  /* CRC polynomial 0xedb88320 */\n  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,\n  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,\n  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,\n  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,\n  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,\n  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,\n  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,\n  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,\n  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,\n  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,\n  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,\n  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,\n  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,\n  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,\n  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,\n  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,\n  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,\n  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,\n  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,\n  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,\n  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,\n  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d\n};\n\n\nCRC32::CRC32()\n  : _crc(0xFFFFFFFF)\n{\n\t// pass...\n}\n\nvoid\nCRC32::update(uint8_t c) {\n  _crc = ( _crc_table[(_crc ^ c) & 0xFF] ^ (_crc >> 8) );\n}\n\nvoid\nCRC32::update(const uint8_t *buf, size_t n) {\n\tfor (size_t i=0; i<n; i++)\n    _crc = ( _crc_table[(_crc ^ buf[i]) & 0xFF] ^ (_crc >> 8) );\n}\n\nvoid\nCRC32::update(const QByteArray &buf) {\n\tupdate((const uint8_t *)buf.constData(), buf.size());\n}\n\n"
  },
  {
    "path": "lib/crc32.hh",
    "content": "#ifndef CRC32_HH\n#define CRC32_HH\n\n#include <QByteArray>\n\n/** Implements the CRC32 checksum as used in DFU files.\n *\n * @ingroup util */\nclass CRC32\n{\npublic:\n  /** Default constructor. */\n\tCRC32();\n  /** Update CRC with given byte. */\n\tvoid update(uint8_t c);\n  /** Update CRC with given data. */\n\tvoid update(const uint8_t *c, size_t n);\n  /** Update CRC with given data. */\n\tvoid update(const QByteArray &data);\n  /** Returns the current CRC. */\n  inline uint32_t get() { return _crc; }\n\nprotected:\n  /** Current CRC. */\n\tuint32_t _crc;\n};\n\n#endif // CRC32_HH\n"
  },
  {
    "path": "lib/csvreader.cc",
    "content": "#include \"csvreader.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n\n#include <QRegularExpression>\n#include <QDebug>\n\nQVector< QPair<QRegularExpression, CSVLexer::Token::TokenType> > CSVLexer::_pattern = {\n  { QRegularExpression(\"^n([0-9]{3})\"),                     CSVLexer::Token::T_DCS_N },\n  { QRegularExpression(\"^i([0-9]{3})\"),                     CSVLexer::Token::T_DCS_I },\n  { QRegularExpression(\"^([a-zA-Z0-9]{1,6}-[0-9]{1,2})\"),   CSVLexer::Token::T_APRSCALL },\n  { QRegularExpression(\"^([a-zA-Z_][a-zA-Z0-9_]*)\"),        CSVLexer::Token::T_KEYWORD },\n  { QRegularExpression(\"^\\\"([^\\\"\\r\\n]*)\\\"\"),                CSVLexer::Token::T_STRING },\n  { QRegularExpression(\"^([+-]?[0-9]+(\\\\.[0-9]*)?)\"),       CSVLexer::Token::T_NUMBER },\n  { QRegularExpression(\"^(:)\"),                             CSVLexer::Token::T_COLON },\n  { QRegularExpression(\"^(-)\"),                             CSVLexer::Token::T_NOT_SET },\n  { QRegularExpression(\"^(\\\\+)\"),                           CSVLexer::Token::T_ENABLED },\n  { QRegularExpression(\"^(,)\"),                             CSVLexer::Token::T_COMMA },\n  { QRegularExpression(\"^([ \\t]+)\"),                        CSVLexer::Token::T_WHITESPACE },\n  { QRegularExpression(\"^(\\r?\\n)\"),                         CSVLexer::Token::T_NEWLINE},\n  { QRegularExpression(\"^(#[^\\n\\r]*)\"),                     CSVLexer::Token::T_COMMENT},\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of CSVLexer\n * ********************************************************************************************* */\nCSVLexer::CSVLexer(QTextStream &stream, QObject *parent)\n  : QObject(parent), _errorMessage(), _stream(stream), _stack(), _currentLine()\n{\n  _stream.seek(0);\n  _stack.reserve(10);\n  _stack.push_back({0, 1, 1});\n  _currentLine = _stream.readLine();\n}\n\nconst QString &\nCSVLexer::errorMessage() const {\n  return _errorMessage;\n}\n\nCSVLexer::Token\nCSVLexer::next() {\n  Token token = lex();\n  while ((Token::T_WHITESPACE == token.type) || (Token::T_COMMENT == token.type))\n    token = lex();\n  return token;\n}\n\nCSVLexer::Token\nCSVLexer::lex() {\n  if (_currentLine.isEmpty() && _stream.atEnd()) {\n    return {Token::T_END_OF_STREAM, \"\", _stack.back().line, _stack.back().column };\n  } else if (_currentLine.isEmpty()) {\n    Token token = {Token::T_NEWLINE, \"\", _stack.back().line, _stack.back().column };\n    _stack.back().offset = _stream.pos();\n    _currentLine = _stream.readLine();\n    _stack.back().line++;\n    _stack.back().column = 1;\n    return token;\n  }\n  foreach (auto pattern, _pattern) {\n    auto match = pattern.first.match(_currentLine);\n    if (0 == match.capturedStart()) {\n      Token token = {pattern.second, match.captured(1), _stack.back().line, _stack.back().column};\n      _stack.back().offset += match.capturedLength();\n      _stack.back().column += token.value.size();\n      _currentLine = _currentLine.mid(match.capturedLength());\n      return token;\n    }\n  }\n  _errorMessage = tr(\"Lexer error %1,%2: Unexpected char '%3'.\").arg(_stack.back().line)\n      .arg(_stack.back().column).arg(_currentLine.at(0));\n  return {Token::T_ERROR, _errorMessage, _stack.back().line, _stack.back().column};\n}\n\nvoid\nCSVLexer::push() {\n  _stack.push_back(_stack.back());\n}\n\nvoid\nCSVLexer::pop() {\n  if (_stack.size() < 2)\n    return;\n  _stack.pop_back();\n  _stream.seek(_stack.back().offset);\n  _currentLine = QString();\n}\n\n/* ********************************************************************************************* *\n * Implementation of CSVHandler\n * ********************************************************************************************* */\nCSVHandler::CSVHandler(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nCSVHandler::~CSVHandler() {\n  // pass...\n}\n\nbool\nCSVHandler::handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(ids);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage)\n  return true;\n}\n\nbool\nCSVHandler::handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(name);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(text);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(text);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(level);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(speech);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,\n                              qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(num);\n  Q_UNUSED(rxTone);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id,\n                                 bool rxTone, qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(type);\n  Q_UNUSED(id);\n  Q_UNUSED(rxTone);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,\n                            qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(contacts);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n    qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,\n    qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(rx);\n  Q_UNUSED(tx);\n  Q_UNUSED(power);\n  Q_UNUSED(scan);\n  Q_UNUSED(tot);\n  Q_UNUSED(ro);\n  Q_UNUSED(admit);\n  Q_UNUSED(color);\n  Q_UNUSED(slot);\n  Q_UNUSED(gl);\n  Q_UNUSED(contact);\n  Q_UNUSED(gps);\n  Q_UNUSED(roam);\n  Q_UNUSED(radioID);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n    qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,\n    FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(rx);\n  Q_UNUSED(tx);\n  Q_UNUSED(power);\n  Q_UNUSED(scan);\n  Q_UNUSED(aprs);\n  Q_UNUSED(tot);\n  Q_UNUSED(ro);\n  Q_UNUSED(admit);\n  Q_UNUSED(squelch);\n  Q_UNUSED(rxTone);\n  Q_UNUSED(txTone);\n  Q_UNUSED(bw);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels,\n                       qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(a);\n  Q_UNUSED(channels);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleGPSSystem(\n    qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx,\n    qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(contactIdx);\n  Q_UNUSED(period);\n  Q_UNUSED(revertChannelIdx);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period,\n    const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path,\n    const QString &icon, const QString &message, qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(channelIdx);\n  Q_UNUSED(period);\n  Q_UNUSED(src);\n  Q_UNUSED(srcSSID);\n  Q_UNUSED(dest);\n  Q_UNUSED(destSSID);\n  Q_UNUSED(path);\n  Q_UNUSED(icon);\n  Q_UNUSED(message);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,\n                           const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(pch1);\n  Q_UNUSED(pch2);\n  Q_UNUSED(txch);\n  Q_UNUSED(channels);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\nbool\nCSVHandler::handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels,\n                              qint64 line, qint64 column, QString &errorMessage)\n{\n  Q_UNUSED(idx);\n  Q_UNUSED(name);\n  Q_UNUSED(channels);\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of CSVParser\n * ********************************************************************************************* */\nCSVParser::CSVParser(CSVHandler *handler, QObject *parent)\n  : QObject(parent), _errorMessage(), _handler(handler)\n{\n  // pass...\n}\n\nconst QString &\nCSVParser::errorMessage() const {\n  return _errorMessage;\n}\n\nbool\nCSVParser::parse(QTextStream &stream) {\n  if (! stream.seek(0))\n    return false;\n\n  CSVLexer lexer(stream);\n\n  for (CSVLexer::Token token=lexer.next(); CSVLexer::Token::T_END_OF_STREAM != token.type; token = lexer.next()) {\n    if (CSVLexer::Token::T_NEWLINE == token.type)\n      continue;\n    if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"id\" == token.value.toLower())) {\n      if (! _parse_radio_id(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"name\" == token.value.toLower())) {\n      if (! _parse_radio_name(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"introline1\" == token.value.toLower())) {\n      if (! _parse_introline1(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"introline2\" == token.value.toLower())) {\n      if (! _parse_introline2(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"miclevel\" == token.value.toLower())) {\n      if (! _parse_mic_level(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"speech\" == token.value.toLower())) {\n      if (! _parse_speech(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"userdb\" == token.value.toLower())) {\n      if (! _parse_userdb(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"contact\" == token.value.toLower())) {\n      if (! _parse_contacts(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"grouplist\" == token.value.toLower())) {\n      if (! _parse_rx_groups(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"digital\" == token.value.toLower())) {\n      if (! _parse_digital_channels(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"analog\" == token.value.toLower())) {\n      if (! _parse_analog_channels(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"zone\" == token.value.toLower())) {\n      if (! _parse_zones(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"gps\" == token.value.toLower())) {\n      if (! _parse_gps_systems(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"aprs\" == token.value.toLower())) {\n      if (! _parse_aprs_systems(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"scanlist\" == token.value.toLower())) {\n      if (! _parse_scanlists(lexer))\n        return false;\n    } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"roaming\" == token.value.toLower())) {\n      if (! _parse_roaming_zones(lexer))\n        return false;\n    } else if (CSVLexer::Token::T_ERROR == token.type) {\n      _errorMessage = QString(\"Lexer error @ %1,%2 '%3': %4\").arg(token.line).arg(token.column)\n          .arg(token.value).arg(lexer.errorMessage());\n      return false;\n    } else {\n      _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4'.\").arg(token.line)\n          .arg(token.column).arg(token.type).arg(token.value);\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nCSVParser::_parse_radio_id(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  QList<qint64> ids;\n  token = lexer.next();\n  while (CSVLexer::Token::T_NUMBER == token.type) {\n    ids.append(token.value.toInt());\n    token = lexer.next();\n    if (CSVLexer::Token::T_COMMA == token.type) {\n      token = lexer.next();\n      continue;\n    }\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  if (0 == ids.size()) {\n    _errorMessage = QString(\"Parse error @ %1,%2: At least one radio ID must be specified.\")\n        .arg(token.line).arg(token.column);\n    return false;\n  }\n\n  return _handler->handleRadioId(ids, token.line, token.column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_radio_name(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  token = lexer.next();\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n  qint64 line=token.line, column=token.column;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleRadioName(name, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_introline1(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  token = lexer.next();\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString text = token.value;\n  qint64 line=token.line, column=token.column;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleIntroLine1(text, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_introline2(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  token = lexer.next();\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString text = token.value;\n  qint64 line=token.line, column=token.column;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleIntroLine2(text, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_mic_level(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 level = token.value.toInt();\n  qint64 line=token.line, column=token.column;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleMicLevel(level, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_speech(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_KEYWORD != token.type) ||\n      ((\"on\" != token.value.toLower()) && (\"off\" != token.value.toLower())))\n  {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'On' or 'Off'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 line=token.line, column=token.column;\n  bool speech = (\"on\" == token.value.toLower());\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleSpeech(speech, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_userdb(CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  if (CSVLexer::Token::T_COLON != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected ':'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_KEYWORD != token.type) ||\n      ((\"on\" != token.value.toLower()) && (\"off\" != token.value.toLower())))\n  {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'On' or 'Off'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 line=token.line, column=token.column;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  logWarn() << line << \",\" << column << \": The 'UserDB' setting is obsolete. \"\n            << \"It will be removed in future releases. Just delete this line.\";\n\n  // Ignore user DB setting\n  return true;\n}\n\nbool\nCSVParser::_parse_contacts(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_contact(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_contact(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_KEYWORD != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  bool dtmf = false;\n  DMRContact::Type type = DMRContact::PrivateCall;\n  if (\"group\" == token.value.toLower()) {\n    type = DMRContact::GroupCall;\n  } else if (\"private\" == token.value.toLower()) {\n    type = DMRContact::PrivateCall;\n  } else if (\"all\" == token.value.toLower()) {\n    type = DMRContact::AllCall;\n  } else if (\"dtmf\" == token.value.toLower()) {\n    dtmf = true;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Group', 'Private', 'All' or 'DTMF'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ( (!dtmf) && (CSVLexer::Token::T_NUMBER != token.type) ) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  } else if (dtmf && (CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_STRING != token.type)) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString dtmf_num = token.value;\n  if (dtmf && (! validDTMFNumber(dtmf_num))) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Invalid DTMF number '%3'.\")\n        .arg(token.line).arg(token.column).arg(token.value);\n  }\n  qint64 id = token.value.toInt();\n\n  bool rxToneEnabled;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type)\n    rxToneEnabled = false;\n  else if (CSVLexer::Token::T_ENABLED == token.type)\n    rxToneEnabled = true;\n  else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  if (dtmf)\n    return _handler->handleDTMFContact(idx, name, dtmf_num, rxToneEnabled, line, column, _errorMessage);\n  return _handler->handleDigitalContact(idx, name, type, id, rxToneEnabled, line, column, _errorMessage);\n}\n\n\nbool\nCSVParser::_parse_rx_groups(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  while (CSVLexer::Token::T_KEYWORD==token.type)\n    token = lexer.next();\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_rx_group(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n\n  return false;\n}\n\nbool\nCSVParser::_parse_rx_group(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  QList<qint64> lst;\n  token = lexer.next();\n  while (CSVLexer::Token::T_NUMBER == token.type) {\n    lst.append(token.value.toInt());\n    token = lexer.next();\n    if (CSVLexer::Token::T_COMMA == token.type)\n      token = lexer.next();\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleGroupList(idx, name, lst, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_digital_channels(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_digital_channel(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_digital_channel(qint64 idx, CSVLexer &lexer) {\n  bool ok=false;\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  double rx = token.value.toDouble(&ok);\n  if (! ok) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Cannot convert '%3' to double.\")\n        .arg(token.line).arg(token.column).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  double tx = token.value.toDouble(&ok);\n  if (! ok) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Cannot convert '%3' to double.\")\n        .arg(token.line).arg(token.column).arg(token.value);\n    return false;\n  }\n  if (token.value.startsWith('+') || token.value.startsWith('-'))\n    tx = rx + tx;\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_KEYWORD != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  Channel::Power pwr;\n  if (\"max\" == token.value.toLower()) {\n    pwr = Channel::Power::Max;\n  } else if (\"high\" == token.value.toLower()) {\n    pwr = Channel::Power::High;\n  } else if (\"mid\" == token.value.toLower()) {\n    pwr = Channel::Power::Mid;\n  } else if (\"low\" == token.value.toLower()) {\n    pwr = Channel::Power::Low;\n  } else if (\"min\" == token.value.toLower()) {\n    pwr = Channel::Power::Min;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'High' or 'Low'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 scanlist;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    scanlist = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    scanlist = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_NOT_SET != token.type)) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 tot =  (CSVLexer::Token::T_NOT_SET == token.type) ? 0 : token.value.toInt();\n\n  bool rxOnly;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type)\n    rxOnly = false;\n  else if (CSVLexer::Token::T_ENABLED == token.type)\n    rxOnly = true;\n  else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  DMRChannel::Admit admit;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    admit = DMRChannel::Admit::Always;\n  } else if (CSVLexer::Token::T_KEYWORD == token.type) {\n    if (\"free\" == token.value.toLower())\n      admit = DMRChannel::Admit::Free;\n    else if (\"color\" == token.value.toLower())\n      admit = DMRChannel::Admit::ColorCode;\n    else {\n      _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free' or 'Color'.\")\n          .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n      return false;\n    }\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Color' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 color = token.value.toInt();\n\n  token = lexer.next();\n  DMRChannel::TimeSlot slot;\n  if (CSVLexer::Token::T_NUMBER == token.type) {\n    if (1 == token.value.toInt()) {\n      slot = DMRChannel::TimeSlot::TS1;\n    } else if (2 == token.value.toInt()) {\n      slot = DMRChannel::TimeSlot::TS2;\n    } else {\n      _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected '1' or '2'.\")\n          .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n      return false;\n    }\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 rxGroupList;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    rxGroupList = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    rxGroupList = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 txContact;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    txContact = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    txContact = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 gps;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    gps = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    gps = token.value.toInt(&ok);\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 roam = -1, radioID = -1;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    roam = -1;\n  } else if (CSVLexer::Token::T_ENABLED == token.type) {\n    roam = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    roam = token.value.toInt(&ok);\n  } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) {\n    // This entry is optional for backward compatibility.\n    goto done;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number, '-' or '+'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    radioID = -1;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    radioID = token.value.toInt(&ok);\n  } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) {\n    // This entry is optional for backward compatibility.\n    goto done;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\ndone:\n  return _handler->handleDigitalChannel(idx, name, rx, tx, pwr, scanlist, tot, rxOnly, admit, color,\n                                        slot, rxGroupList, txContact, gps, roam, radioID, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_analog_channels(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_analog_channel(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_analog_channel(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  double rx = token.value.toDouble();\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  double tx = token.value.toDouble();\n  if (token.value.startsWith('+') || token.value.startsWith('-'))\n    tx = rx + tx;\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_KEYWORD != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  Channel::Power pwr;\n  if (\"max\" == token.value.toLower()) {\n    pwr = Channel::Power::Max;\n  } else if (\"high\" == token.value.toLower()) {\n    pwr = Channel::Power::High;\n  } else if (\"mid\" == token.value.toLower()) {\n    pwr = Channel::Power::Mid;\n  } else if (\"low\" == token.value.toLower()) {\n    pwr = Channel::Power::Low;\n  } else if (\"min\" == token.value.toLower()) {\n    pwr = Channel::Power::Min;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'High' or 'Low'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 scanlist;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    scanlist = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    scanlist = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NUMBER != token.type) && (CSVLexer::Token::T_NOT_SET != token.type)) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 tot =  (CSVLexer::Token::T_NOT_SET == token.type) ? 0 : token.value.toInt();\n\n  bool rxOnly;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type)\n    rxOnly = false;\n  else if (CSVLexer::Token::T_ENABLED == token.type)\n    rxOnly = true;\n  else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected '+' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  FMChannel::Admit admit;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    admit = FMChannel::Admit::Always;\n  } else if (CSVLexer::Token::T_KEYWORD == token.type) {\n    if (\"free\" == token.value.toLower())\n      admit = FMChannel::Admit::Free;\n    else if (\"tone\" == token.value.toLower())\n      admit = FMChannel::Admit::Tone;\n    else {\n      _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Tone'.\")\n          .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n      return false;\n    }\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'Free', 'Tone' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  qint64 squelch = token.value.toInt();\n\n  token = lexer.next();\n  SelectiveCall rxTone;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    rxTone = SelectiveCall();\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    rxTone = SelectiveCall(token.value.toFloat());\n  } else if (CSVLexer::Token::T_DCS_N == token.type) {\n    rxTone = SelectiveCall(token.value.toUInt(), false);\n  } else if (CSVLexer::Token::T_DCS_I == token.type) {\n    rxTone = SelectiveCall(token.value.toUInt(), true);\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  SelectiveCall txTone;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    txTone = SelectiveCall();\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    txTone = SelectiveCall(token.value.toFloat());\n  } else if (CSVLexer::Token::T_DCS_N == token.type) {\n    txTone = SelectiveCall(token.value.toUInt(), false);\n  } else if (CSVLexer::Token::T_DCS_I == token.type) {\n    txTone = SelectiveCall(token.value.toUInt(), true);\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  FMChannel::Bandwidth bw;\n  if (25 == token.value.toFloat()) {\n    bw = FMChannel::Bandwidth::Wide;\n  } else if (12.5 == token.value.toFloat()) {\n    bw = FMChannel::Bandwidth::Narrow;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected '12.5' or '25'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  qint64 aprs = 0;\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    aprs = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    aprs = token.value.toInt();\n  } else if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type)) {\n    // This entry is optional for backward compatibility.\n    goto done;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\ndone:\n  return _handler->handleAnalogChannel(idx, name, rx, tx, pwr, scanlist, aprs, tot, rxOnly, admit, squelch,\n                                       rxTone, txTone, bw, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_zones(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_zone(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_zone(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_KEYWORD != token.type) || ((\"a\" != token.value.toLower()) && (\"b\" != token.value.toLower())) ) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected 'A or 'B'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  bool a = (\"a\" == token.value.toLower());\n\n  QList<qint64> lst;\n  token = lexer.next();\n  while (CSVLexer::Token::T_NUMBER == token.type) {\n    lst.append(token.value.toInt());\n    token = lexer.next();\n    if (CSVLexer::Token::T_COMMA == token.type)\n      token = lexer.next();\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleZone(idx, name, a, lst, line, column, _errorMessage);\n}\n\n\nbool\nCSVParser::_parse_gps_systems(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_gps_system(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_gps_system(qint64 id, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  qint64 contact;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER == token.type) {\n    contact = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  qint64 period;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER == token.type) {\n    period = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  qint64 chan;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    chan = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    chan = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleGPSSystem(id, name, contact, period, chan, line, column, _errorMessage);\n}\n\n\nbool\nCSVParser::_parse_aprs_systems(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_aprs_system(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_aprs_system(qint64 id, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  qint64 channel;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER == token.type) {\n    channel = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  qint64 period;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NUMBER == token.type) {\n    period = token.value.toInt();\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  QString src=\"\"; unsigned srcSSID=0;\n  if (CSVLexer::Token::T_APRSCALL == token.type) {\n    QStringList lst = token.value.split('-');\n    src = lst.first(); srcSSID = lst.last().toInt();\n  } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (token.value.size()<=6)) {\n    src = token.value;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected APRS call.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  QString dest=\"\"; unsigned destSSID=0;\n  if (CSVLexer::Token::T_APRSCALL == token.type) {\n    QStringList lst = token.value.split('-');\n    dest = lst.first(); destSSID = lst.last().toInt();\n  } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (token.value.size()<=6)) {\n    dest = token.value;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected APRS call.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  QString path=\"\";\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    // pass..\n  } else if (CSVLexer::Token::T_STRING == token.type) {\n    path = token.value;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  QString iconname;\n  if ((CSVLexer::Token::T_KEYWORD == token.type) || (CSVLexer::Token::T_STRING == token.type)) {\n    iconname = token.value;\n  } else if (CSVLexer::Token::T_NOT_SET == token.type) {\n    iconname = \"\";\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected keyword or string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  QString message;\n  if (CSVLexer::Token::T_STRING == token.type) {\n    message = token.value;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleAPRSSystem(id, name, channel, period, src, srcSSID, dest, destSSID,\n                                    path, iconname, message, line, column, _errorMessage);\n}\n\n\nbool\nCSVParser::_parse_scanlists(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_scanlist(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_scanlist(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  qint64 pch1;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    pch1 = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    pch1 = token.value.toInt() + 1;\n  } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"sel\" == token.value.toLower())) {\n    pch1 = -1;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  qint64 pch2;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    pch2 = 0;\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    pch2 = token.value.toInt() + 1;\n  } else if ((CSVLexer::Token::T_KEYWORD == token.type) && (\"sel\" == token.value.toLower())) {\n    pch2 = -1;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  qint64 txch;\n  token = lexer.next();\n  if (CSVLexer::Token::T_NOT_SET == token.type) {\n    txch = 0;\n  } else if (CSVLexer::Token::T_KEYWORD == token.type) {\n    if (\"sel\" == token.value.toLower()) {\n      txch = -1;\n    } else {\n      _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number, 'Sel' or '-'.\")\n          .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n      return false;\n    }\n  } else if (CSVLexer::Token::T_NUMBER == token.type) {\n    txch = token.value.toInt() + 1;\n  } else {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected number, 'Sel' or '-'.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  QList<qint64> lst;\n  token = lexer.next();\n  while ((CSVLexer::Token::T_NUMBER == token.type) || (CSVLexer::Token::T_KEYWORD == token.type)) {\n    if (CSVLexer::Token::T_NUMBER == token.type) {\n      lst.append(token.value.toInt());\n    } else if (CSVLexer::Token::T_KEYWORD == token.type) {\n      if (\"sel\" == token.value.toLower())\n        lst.append(-1);\n      else {\n        _errorMessage = QString(\"Parse error @ %1,%2: Unexpected keyword '%3' expected number or 'Sel'.\")\n            .arg(token.line).arg(token.column).arg(token.value);\n        return false;\n      }\n    }\n\n    token = lexer.next();\n    if (CSVLexer::Token::T_COMMA == token.type)\n      token = lexer.next();\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleScanList(idx, name, pch1, pch2, txch, lst, line, column, _errorMessage);\n}\n\nbool\nCSVParser::_parse_roaming_zones(CSVLexer &lexer) {\n  // skip rest of header\n  CSVLexer::Token token = lexer.next();\n  for (; CSVLexer::Token::T_KEYWORD==token.type; token=lexer.next()) {\n    // skip\n  }\n  if (CSVLexer::Token::T_NEWLINE != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  token = lexer.next();\n  for (; CSVLexer::Token::T_NUMBER == token.type; token=lexer.next()) {\n    qint64 idx = token.value.toInt();\n    if (! _parse_roaming_zone(idx, lexer))\n      return false;\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE == token.type) || (CSVLexer::Token::T_END_OF_STREAM == token.type))\n    return true;\n\n  _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n      .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n  return false;\n}\n\nbool\nCSVParser::_parse_roaming_zone(qint64 idx, CSVLexer &lexer) {\n  CSVLexer::Token token = lexer.next();\n  qint64 line=token.line, column=token.column;\n  if (CSVLexer::Token::T_STRING != token.type) {\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected string.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n  QString name = token.value;\n\n  QList<qint64> lst;\n  token = lexer.next();\n  while (CSVLexer::Token::T_NUMBER == token.type) {\n    lst.append(token.value.toInt());\n    token = lexer.next();\n    if (CSVLexer::Token::T_COMMA == token.type)\n      token = lexer.next();\n  }\n\n  if ((CSVLexer::Token::T_NEWLINE != token.type) && (CSVLexer::Token::T_END_OF_STREAM != token.type)){\n    _errorMessage = QString(\"Parse error @ %1,%2: Unexpected token %3 '%4' expected newline/EOS.\")\n        .arg(token.line).arg(token.column).arg(token.type).arg(token.value);\n    return false;\n  }\n\n  return _handler->handleRoamingZone(idx, name, lst, line, column, _errorMessage);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of CSVReader\n * ********************************************************************************************* */\nCSVReader::CSVReader(Config *config, QObject *parent)\n  : CSVHandler(parent), _link(false), _config(config)\n{\n  // pass...\n}\n\n\nbool\nCSVReader::read(Config *config, QTextStream &stream, QString &errorMessage) {\n  // Seek to start-of-stream\n  if (! stream.seek(0)) {\n    errorMessage = \"Cannot read CSV codeplug: Cannot seek within text stream. Abort import.\" ;\n    return false;\n  }\n\n  config->clear();\n\n  CSVReader reader(config);\n  CSVParser parser(&reader);\n\n  if (! parser.parse(stream)) {\n    errorMessage = parser.errorMessage();\n    errorMessage.append(tr(\"\\nThe generic code-plug format might be changed with a new release of qdmr.\"\n                           \"\\nVisit https://github.com/hmatuschek/qdmr/releases for further information.\"));\n    return false;\n  }\n  reader._link = true;\n\n  if (! parser.parse(stream)) {\n    errorMessage = parser.errorMessage();\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nCSVReader::handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link)\n    return true;\n  logDebug() << \"Got \" << ids.count() << \" IDs...\";\n  for (int i=0; i<ids.count(); i++) {\n    DMRRadioID *id = new DMRRadioID(\"\", ids.at(i));\n    _config->radioIDs()->add(id);\n    _radioIDs[i+1] = id;\n  }\n\n  return true;\n}\n\nbool\nCSVReader::handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link && (!_config->settings()->defaultIdRef()->isNull())) {\n    _config->settings()->defaultIdRef()->as<DMRRadioID>()->setName(name);\n  }\n  return true;\n}\n\nbool\nCSVReader::handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link) {\n    _config->settings()->boot()->setMessage1(text);\n  }\n  return true;\n}\n\nbool\nCSVReader::handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link) {\n    _config->settings()->boot()->setMessage2(text);\n  }\n  return true;\n}\n\nbool\nCSVReader::handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link) {\n    _config->settings()->audio()->setMicGain(Level::fromValue(level));\n  }\n  return true;\n}\n\n\nbool\nCSVReader::handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage) {\n  Q_UNUSED(line);\n  Q_UNUSED(column);\n  Q_UNUSED(errorMessage);\n\n  if (_link) {\n    _config->settings()->audio()->enableSpeechSynthesis(speech);\n  }\n  return true;\n}\n\n\nbool\nCSVReader::handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,\n                             qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    // pass...\n    return true;\n  }\n\n  if (_digital_contacts.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create contact '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n  DTMFContact *contact = new DTMFContact(name, num, rxTone);\n  //_contacts[idx] = contact;\n  _config->contacts()->add(contact);\n\n  return true;\n}\n\n\nbool\nCSVReader::handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type,\n                                qint64 id, bool rxTone, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    // pass...\n    return true;\n  }\n\n  if (_digital_contacts.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create contact '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n  DMRContact *contact = new DMRContact(type, name, id, rxTone);\n  _digital_contacts[idx] = contact;\n  _config->contacts()->add(contact);\n\n  return true;\n}\n\nbool\nCSVReader::handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,\n                           qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    foreach(qint64 i, contacts) {\n      // Check contacts\n      if (! _digital_contacts.contains(i)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create contact list '%3' with index %4, unknown index.\")\n            .arg(line).arg(column).arg(name).arg(i);\n        return false;\n      }\n      // link contacts\n      _rxgroups[idx]->addContact(_digital_contacts[i]);\n    }\n    // done\n    return true;\n  }\n\n  // check index\n  if (_rxgroups.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create RX-group list '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n  // Create group list\n  RXGroupList *lst = new RXGroupList(name);\n  _config->rxGroupLists()->add(lst);\n  _rxgroups[idx] = lst;\n\n  return true;\n}\n\nbool\nCSVReader::handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n    qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,\n    qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    // Check RX Grouplist\n    if (0 < gl) {\n      if (! _rxgroups.contains(gl)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown RX-group list %4.\")\n            .arg(line).arg(column).arg(name).arg(gl);\n        return false;\n      }\n      _channels[idx]->as<DMRChannel>()->setGroupList(_rxgroups[gl]);\n    }\n\n    // Check TX Contact\n    if (0 < contact) {\n      if (! _digital_contacts.contains(contact)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown contact index %4.\")\n            .arg(line).arg(column).arg(name).arg(contact);\n        return false;\n      }\n      _channels[idx]->as<DMRChannel>()->setContact(_digital_contacts[contact]);\n    }\n\n    // Check scanlist\n    if (0 < scan) {\n      if (! _scanlists.contains(scan)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown scan-list index %4.\")\n            .arg(line).arg(column).arg(name).arg(scan);\n        return false;\n      }\n      _channels[idx]->as<DMRChannel>()->setScanList(_scanlists[scan]);\n    }\n\n    // Check GPS System\n    if (0 < gps) {\n      if (! _posSystems.contains(gps)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown GPS system index %4.\")\n            .arg(line).arg(column).arg(name).arg(gps);\n        return false;\n      }\n      _channels[idx]->as<DMRChannel>()->setAPRS(_posSystems[gps]);\n    }\n\n    // Check roaming zone\n    if (0 == roam) {\n      // index 0 mean default zone -> just set it\n      _channels[idx]->as<DMRChannel>()->setRoaming(DefaultRoamingZone::get());\n    } else if (0 < roam) {\n      // positive index means reference to roaming specific roaming zone\n      if (! _roamingZones.contains(roam)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown roaming zone index %4.\")\n            .arg(line).arg(column).arg(name).arg(roam);\n        return false;\n      }\n      _channels[idx]->as<DMRChannel>()->setRoaming(_roamingZones[roam]);\n    }\n\n    // check radio ID\n    if (-1 == radioID) {\n      _channels[idx]->as<DMRChannel>()->setRadioId(DefaultRadioID::get());\n    } else if ((0 < radioID) && (_radioIDs.contains(radioID))) {\n      _channels[idx]->as<DMRChannel>()->setRadioId(_radioIDs[radioID]);\n    } else {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot link digital channel '%3', unknown radio ID index %4.\")\n          .arg(line).arg(column).arg(name).arg(radioID);\n      return false;\n    }\n\n    return true;\n  }\n\n  // check index\n  if (_channels.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create digital channel '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n\n  DMRChannel *chan = new DMRChannel();\n  chan->setName(name);\n  chan->setRXFrequency(Frequency::fromMHz(rx));\n  chan->setTXFrequency(Frequency::fromMHz(tx));\n  chan->setPower(power);\n  chan->setTimeout(Interval::fromSeconds(tot));\n  chan->setRXOnly(ro);\n  chan->setAdmit(admit);\n  chan->setColorCode(color);\n  chan->setTimeSlot(slot);\n\n  _config->channelList()->add(chan);\n  _channels[idx] = chan;\n\n  return true;\n}\n\n\nbool\nCSVReader::handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n    qint64 aprs, qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,\n    FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    // Check scanlist\n    if (0 < scan) {\n      if (! _scanlists.contains(scan)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link analog channel '%3', unknown scan-list index %4.\")\n            .arg(line).arg(column).arg(name).arg(scan);\n        return false;\n      }\n      _channels[idx]->as<FMChannel>()->setScanList(_scanlists[scan]);\n    }\n\n    // Check APRS system\n    if (0 < aprs) {\n      if (! _posSystems.contains(aprs)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link analog channel '%3', unknown APRS system index %4.\")\n            .arg(line).arg(column).arg(name).arg(aprs);\n        return false;\n      }\n      if (! _posSystems[aprs]->is<FMAPRSSystem>()) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot link analog channel '%3', positioning system %4 ('%5') is not an APRS system!.\")\n            .arg(line).arg(column).arg(name).arg(aprs).arg(_posSystems[aprs]->name());\n        return false;\n      }\n      _channels[idx]->as<FMChannel>()->setAPRS(_posSystems[aprs]->as<FMAPRSSystem>());\n    }\n    return true;\n  }\n\n  // check index\n  if (_channels.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create analog channel '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n\n  FMChannel *chan = new FMChannel();\n  chan->setName(name);\n  chan->setRXFrequency(Frequency::fromMHz(rx));\n  chan->setTXFrequency(Frequency::fromMHz(tx));\n  chan->setPower(power);\n  chan->setTimeout(Interval::fromSeconds(tot));\n  chan->setRXOnly(ro);\n  chan->setAdmit(admit);\n  chan->setSquelch(Level::fromValue(squelch));\n  chan->setRXTone(rxTone);\n  chan->setTXTone(txTone);\n  chan->setBandwidth(bw);\n\n  _config->channelList()->add(chan);\n  _channels[idx] = chan;\n\n  return true;\n}\n\nbool\nCSVReader::handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    foreach(qint64 i, channels) {\n      // Check channels\n      if (! _channels.contains(i)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create zone '%3', unknown channel index %4.\")\n            .arg(line).arg(column).arg(name).arg(i);\n        return false;\n      }\n      // link channels\n      if (a)\n        _zones[idx]->A()->add(_channels[i]);\n      else\n        _zones[idx]->B()->add(_channels[i]);\n    }\n    // done\n    return true;\n  }\n\n  // check index\n  if (! _zones.contains(idx)) {\n    Zone *zone = new Zone(name);\n    _zones[idx] = zone;\n    _config->zones()->add(zone);\n  }\n\n  return true;\n}\n\nbool\nCSVReader::handleGPSSystem(\n    qint64 idx, const QString &name, qint64 contactIdx, qint64 period, qint64 revertChannelIdx,\n    qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    DMRAPRSSystem *gps = _posSystems[idx]->as<DMRAPRSSystem>();\n    // Check contact ID\n    if (! _digital_contacts.contains(contactIdx)) {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create GPS system '%3', unknown destination contact ID %4.\")\n          .arg(line).arg(column).arg(name).arg(contactIdx);\n      logError() << errorMessage;\n      return false;\n    }\n    gps->setContact(_digital_contacts[contactIdx]);\n\n    if (revertChannelIdx) {\n      if (! _channels.contains(revertChannelIdx))  {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create GPS system '%3', unknown revert channel ID %4.\")\n            .arg(line).arg(column).arg(name).arg(revertChannelIdx);\n        return false;\n      }\n      if (! _channels[revertChannelIdx]->is<DMRChannel>()) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create GPS system '%3', revert channel %4 is not a digital channel.\")\n            .arg(line).arg(column).arg(name).arg(revertChannelIdx);\n        return false;\n      }\n      gps->setRevertChannel(_channels[revertChannelIdx]->as<DMRChannel>());\n    }\n\n    return true;\n  }\n\n  // check index\n  if (_posSystems.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create GPS system '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n\n  DMRAPRSSystem *gps = new DMRAPRSSystem(name, nullptr, nullptr, Interval::fromSeconds(period));\n  _posSystems[idx] = gps;\n  _config->posSystems()->add(gps);\n\n  return true;\n}\n\nbool\nCSVReader::handleAPRSSystem(\n    qint64 idx, const QString &name, qint64 channelIdx, qint64 period,\n    const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID,\n    const QString &path, const QString &iconname, const QString &message,\n    qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    FMAPRSSystem *aprs = _posSystems[idx]->as<FMAPRSSystem>();\n    if (! _channels.contains(channelIdx))  {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create APRS system '%3', unknown channel ID %4.\")\n          .arg(line).arg(column).arg(name).arg(channelIdx);\n      return false;\n    }\n    if (! _channels[channelIdx]->is<FMChannel>()) {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create APRS system '%3', transmit channel %4 is not an analog channel.\")\n          .arg(line).arg(column).arg(name).arg(channelIdx);\n      return false;\n    }\n    aprs->setRevertChannel(_channels[channelIdx]->as<FMChannel>());\n\n    return true;\n  }\n\n  // check index\n  if (_posSystems.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create GPS system '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n\n  FMAPRSSystem::Icon icon = name2aprsicon(iconname);\n  FMAPRSSystem *aprs = new FMAPRSSystem(name, nullptr, dest, destSSID, src, srcSSID, path,\n                                        icon, message, Interval::fromSeconds(period));\n  _posSystems[idx] = aprs;\n  _config->posSystems()->add(aprs);\n\n  return true;\n}\n\nbool\nCSVReader::handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,\n                          const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    // Check PriChan 1\n    if ((pch1>0) && (! _channels.contains(pch1))) {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create scanlist '%3', unknown priority channel 1 index %4.\")\n          .arg(line).arg(column).arg(name).arg(pch1);\n      return false;\n    }\n    if (pch1 < 0) {\n      _scanlists[idx]->setPrimaryChannel(SelectedChannel::get());\n    } else if (pch2 > 0) {\n      _scanlists[idx]->setPrimaryChannel(_channels[pch1-1]);\n    }\n\n    // Check PriChan 2\n    if ((pch2>0) && (! _channels.contains(pch2))) {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create scanlist '%3', unknown priority channel 2 index %4.\")\n          .arg(line).arg(column).arg(name).arg(pch2);\n      return false;\n    }\n    if (pch2 < 0) {\n      _scanlists[idx]->setSecondaryChannel(SelectedChannel::get());\n    } else if (pch2 > 0){\n      _scanlists[idx]->setSecondaryChannel(_channels[pch2-1]);\n    }\n\n    // Check Tx channel\n    if ((txch>0) && (! _channels.contains(txch-1))) {\n      errorMessage = QString(\"Parse error @ %1,%2: Cannot create scanlist '%3', unknown TX channel index %4.\")\n          .arg(line).arg(column).arg(name).arg(txch-1);\n      return false;\n    }\n    if (txch<0) {\n      _scanlists[idx]->setRevertChannel(SelectedChannel::get());\n    } else if (txch>0) {\n      _scanlists[idx]->setRevertChannel(_channels[txch-2]);\n    }\n\n    // Check channels\n    foreach(qint64 i, channels) {\n      if ((i>=0) && (!_channels.contains(i))) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create scanlist '%3', unknown channel index %4.\")\n            .arg(line).arg(column).arg(name).arg(i);\n        return false;\n      }\n      // link channels\n      if (i<0)\n        _scanlists[idx]->addChannel(SelectedChannel::get());\n      else\n        _scanlists[idx]->addChannel(_channels[i]);\n    }\n    // done\n    return true;\n  }\n\n  // check index\n  if (_scanlists.contains(idx)) {\n    errorMessage = QString(\"Parse error @ %1,%2: Cannot create scan list '%3' with index %4, index already taken.\")\n        .arg(line).arg(column).arg(name).arg(idx);\n    return false;\n  }\n\n  ScanList *lst = new ScanList(name);\n  _scanlists[idx] = lst;\n  _config->scanlists()->add(lst);\n\n  return true;\n}\n\nbool\nCSVReader::handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage)\n{\n  if (_link) {\n    foreach(qint64 i, channels) {\n      // Check channels\n      if (! _channels.contains(i)) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot create zone '%3', unknown channel index %4.\")\n            .arg(line).arg(column).arg(name).arg(i);\n        return false;\n      } else if (_channels[i]->is<FMChannel>()) {\n        errorMessage = QString(\"Parse error @ %1,%2: Cannot add channel '%3' (idx %4) \"\n                               \"to roaming zone, only digital channels can be used.\")\n            .arg(line).arg(column).arg(name).arg(i);\n        return false;\n      }\n      RoamingChannel *rch = RoamingChannel::fromDMRChannel(_channels[i]->as<DMRChannel>());\n      _config->roamingChannels()->add(rch);\n      _roamingZones[idx]->addChannel(rch);\n    }\n    // done\n    return true;\n  }\n\n  // check index\n  if (! _roamingZones.contains(idx)) {\n    RoamingZone *zone = new RoamingZone(name);\n    _roamingZones[idx] = zone;\n    _config->roamingZones()->add(zone);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/csvreader.hh",
    "content": "#ifndef CSVREADER_HH\n#define CSVREADER_HH\n\n#include <QObject>\n#include <QTextStream>\n#include <QMap>\n#include <QVector>\n\n#include \"channel.hh\"\n#include \"contact.hh\"\n\nclass Config;\nclass DMRContact;\nclass RXGroupList;\nclass Zone;\nclass PositionReportingSystem;\nclass ScanList;\nclass RoamingZone;\n\n\n/** The lexer class divides a text stream into tokens. */\nclass CSVLexer: public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** The token. */\n  struct Token {\n  public:\n    /** Possible token types. */\n    enum TokenType {\n      T_KEYWORD,         ///< A Keyword/Identifier.\n      T_APRSCALL,        ///< A APRS call of form CALL-SSID.\n      T_STRING,          ///< A quoted string.\n      T_NUMBER,          ///< An integer or floating point number.\n      T_DCS_N,           ///< A normal DCS code number.\n      T_DCS_I,           ///< An inverted DCS code number.\n      T_COLON,           ///< A colon.\n      T_NOT_SET,         ///< A dash, being used as \"not-set\".\n      T_ENABLED,         ///< A plus sign, being used as \"enabled\"\n      T_COMMA,           ///< A comma\n\n      T_WHITESPACE,      ///< Any whitespace character excluding newline.\n      T_NEWLINE,         ///< A new line.\n      T_COMMENT,         ///< A comment starts with # end ends at the line-end.\n\n      T_END_OF_STREAM,   ///< Indicates the end-of-input.\n      T_ERROR            ///< Indicates a lexer error.\n    };\n\n    /// The token type.\n    TokenType type;\n    /// The token value.\n    QString value;\n    /// Line number.\n    qint64 line;\n    /// Column number.\n    qint64 column;\n  };\n\n  /// Current state of lexer.\n  struct State {\n    /// The current stream offset.\n    qint64 offset;\n    /// The current line count.\n    qint64 line;\n    /// The current column number.\n    qint64 column;\n  };\n\npublic:\n  /** Constructs a lexer for the given stream. */\n  CSVLexer(QTextStream &stream, QObject *parent=nullptr);\n\n  /** Saves the current lexer state. */\n  void push();\n  /** Restores the last lexer state. */\n  void pop();\n  /** Reads the next token from the stream. */\n  Token next();\n  /** Returns the last error message. */\n  const QString &errorMessage() const;\n\nprotected:\n  /** Internal used function to get the next token. Also returns ignored tokens like whitespace\n   * and comment. */\n  Token lex();\n\nprotected:\n  /// The error message.\n  QString _errorMessage;\n  /// The text stream to read from.\n  QTextStream &_stream;\n  /// The stack of saved lexer states\n  QVector<State> _stack;\n  /// The current line count\n  QString _currentLine;\n  /// The list of patterns to match\n  static QVector< QPair<QRegularExpression, Token::TokenType> > _pattern;\n};\n\n\n/** Basic parse-handler interface.\n *\n * That is, a set of callbacks getting called by the parser on the occurrence of a particular\n * statement in the config file.\n * @ingroup conf */\nclass CSVHandler: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit CSVHandler(QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~CSVHandler();\n\n  /** Gets called once the DMR IDs has been parsed. */\n  virtual bool handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once the radio name has been parsed. */\n  virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once the first intro line has been parsed. */\n  virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once the second intro line has been parsed. */\n  virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once the MIC level has been parsed. */\n  virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once the Speech flag has been parsed. */\n  virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a DTMF contact has been parsed. */\n  virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,\n                                 qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a digital contact has been parsed. */\n  virtual bool handleDigitalContact(qint64 idx, const QString &name, DMRContact::Type type, qint64 id,\n                                    bool rxTone, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once an RX group list has been parsed. */\n  virtual bool handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,\n                               qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a digital channel has been parsed. */\n  virtual bool handleDigitalChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n      qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,\n      qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a analog channel has been parsed. */\n  virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs,\n      qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,\n      FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a zone list has been parsed. */\n  virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels,\n                          qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a GPS system has been parsed. */\n  virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period,\n                               qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a APRS system has been parsed. */\n  virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period,\n                                const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID,\n                                const QString &path, const QString &icon, const QString &message,\n                                qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a scan list has been parsed. */\n  virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,\n                              const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage);\n  /** Gets called once a roaming zone list has been parsed. */\n  virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels,\n                                 qint64 line, qint64 column, QString &errorMessage);\n\n};\n\n\n/** The actual config file parser.\n *\n * This class parses the config file and calls the associated callback functions of a handler\n * instance that is responsible to assemble the final @c Config instance. */\nclass CSVParser: public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a parser using the given handler instance. */\n  explicit CSVParser(CSVHandler *handler, QObject *parent=nullptr);\n\n  /** Parses the given text stream. */\n  bool parse(QTextStream &stream);\n\n  /** Returns the current error message, for example if @c parse returns @c false. */\n  const QString &errorMessage() const;\n\nprotected:\n  /** Internal function to parse DMR IDs. */\n  bool _parse_radio_id(CSVLexer &lexer);\n  /** Internal function to parse radio names. */\n  bool _parse_radio_name(CSVLexer &lexer);\n  /** Internal function to parse intro line 1. */\n  bool _parse_introline1(CSVLexer &lexer);\n  /** Internal function to parse intro line 2. */\n  bool _parse_introline2(CSVLexer &lexer);\n  /** Internal function to parse MIC level. */\n  bool _parse_mic_level(CSVLexer &lexer);\n  /** Internal function to parse Speech flag. */\n  bool _parse_speech(CSVLexer &lexer);\n  /** Internal function to parse UserDB flag. */\n  bool _parse_userdb(CSVLexer &lexer);\n  /** Internal function to parse a digital contact list. */\n  bool _parse_contacts(CSVLexer &lexer);\n  /** Internal function to parse digital contact. */\n  bool _parse_contact(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse an RX group list. */\n  bool _parse_rx_groups(CSVLexer &lexer);\n  /** Internal function to parse an RX group. */\n  bool _parse_rx_group(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a digital channel list. */\n  bool _parse_digital_channels(CSVLexer &lexer);\n  /** Internal function to parse a digital channel. */\n  bool _parse_digital_channel(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse an analog channel list. */\n  bool _parse_analog_channels(CSVLexer &lexer);\n  /** Internal function to parse an analog channel. */\n  bool _parse_analog_channel(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a zone list. */\n  bool _parse_zones(CSVLexer &lexer);\n  /** Internal function to parse a zone. */\n  bool _parse_zone(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a GPS system list. */\n  bool _parse_gps_systems(CSVLexer &lexer);\n  /** Internal function to parse a GPS system. */\n  bool _parse_gps_system(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a APRS system list. */\n  bool _parse_aprs_systems(CSVLexer &lexer);\n  /** Internal function to parse a APRS system. */\n  bool _parse_aprs_system(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a scanlist list. */\n  bool _parse_scanlists(CSVLexer &lexer);\n  /** Internal function to parse a scanlist. */\n  bool _parse_scanlist(qint64 id, CSVLexer &lexer);\n  /** Internal function to parse a roaming zone list. */\n  bool _parse_roaming_zones(CSVLexer &lexer);\n  /** Internal function to parse a zone. */\n  bool _parse_roaming_zone(qint64 id, CSVLexer &lexer);\n\nprotected:\n  /** Holds the current error message. */\n  QString _errorMessage;\n  /** The handler instance. */\n  CSVHandler *_handler;\n};\n\n\n\n/** Implements the text-file codeplug reader.\n * @ingroup conf */\nclass CSVReader : public CSVHandler\n{\n\tQ_OBJECT\n\nprotected:\n  /** Hidden constructor. Consider using the static @c read method. */\n  CSVReader(Config *config, QObject *parent=nullptr);\n\npublic:\n  /** Reads a config file from @c stream and stores the read configuration into @c config.\n   * @returns @c true on success. */\n  static bool read(Config *config, QTextStream &stream, QString &errorMessage);\n\n  virtual bool handleRadioId(const QList<qint64> &ids, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleRadioName(const QString &name, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleIntroLine1(const QString &text, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleIntroLine2(const QString &text, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleMicLevel(unsigned level, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleSpeech(bool speech, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleDTMFContact(qint64 idx, const QString &name, const QString &num, bool rxTone,\n                                 qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleDigitalContact(\n      qint64 idx, const QString &name, DMRContact::Type type, qint64 id, bool rxTone,\n      qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleGroupList(qint64 idx, const QString &name, const QList<qint64> &contacts,\n                               qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleDigitalChannel(\n      qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan,\n      qint64 tot, bool ro, DMRChannel::Admit admit, qint64 color, DMRChannel::TimeSlot slot,\n      qint64 gl, qint64 contact, qint64 gps, qint64 roam, qint64 radioID, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleAnalogChannel(qint64 idx, const QString &name, double rx, double tx, Channel::Power power, qint64 scan, qint64 aprs,\n      qint64 tot, bool ro, FMChannel::Admit admit, qint64 squelch, const SelectiveCall &rxTone, const SelectiveCall &txTone,\n      FMChannel::Bandwidth bw, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleZone(qint64 idx, const QString &name, bool a, const QList<qint64> &channels,\n                          qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleGPSSystem(qint64 idx, const QString &name, qint64 contactIdx, qint64 period,\n                               qint64 revertChannelIdx, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleAPRSSystem(qint64 idx, const QString &name, qint64 channelIdx, qint64 period,\n                                const QString &src, unsigned srcSSID, const QString &dest, unsigned destSSID, const QString &path,\n                                const QString &icon, const QString &message,\n                                qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleScanList(qint64 idx, const QString &name, qint64 pch1, qint64 pch2, qint64 txch,\n                              const QList<qint64> &channels, qint64 line, qint64 column, QString &errorMessage);\n  virtual bool handleRoamingZone(qint64 idx, const QString &name, const QList<qint64> &channels,\n                                 qint64 line, qint64 column, QString &errorMessage);\n\nprotected:\n  /** If @c true, the reader is in \"link\" mode. */\n  bool _link;\n  /** The configuration to read. */\n  Config *_config;\n  /** Index <-> Channel map. */\n\tQMap<int, Channel *> _channels;\n  /** Index <-> Digital contact map. */\n  QMap<int, DMRContact *> _digital_contacts;\n  /** Index <-> RX group list map. */\n  QMap<int, RXGroupList *> _rxgroups;\n  /** Index <-> Zone map. */\n  QMap<int, Zone *> _zones;\n  /** Index <-> GPS System map. */\n  QMap<int, PositionReportingSystem *> _posSystems;\n  /** Index <-> Scan list map. */\n  QMap<int, ScanList *> _scanlists;\n  /** Index <-> RoamingZone map */\n  QMap<int, RoamingZone *> _roamingZones;\n  /** Index <-> radio ID map */\n  QMap<int, DMRRadioID *> _radioIDs;\n};\n\n#endif // CSVREADER_HH\n"
  },
  {
    "path": "lib/d168uv.cc",
    "content": "#include \"d168uv.hh\"\n#include \"logger.hh\"\n\n#include \"d168uv_limits.hh\"\n#include \"d168uv_codeplug.hh\"\n#include \"d878uv2_callsigndb.hh\"\n\n\nD168UV::D168UV(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"Anytone AT-D168UV\", device, parent), _limits(nullptr)\n{\n  _codeplug = new D168UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D878UV2CallsignDB(this);\n  _satellites = new AnytoneSatelliteConfig(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n  case 0x01:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this);\n    break;\n  case 0x0d:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x0e:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0f:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x10:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x11:\n    _limits = new D168UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Do not check frequency range.\";\n    _limits = new D168UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nD168UV::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nD168UV::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::D168UV, \"d168uv\", \"AT-D168UV\", \"AnyTone\",\n        {AnytoneSTM32Interface::interfaceInfo()});\n}\n"
  },
  {
    "path": "lib/d168uv.hh",
    "content": "/** @defgroup d168uv Anytone AT-D168UV\n * Device specific classes for Anytone AT-D168UVII.\n *\n * @ingroup anytone */\n#ifndef __D168UV2_HH__\n#define __D168UV2_HH__\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n//#include \"d878uv2_callsigndb.hh\"\n\n/** Implements an interface to Anytone AT-D168UVII VHF/UHF 5W DMR (Tier I & II) radios.\n *\n * @ingroup d128uv */\nclass D168UV: public AnytoneRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit D168UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** The limits for the radio. */\n  RadioLimits *_limits;\n};\n\n#endif // __D878UV2_HH__\n"
  },
  {
    "path": "lib/d168uv_codeplug.cc",
    "content": "#include \"d168uv_codeplug.hh\"\n#include \"config.hh\"\n\n#include <QTimeZone>\n#include <QtEndian>\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nD168UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : D878UVCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\nbool\nD168UVCodeplug::ChannelElement::dmrCRCDisabled() const {\n  return getBit(Offset::disableDMRCRC());\n}\n\nvoid\nD168UVCodeplug::ChannelElement::disableDMRCRC(bool disable) {\n  setBit(Offset::disableDMRCRC(), disable);\n}\n\n\nChannel *\nD168UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const {\n  auto ch = D878UVCodeplug::ChannelElement::toChannelObj(ctx);\n  if (nullptr == ch)\n    return ch;\n\n  if (ch->is<DMRChannel>()) {\n    auto ext = ch->as<DMRChannel>()->anytoneChannelExtension();\n    if (nullptr == ext)\n      ch->as<DMRChannel>()->setAnytoneChannelExtension(\n        ext = new AnytoneDMRChannelExtension());\n    ext->enableCRC(! dmrCRCDisabled());\n  }\n\n  return ch;\n}\n\nbool D168UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  if (! D878UVCodeplug::ChannelElement::fromChannelObj(c, ctx))\n    return false;\n\n  if (c->is<DMRChannel>() && c->as<DMRChannel>()->anytoneChannelExtension()) {\n    auto ext = c->as<DMRChannel>()->anytoneChannelExtension();\n    disableDMRCRC(! ext->crcEnabled());\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug::GeneralSettingsElement::TimeZone\n * ******************************************************************************************** */\nQVector<QTimeZone>\nD168UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = {\n  QTimeZone(-43200), QTimeZone(-39600), QTimeZone(-36000), QTimeZone(-32400), QTimeZone(-28800),\n  QTimeZone(-25200), QTimeZone(-21600), QTimeZone(-18000), QTimeZone(-14400), QTimeZone(-12600),\n  QTimeZone(-10800), QTimeZone(- 7200), QTimeZone(- 3600), QTimeZone(     0), QTimeZone(  3600),\n  QTimeZone(  7200), QTimeZone( 10800), QTimeZone( 12600), QTimeZone( 14400), QTimeZone( 16200),\n  QTimeZone( 18000), QTimeZone( 19800), QTimeZone( 20700), QTimeZone( 21600), QTimeZone( 25200),\n  QTimeZone( 28600), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 36000), QTimeZone( 39600),\n  QTimeZone( 43200), QTimeZone( 46800) };\n\nQTimeZone\nD168UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) {\n  if (code >= _timeZones.size())\n    return _timeZones.back();\n  return _timeZones.at(code);\n}\nuint8_t\nD168UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) {\n  if (! _timeZones.contains(zone))\n    return 13; //<- UTC\n  return _timeZones.indexOf(zone);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nD168UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : D878UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\n\nQTimeZone\nD168UVCodeplug::GeneralSettingsElement::gpsTimeZone() const {\n  return TimeZone::decode(getUInt8(Offset::gpsTimeZone()));\n}\n\nvoid\nD168UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) {\n  setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone)); // <- Set to UTC\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug::ExtendedSettingsElement\n * ******************************************************************************************** */\nD168UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)\n  : D878UVCodeplug::ExtendedSettingsElement(ptr, size())\n{\n  // pass...\n}\n\nbool\nD168UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! D878UVCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  setUInt8(Offset::talkerAliasType(), (uint8_t)TalkerAliasType::RadioName);\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug::FixedLocationNamesElement\n * ******************************************************************************************** */\nD168UVCodeplug::FixedLocationNamesElement::FixedLocationNamesElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nD168UVCodeplug::FixedLocationNamesElement::clear() {\n  memset(_data, 0x00, size());\n}\n\n\nQString\nD168UVCodeplug::FixedLocationNamesElement::name(unsigned int n) const {\n  if (n >= Limit::nameCount())\n    return {};\n  return readASCII(Offset::names() + n*Offset::betweenNames(), Limit::nameLength(), 0x00);\n}\n\nvoid\nD168UVCodeplug::FixedLocationNamesElement::setName(unsigned int n, const QString &name) {\n  if (n >= Limit::nameCount())\n    return;\n  writeASCII(Offset::names() + n*Offset::betweenNames(), name, Limit::nameLength(), 0x00);\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D168UVCodeplug\n * ******************************************************************************************** */\nD168UVCodeplug::D168UVCodeplug(const QString &label, QObject *parent)\n  : D878UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nD168UVCodeplug::D168UVCodeplug(QObject *parent)\n  : D878UVCodeplug(\"AnyTone AT-D168UV Codeplug\", parent)\n{\n  // pass...\n}\n\n\nvoid\nD168UVCodeplug::allocateUpdated() {\n  D878UVCodeplug::allocateUpdated();\n\n  // Fixed location names, not yet touched by dmrconf\n  image(0).addElement(Offset::fixedLocationNames(), FixedLocationNamesElement::size());\n\n  // allocate unknown segments\n  image(0).addElement(Offset::unknown_25c2000(), 0x0020);\n  image(0).addElement(Offset::unknown_25c2c80(), 0x0020);\n}\n\n\nbool\nD168UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Encode channels\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    // enable channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n\n    ChannelElement ch(data(addr));\n    ch.fromChannelObj(ctx.get<Channel>(i), ctx);\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n    ext.fromChannelObj(ctx.get<Channel>(i), ctx);\n  }\n  return true;\n}\n\nbool\nD168UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create channels\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n\n    ChannelElement ch(data(addr));\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n\n    if (Channel *obj = ch.toChannelObj(ctx)) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i);\n      ext.updateChannelObj(obj, ctx);\n    }\n  }\n  return true;\n}\n\nbool\nD168UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n\n  // Link channel objects\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(addr));\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n\n    if (ctx.has<Channel>(i)) {\n      ch.linkChannelObj(ctx.get<Channel>(i), ctx);\n      ext.linkChannelObj(ctx.get<Channel>(i), ctx);\n    }\n  }\n  return true;\n}\n\n\nbool\nD168UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err);\n  DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).fromConfig(flags, ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx);\n  return true;\n}\n\nbool\nD168UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n  DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).updateConfig(ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx, err);\n  return true;\n}\n\nbool\nD168UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n\n"
  },
  {
    "path": "lib/d168uv_codeplug.hh",
    "content": "#ifndef D168UV_CODEPLUG_HH\n#define D168UV_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n\nclass Channel;\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\n\n\n/** Represents the device specific binary codeplug for Anytone AT-D878UVII radios.\n *\n * This class only implements the difference to the AT-D878UV codeplug. In fact there is only a\n * difference in the address of the contact ID<->Index map.\n *\n * @ingroup d878uv2 */\nclass D168UVCodeplug : public D878UVCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Impleemnts a channel for the AnyTone AT-D168UV. */\n  class ChannelElement: public D878UVCodeplug::ChannelElement\n  {\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns @c true if the CRC check for DMR channels is disabled. */\n    virtual bool dmrCRCDisabled() const;\n    /** Disables DMR CRC check. */\n    virtual void disableDMRCRC(bool disable);\n\n    /** Constructs a Channel object from this element. */\n    Channel *toChannelObj(Context &ctx) const override;\n    /** Encodes the given channel object. */\n    bool fromChannelObj(const Channel *c, Context &ctx) override;\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: D878UVCodeplug::ChannelElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit disableDMRCRC() { return {34, 3}; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the general config of the radio within the D168UV binary codeplug.\n   * This covers the CPS version 1.07. */\n  class GeneralSettingsElement: public D878UVCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific time zones. */\n    struct TimeZone {\n    public:\n      /** Encodes time zone. */\n      static uint8_t encode(const QTimeZone& zone);\n      /** Decodes time zone. */\n      static QTimeZone decode(uint8_t code);\n\n    protected:\n      /** Vector of possible time-zones. */\n      static QVector<QTimeZone> _timeZones;\n    };\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    QTimeZone gpsTimeZone() const override;\n    void setGPSTimeZone(const QTimeZone &zone) override;\n  };\n\n  /** Represents the extended settings within the D168UV codeplug.\n   * This covers the CPS version 1.07. */\n  class ExtendedSettingsElement: public D878UVCodeplug::ExtendedSettingsElement\n  {\n  protected:\n    /** Encoding of possible talker-alias types. That is the text being sent as talker alias.*/\n    enum class TalkerAliasType {\n      RadioName = 0, CustomText = 1\n    };\n\n  public:\n    /** Constructor. */\n    ExtendedSettingsElement(uint8_t *ptr);\n\n    // Size changed\n    static constexpr unsigned int size() { return 0x100; }\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\n  protected:\n    struct Offset: D878UVCodeplug::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int talkerAliasType() { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n  /** Represents a set of up to 8 fixed location names. The locations are stored in the\n   * APRSSettingsElement. */\n  class FixedLocationNamesElement: public Element\n  {\n  public:\n    /** Constructor. */\n    explicit FixedLocationNamesElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n\n    void clear() override;\n\n    /** Returns the n-th name. */\n    virtual QString name(unsigned int n) const;\n    /** Sets the n-th name. */\n    virtual void setName(unsigned int n, const QString &name);\n\n  public:\n    /** Some limits. */\n    struct Limit: public D878UVCodeplug::Limit {\n      /// Number of names.\n      static constexpr unsigned int nameCount() { return 8; }\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: D878UVCodeplug::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int names() { return 0x0003; }\n      static constexpr unsigned int betweenNames() { return Limit::nameLength() + 1; }\n      /// @endcond\n    };\n  };\n\nprotected:\n  /** Hidden constructor. */\n  explicit D168UVCodeplug(const QString &label, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit D168UVCodeplug(QObject *parent = nullptr);\n\nprotected:\n  void allocateUpdated() override;\n\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) override;\n  bool createChannels(Context &ctx, const ErrorStack &err) override;\n  bool linkChannels(Context &ctx, const ErrorStack &err) override;\n\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\nprotected:\n  /** Internal used offsets within the codeplug. */\n  struct Offset: public D878UVCodeplug::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int fixedLocationNames() { return 0x02504800; }\n    static constexpr unsigned int unknown_25c2000()    { return 0x025c2000; }\n    static constexpr unsigned int unknown_25c2c80()    { return 0x025c2c80; }\n    /// @endcond\n  };\n};\n\n#endif // D168UV_CODEPLUG_HH\n"
  },
  {
    "path": "lib/d168uv_limits.cc",
    "content": "#include \"d168uv_limits.hh\"\n#include \"d878uv2_callsigndb.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nD168UVLimits::D168UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                           const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                           const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V100\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = D878UV2CallsignDB::Limit::entries();\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = true;\n  _satelliteConfigImplemented  = true;\n  _numSatellites               = 25;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n            { \"number\", new RadioLimitString(1, 14, RadioLimitString::DTMF) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/d168uv_limits.hh",
    "content": "#ifndef D168UVLIMITS_HH\n#define D168UVLIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the AnyTone AT-D168UV.\n * @ingroup d878uv2 */\nclass D168UVLimits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  D168UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n               const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n               const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D878UVLIMITS_HH\n"
  },
  {
    "path": "lib/d168uv_satelliteconfig.cc",
    "content": "#include \"d168uv_satelliteconfig.hh\"\n#include \"satellitedatabase.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of D168UVSatelliteConfig\n * ********************************************************************************************* */\nD168UVSatelliteConfig::D168UVSatelliteConfig(QObject *parent)\n  : AnytoneSatelliteConfig{parent}\n{\n  // pass...\n}\n\n\nAnytoneSatelliteConfig::SatelliteElement\nD168UVSatelliteConfig::satellite(unsigned int idx) {\n  return SatelliteElement(data(Offset::satellites() + idx*Offset::betweenSatellites()));\n}\n\n\nbool\nD168UVSatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) {\n  unsigned int numSat = std::min(Limit::satellites(), db->count());\n  image(0).addElement(Offset::satellites(), numSat*SatelliteElement::size());\n\n  for (unsigned int i=0; i<numSat; i++) {\n    logDebug() << \"Encode sat '\" << db->getAt(i).name() << \"' at index \" << i << \".\";\n    if (! satellite(i).encode(db->getAt(i), err)) {\n      errMsg(err) << \"Cannot encode satellite '\" << db->getAt(i).name()\n                  << \"at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/d168uv_satelliteconfig.hh",
    "content": "#ifndef D168UV_SATELLITECONFIG_HH\n#define D168UV_SATELLITECONFIG_HH\n\n#include \"anytone_satelliteconfig.hh\"\n\n/** Implements the satellite configuration for the AnyTone AT-D168UV.\n * @ingroup d168uv */\nclass D168UVSatelliteConfig : public AnytoneSatelliteConfig\n{\nQ_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit D168UVSatelliteConfig(QObject *parent = nullptr);\n\n  /** Returns the satellite element at the specified index. */\n  SatelliteElement satellite(unsigned int idx);\n  bool encode(SatelliteDatabase *db, const ErrorStack &err) override;\n\n  /** Some limits for the satellite config. */\n  struct Limit: AnytoneSatelliteConfig::Limit {\n    /// Number of satellites.\n    static constexpr unsigned int satellites() { return 25; }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset: AnytoneSatelliteConfig::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int satellites() { return 0x2d40000; }\n    /// @endcond\n  };\n};\n\n\n#endif //D168UV_SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/d578uv.cc",
    "content": "#include \"userdatabase.hh\"\n#include \"d578uv.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n\n#include \"d578uv_limits.hh\"\n#include \"d578uv_codeplug.hh\"\n// uses same callsign db as 878\n#include \"d878uv2_callsigndb.hh\"\n\n#define RBSIZE 16\n#define WBSIZE 16\n\n\nD578UV::D578UV(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"Anytone AT-D578UV\", device, parent), _limits(nullptr)\n{\n  _codeplug = new D578UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D878UV2CallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n  case 0x01:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this);\n    break;\n  case 0x0d:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} },info.version, this);\n    break;\n  case 0x0e:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0f:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x10:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x11:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this);\n    break;\n  case 0x12:\n    _limits = new D578UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(222.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code 0x\" << QString::number(int(info.bands), 16)\n              << \": Do not perform a frequency range check.\";\n    _limits = new D578UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nD578UV::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nD578UV::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::D578UV, \"d578uv\", \"AT-D578UV\", \"AnyTone\",\n        { AnytoneGD32Interface::interfaceInfo(),\n          AnytoneSTM32Interface::interfaceInfo() },\n        { RadioInfo(RadioInfo::D578UV, \"d578uv2\", \"AT-D578UVII\", \"AnyTone\",\n          { AnytoneGD32Interface::interfaceInfo(),\n            AnytoneSTM32Interface::interfaceInfo() })});\n}\n"
  },
  {
    "path": "lib/d578uv.hh",
    "content": "/** @defgroup d578uv Anytone AT-D578UV\n * Device specific classes for Anytone AT-D578UV.\n *\n * \\image html d578uv.jpg \"AT-D578UV\" width=200px\n * \\image latex d578uv.jpg \"AT-D578UV\" width=200px\n *\n * @ingroup anytone */\n#ifndef __D578UV_HH__\n#define __D578UV_HH__\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n#include \"d878uv2_callsigndb.hh\"\n\n/** Implements an interface to Anytone AT-D578UV VHF/UHF 50W DMR (Tier I & II) radios.\n *\n * @ingroup d578uv */\nclass D578UV: public AnytoneRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit D578UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** Holds the limits for the radio. */\n  RadioLimits *_limits;\n};\n\n#endif // __D878UV_HH__\n"
  },
  {
    "path": "lib/d578uv_codeplug.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"channel.hh\"\n#include \"config.h\"\n#include \"intermediaterepresentation.hh\"\n\n#include <QTimeZone>\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nD578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)\n  : D878UVCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : D878UVCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::bluetoothEnabled() const {\n  return getBit(Offset::bluetooth());\n}\nvoid\nD578UVCodeplug::ChannelElement::enableBluetooth(bool enable) {\n  setBit(Offset::bluetooth(), enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::roamingEnabled() const {\n  // inverted!\n  return !getBit(Offset::roaming());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableRoaming(bool enable) {\n  // inverted!\n  setBit(Offset::roaming(), !enable);\n}\n\n\nD578UVCodeplug::ChannelElement::InterruptPriority\nD578UVCodeplug::ChannelElement::interruptPriority() const {\n  return (InterruptPriority)getUInt2(Offset::interruptPriority());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::setInterruptPriority(InterruptPriority pri) {\n  setUInt2(Offset::interruptPriority(), (unsigned) pri);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::noiseReductionEnabled() const {\n  return getBit(Offset::noiseReduction());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableNoiseReduction(bool enable) {\n  setBit(Offset::noiseReduction(), enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::multipleKeyEncryption() const {\n  return getBit(Offset::multipleKeyEncryption());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableMultipleKeyEncryption(bool enable) {\n  setBit(Offset::multipleKeyEncryption(), enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::randomKey() const {\n  return getBit(Offset::randomKey());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableRandomKey(bool enable) {\n  setBit(Offset::randomKey(), enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::sms() const {\n  return !getBit(Offset::sms());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableSMS(bool enable) {\n  setBit(Offset::sms(), !enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::dataACK() const {\n  // inverted!\n  return !getBit(Offset::dataACK());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableDataACK(bool enable) {\n  // inverted!\n  setBit(Offset::dataACK(), !enable);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::autoScan() const {\n  return getBit(Offset::autoScan());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableAutoScan(bool enable) {\n  setBit(Offset::autoScan(), enable);\n}\n\nbool\nD578UVCodeplug::ChannelElement::sendTalkerAlias() const {\n  return getBit(Offset::talkerAlias());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::enableSendTalkerAlias(bool enable) {\n  setBit(Offset::talkerAlias(), enable);\n}\n\n\nD878UVCodeplug::ChannelElement::AdvancedEncryptionType\nD578UVCodeplug::ChannelElement::advancedEncryptionType() const {\n  return getBit(Offset::dmrEncryptionType()) ? AdvancedEncryptionType::ARC4 : AdvancedEncryptionType::AES;\n}\n\nvoid\nD578UVCodeplug::ChannelElement::setEncryptionType(AdvancedEncryptionType type) {\n  setBit(Offset::dmrEncryptionType(), AdvancedEncryptionType::ARC4 == type);\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::analogScamblerEnabled() const {\n  return FMScramblerFrequency::Off != (FMScramblerFrequency)getUInt8(Offset::fmScrambler());\n}\n\nFrequency\nD578UVCodeplug::ChannelElement::analogScramblerFrequency() const {\n  switch ((FMScramblerFrequency)getUInt8(Offset::fmScrambler())) {\n  case FMScramblerFrequency::Off: return Frequency();\n  case FMScramblerFrequency::Hz3300: return Frequency::fromHz(3300);\n  case FMScramblerFrequency::Hz3200: return Frequency::fromHz(3200);\n  case FMScramblerFrequency::Hz3100: return Frequency::fromHz(3100);\n  case FMScramblerFrequency::Hz3000: return Frequency::fromHz(3000);\n  case FMScramblerFrequency::Hz2900: return Frequency::fromHz(2900);\n  case FMScramblerFrequency::Hz2800: return Frequency::fromHz(2800);\n  case FMScramblerFrequency::Hz2700: return Frequency::fromHz(2700);\n  case FMScramblerFrequency::Hz2600: return Frequency::fromHz(2600);\n  case FMScramblerFrequency::Hz2500: return Frequency::fromHz(2500);\n  case FMScramblerFrequency::Hz4095: return Frequency::fromHz(4095);\n  case FMScramblerFrequency::Hz3458: return Frequency::fromHz(3458);\n  case FMScramblerFrequency::Custom:\n    return Frequency::fromHz(100*getUInt8(Offset::customScrambler())+1500);\n  }\n  return Frequency();\n}\n\nvoid\nD578UVCodeplug::ChannelElement::setAnalogScamberFrequency(Frequency f) {\n  if (4095 == f.inHz()) {\n    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Hz4095);\n    setUInt8(Offset::customScrambler(), 0);\n  } else if (3458 == f.inHz()) {\n    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Hz3458);\n    setUInt8(Offset::customScrambler(), 0);\n  } else if ((f.inHz() >= 1500) && (f.inHz() <= 4100)) {\n    setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Custom);\n    setUInt8(Offset::customScrambler(), (f.inHz()-1500)/100);\n  } else {\n    clearAnalogScambler();\n  }\n}\n\nvoid\nD578UVCodeplug::ChannelElement::clearAnalogScambler() {\n  setUInt8(Offset::fmScrambler(), (unsigned)FMScramblerFrequency::Off);\n  setUInt8(Offset::customScrambler(), 0);\n}\n\n\nunsigned int\nD578UVCodeplug::ChannelElement::fmAPRSFrequencyIndex() const {\n  return getUInt8(Offset::fmAPRSFrequencyIndex());\n}\n\nvoid\nD578UVCodeplug::ChannelElement::setFMAPRSFrequencyIndex(unsigned int idx) {\n  setUInt8(Offset::fmAPRSFrequencyIndex(), std::min(7U, idx));\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::hasARC4EncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::arc4KeyIndex());\n}\n\nunsigned\nD578UVCodeplug::ChannelElement::arc4EncryptionKeyIndex() const {\n  return getUInt8(Offset::arc4KeyIndex()) - 1;\n}\n\nvoid\nD578UVCodeplug::ChannelElement::setARC4EncryptionKeyIndex(unsigned idx) {\n  setUInt8(Offset::arc4KeyIndex(), idx+1);\n}\n\nvoid\nD578UVCodeplug::ChannelElement::clearARC4EncryptionKeyIndex() {\n  setUInt8(Offset::arc4KeyIndex(), 0);\n}\n\n\nChannel *\nD578UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const {\n  Channel *ch = D878UVCodeplug::ChannelElement::toChannelObj(ctx);\n  if (nullptr == ch)\n    return nullptr;\n\n  // Apply extensions\n  if (FMChannel *fch = ch->as<FMChannel>()) {\n    if (AnytoneFMChannelExtension *ext = fch->anytoneChannelExtension()) {\n      // Common settings\n      ext->enableHandsFree(bluetoothEnabled());\n      // FM specific settings\n      ext->setScramblerFrequency(analogScamblerEnabled() ? analogScramblerFrequency() : Frequency());\n    }\n  } else if (DMRChannel *dch = ch->as<DMRChannel>()) {\n    if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) {\n      // Common settings\n      ext->enableHandsFree(bluetoothEnabled());\n      // DMR specific extensions\n    }\n  }\n\n  // Done.\n  return ch;\n}\n\n\nbool\nD578UVCodeplug::ChannelElement::fromChannelObj(const Channel *ch, Context &ctx) {\n  if (! D878UVCodeplug::ChannelElement::fromChannelObj(ch, ctx))\n    return false;\n\n  // Apply extensions\n  if (const FMChannel *fch = ch->as<FMChannel>()) {\n    if (AnytoneFMChannelExtension *ext = fch->anytoneChannelExtension()) {\n      // Common settings\n      enableBluetooth(ext->handsFree());\n      // FM specific settings\n      if (ext->scramblerFrequency().isZero())\n        clearAnalogScambler();\n      else\n        setAnalogScamberFrequency(ext->scramblerFrequency());\n    }\n  } else if (const DMRChannel *dch = ch->as<DMRChannel>()) {\n    if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) {\n      // Common settings\n      enableBluetooth(ext->handsFree());\n      // DMR specific extensions\n    }\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::ChannelExtensionElement\n * ******************************************************************************************** */\nD578UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *data, unsigned size)\n  : Element(data, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *data)\n  : Element(data, size())\n{\n  // pass...\n}\n\n\nvoid\nD578UVCodeplug::ChannelExtensionElement::clear() {\n  Element::clear();\n  std::memset(_data, 0, size());\n}\n\n\nunsigned int\nD578UVCodeplug::ChannelExtensionElement::fiveToneIdIndexBOT() const {\n  return getUInt8(Offset::fiveToneIdIndexBOT());\n}\n\nvoid\nD578UVCodeplug::ChannelExtensionElement::setFiveToneIdIndexBOT(unsigned int idx) {\n  setUInt8(Offset::fiveToneIdIndexBOT(), idx);\n}\n\n\nunsigned int\nD578UVCodeplug::ChannelExtensionElement::fiveToneIdIndexEOT() const {\n  return getUInt8(Offset::fiveToneIdIndexEOT());\n}\n\nvoid\nD578UVCodeplug::ChannelExtensionElement::setFiveToneIdIndexEOT(unsigned int idx) {\n  setUInt8(Offset::fiveToneIdIndexEOT(), idx);\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::GeneralSettingsElement::KeyFunction\n * ******************************************************************************************** */\nuint8_t\nD578UVCodeplug::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;\n  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;\n  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;\n  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;\n  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;\n  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;\n  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;\n  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;\n  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;\n  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;\n  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;\n  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;\n  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;\n  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;\n  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;\n  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;\n  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;\n  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;\n  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;\n  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;\n  case AnytoneKeySettingsExtension::KeyFunction::Roaming:           return (uint8_t)KeyFunction::Roaming;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;\n  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;\n  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch:    return (uint8_t)KeyFunction::APRSTypeSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Zone:              return (uint8_t)KeyFunction::Zone;\n  case AnytoneKeySettingsExtension::KeyFunction::MuteA:             return (uint8_t)KeyFunction::MuteA;\n  case AnytoneKeySettingsExtension::KeyFunction::MuteB:             return (uint8_t)KeyFunction::MuteB;\n  case AnytoneKeySettingsExtension::KeyFunction::RoamingSet:        return (uint8_t)KeyFunction::RoamingSet;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSet:           return (uint8_t)KeyFunction::APRSSet;\n  case AnytoneKeySettingsExtension::KeyFunction::ZoneUp:            return (uint8_t)KeyFunction::ZoneUp;\n  case AnytoneKeySettingsExtension::KeyFunction::ZoneDown:          return (uint8_t)KeyFunction::ZoneDown;\n  case AnytoneKeySettingsExtension::KeyFunction::Exit:              return (uint8_t)KeyFunction::Exit;\n  case AnytoneKeySettingsExtension::KeyFunction::Menu:              return (uint8_t)KeyFunction::Menu;\n  case AnytoneKeySettingsExtension::KeyFunction::XBandRepeater:     return (uint8_t)KeyFunction::XBandRepeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Speaker:           return (uint8_t)KeyFunction::Speaker;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelName:       return (uint8_t)KeyFunction::ChannelName;\n  case AnytoneKeySettingsExtension::KeyFunction::Bluetooth:         return (uint8_t)KeyFunction::Bluetooth;\n  case AnytoneKeySettingsExtension::KeyFunction::GPS:               return (uint8_t)KeyFunction::GPS;\n  case AnytoneKeySettingsExtension::KeyFunction::CDTScan:           return (uint8_t)KeyFunction::CDTScan;\n  case AnytoneKeySettingsExtension::KeyFunction::TBSTSend:          return (uint8_t)KeyFunction::TBSTSend;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSend:          return (uint8_t)KeyFunction::APRSSend;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSInfo:          return (uint8_t)KeyFunction::APRSInfo;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSRoaming:        return (uint8_t)KeyFunction::GPSRoaming;\n  case AnytoneKeySettingsExtension::KeyFunction::Squelch:           return (uint8_t)KeyFunction::Squelch;\n  case AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX:  return (uint8_t)KeyFunction::NoiseReductionTX;\n  default:                                                          return (uint8_t)KeyFunction::Off;\n  }\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::KeyFunction::decode(uint8_t code) {\n  switch ((KeyFunctionCode)code) {\n  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;\n  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;\n  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;\n  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;\n  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;\n  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;\n  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;\n  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;\n  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;\n  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;\n  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;\n  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;\n  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;\n  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;\n  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;\n  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;\n  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;\n  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;\n  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;\n  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;\n  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;\n  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;\n  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;\n  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;\n  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;\n  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;\n  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;\n  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;\n  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;\n  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;\n  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;\n  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;\n  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;\n  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;\n  case KeyFunction::Roaming:           return AnytoneKeySettingsExtension::KeyFunction::Roaming;\n  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;\n  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;\n  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;\n  case KeyFunction::APRSTypeSwitch:    return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch;\n  case KeyFunction::Zone:              return AnytoneKeySettingsExtension::KeyFunction::Zone;\n  case KeyFunction::MuteA:             return AnytoneKeySettingsExtension::KeyFunction::MuteA;\n  case KeyFunction::MuteB:             return AnytoneKeySettingsExtension::KeyFunction::MuteB;\n  case KeyFunction::RoamingSet:        return AnytoneKeySettingsExtension::KeyFunction::RoamingSet;\n  case KeyFunction::APRSSet:           return AnytoneKeySettingsExtension::KeyFunction::APRSSet;\n  case KeyFunction::ZoneUp:            return AnytoneKeySettingsExtension::KeyFunction::ZoneUp;\n  case KeyFunction::ZoneDown:          return AnytoneKeySettingsExtension::KeyFunction::ZoneDown;\n  case KeyFunction::Exit:              return AnytoneKeySettingsExtension::KeyFunction::Exit;\n  case KeyFunction::Menu:              return AnytoneKeySettingsExtension::KeyFunction::Menu;\n  case KeyFunction::XBandRepeater:     return AnytoneKeySettingsExtension::KeyFunction::XBandRepeater;\n  case KeyFunction::Speaker:           return AnytoneKeySettingsExtension::KeyFunction::Speaker;\n  case KeyFunction::ChannelName:       return AnytoneKeySettingsExtension::KeyFunction::ChannelName;\n  case KeyFunction::Bluetooth:         return AnytoneKeySettingsExtension::KeyFunction::Bluetooth;\n  case KeyFunction::GPS:               return AnytoneKeySettingsExtension::KeyFunction::GPS;\n  case KeyFunction::CDTScan:           return AnytoneKeySettingsExtension::KeyFunction::CDTScan;\n  case KeyFunction::TBSTSend:          return AnytoneKeySettingsExtension::KeyFunction::TBSTSend;\n  case KeyFunction::APRSSend:          return AnytoneKeySettingsExtension::KeyFunction::APRSSend;\n  case KeyFunction::APRSInfo:          return AnytoneKeySettingsExtension::KeyFunction::APRSInfo;\n  case KeyFunction::GPSRoaming:        return AnytoneKeySettingsExtension::KeyFunction::GPSRoaming;\n  case KeyFunction::Squelch:           return AnytoneKeySettingsExtension::KeyFunction::Squelch;\n  case KeyFunction::NoiseReductionTX:  return AnytoneKeySettingsExtension::KeyFunction::NoiseReductionTX;\n  default:                             return AnytoneKeySettingsExtension::KeyFunction::Off;\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::GeneralSettingsElement::TimeZone\n * ******************************************************************************************** */\nQVector<QTimeZone>\nD578UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = {\n  QTimeZone(-43200), QTimeZone(-39600), QTimeZone(-36000), QTimeZone(-32400), QTimeZone(-28800),\n  QTimeZone(-25200), QTimeZone(-21600), QTimeZone(-18000), QTimeZone(-14400), QTimeZone(-12600),\n  QTimeZone(-10800), QTimeZone(- 7200), QTimeZone(- 3600), QTimeZone(     0), QTimeZone(  3600),\n  QTimeZone(  7200), QTimeZone( 10800), QTimeZone( 12600), QTimeZone( 14400), QTimeZone( 16200),\n  QTimeZone( 18000), QTimeZone( 19800), QTimeZone( 20700), QTimeZone( 21600), QTimeZone( 25200),\n  QTimeZone( 28800), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 36000), QTimeZone( 39600),\n  QTimeZone( 43200), QTimeZone( 46800) };\n\nQTimeZone\nD578UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) {\n  if (code >= _timeZones.size())\n    return _timeZones.back();\n  return _timeZones.at(code);\n}\nuint8_t\nD578UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) {\n  if (! _timeZones.contains(zone))\n    return 13; //<- UTC\n  return _timeZones.indexOf(zone);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nD578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : AnytoneCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::keyToneEnabled() const {\n  return 0x00 != getUInt8(Offset::enableKeyTone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) {\n  setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00);\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::transmitTimeout() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::transmitTimeout())*30);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setTransmitTimeout(const Interval &tot) {\n  setUInt8(Offset::transmitTimeout(), tot.seconds()/30);\n}\n\nAnytoneDisplaySettingsExtension::Language\nD578UVCodeplug::GeneralSettingsElement::language() const {\n  return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) {\n  setUInt8(Offset::language(), (unsigned)lang);\n}\n\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::vfoStepSize() const {\n  switch ((VFOStepSize)getUInt8(Offset::vfoStepSize())) {\n  case VFOStepSize::Hz2500:  return Frequency::fromHz(2500);\n  case VFOStepSize::Hz5000:  return Frequency::fromHz(5000);\n  case VFOStepSize::Hz6250:  return Frequency::fromHz(6250);\n  case VFOStepSize::Hz8330:  return Frequency::fromHz(8330);\n  case VFOStepSize::kHz10:   return Frequency::fromkHz(10);\n  case VFOStepSize::Hz12500: return Frequency::fromHz(12500);\n  case VFOStepSize::kHz20:   return Frequency::fromkHz(20);\n  case VFOStepSize::kHz25:   return Frequency::fromkHz(25);\n  case VFOStepSize::kHz30:   return Frequency::fromkHz(30);\n  case VFOStepSize::kHz50:   return Frequency::fromkHz(50);\n  }\n\n  return Frequency::fromHz(2500);\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setVFOStepSize(const Frequency &f) {\n  VFOStepSize stepSize = VFOStepSize::Hz2500;\n  if (f.inHz() <= 2500)       stepSize = VFOStepSize::Hz2500;\n  else if (f.inHz() <= 5000)  stepSize = VFOStepSize::Hz5000;\n  else if (f.inHz() <= 6250)  stepSize = VFOStepSize::Hz6250;\n  else if (f.inHz() <= 8330)  stepSize = VFOStepSize::Hz8330;\n  else if (f.inkHz() <= 10)   stepSize = VFOStepSize::kHz10;\n  else if (f.inHz() <= 12500) stepSize = VFOStepSize::Hz12500;\n  else if (f.inkHz() <= 20)   stepSize = VFOStepSize::kHz20;\n  else if (f.inkHz() <= 25)   stepSize = VFOStepSize::kHz25;\n  else if (f.inkHz() <= 30)   stepSize = VFOStepSize::kHz30;\n  else if (f.inkHz() <= 50)   stepSize = VFOStepSize::kHz50;\n  setUInt8(Offset::vfoStepSize(), (unsigned) stepSize);\n}\n\n\nAnytoneSettingsExtension::VFOScanType\nD578UVCodeplug::GeneralSettingsElement::vfoScanType() const {\n  return (AnytoneSettingsExtension::VFOScanType)getUInt8(Offset::vfoScanType());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setVFOScanType(AnytoneSettingsExtension::VFOScanType type) {\n  setUInt8(Offset::vfoScanType(), (unsigned int) type);\n}\n\n\nLevel\nD578UVCodeplug::GeneralSettingsElement::dmrMicGain() const {\n  return Level::fromValue(getUInt8(Offset::dmrMicGain()), Limit::micGain());\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDMRMicGain(Level gain) {\n  setUInt8(Offset::dmrMicGain(), gain.mapTo(Limit::micGain()));\n}\n\n\nbool\nD578UVCodeplug::GeneralSettingsElement::vfoModeA() const {\n  return getUInt8(Offset::vfoModeA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) {\n  setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::vfoModeB() const {\n  return getUInt8(Offset::vfoModeB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) {\n  setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneSettingsExtension::STEType\nD578UVCodeplug::GeneralSettingsElement::steType() const {\n  return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) {\n  setUInt8(Offset::steType(), (unsigned)type);\n}\ndouble\nD578UVCodeplug::GeneralSettingsElement::steFrequency() const {\n  switch ((STEFrequency)getUInt8(Offset::steFrequency())) {\n  case STEFrequency::Off: return 0;\n  case STEFrequency::Hz55_2: return 55.2;\n  case STEFrequency::Hz259_2: return 259.2;\n  }\n  return 0;\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) {\n  if (0 >= freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off);\n  } else if (100 > freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2);\n  } else {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2);\n  }\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::groupCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) {\n  setUInt8(Offset::groupCallHangTime(), intv.seconds());\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::privateCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) {\n  setUInt8(Offset::privateCallHangTime(), intv.seconds());\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) {\n  setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const {\n  return getUInt8(Offset::wfmChannelIndex());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) {\n  setUInt8(Offset::wfmChannelIndex(), idx);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const {\n  return getUInt8(Offset::wfmVFOEnabled());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) {\n  setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::memoryZoneA() const {\n  return getUInt8(Offset::memZoneA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMemoryZoneA(unsigned zone) {\n  setUInt8(Offset::memZoneA(), zone);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::memoryZoneB() const {\n  return getUInt8(Offset::memZoneB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMemoryZoneB(unsigned zone) {\n  setUInt8(Offset::memZoneB(), zone);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::wfmEnabled() const {\n  return 0x00 != getUInt8(Offset::wfmEnable());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableWFM(bool enable) {\n  setUInt8(Offset::wfmEnable(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::recording() const {\n  return getUInt8(Offset::enableRecoding());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) {\n  setUInt8(Offset::enableRecoding(), (enable ? 0x01 : 0x00));\n}\n\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::brightness() const {\n  return (getUInt8(Offset::displayBrightness())*10)/4;\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBrightness(unsigned level) {\n  setUInt8(Offset::displayBrightness(), (level*4)/10);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::gps() const {\n  return getUInt8(Offset::gpsEnable());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableGPS(bool enable) {\n  setUInt8(Offset::gpsEnable(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::smsAlert() const {\n  return getUInt8(Offset::smsAlert());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableSMSAlert(bool enable) {\n  setUInt8(Offset::smsAlert(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::wfmMonitor() const {\n  return getUInt8(Offset::wfmMonitor());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) {\n  setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::activeChannelB() const {\n  return getUInt8(Offset::activeChannelB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableActiveChannelB(bool enable) {\n  setUInt8(Offset::activeChannelB(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::subChannel() const {\n  return getUInt8(Offset::subChannel());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableSubChannel(bool enable) {\n  setUInt8(Offset::subChannel(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::tbstFrequency() const {\n  switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) {\n  case TBSTFrequency::Hz1000: return Frequency::fromHz(1000);\n  case TBSTFrequency::Hz1450: return Frequency::fromHz(1450);\n  case TBSTFrequency::Hz1750: return Frequency::fromHz(1750);\n  case TBSTFrequency::Hz2100: return Frequency::fromHz(2100);\n  }\n  return Frequency::fromHz(1750);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) {\n  if (1000 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000);\n  } else if (1450 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450);\n  } else if (1750 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  } else if (2100 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100);\n  } else {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  }\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::callAlert() const {\n  return getUInt8(Offset::callAlert());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableCallAlert(bool enable) {\n  setUInt8(Offset::callAlert(), (enable ? 0x01 : 0x00));\n}\n\nQTimeZone\nD578UVCodeplug::GeneralSettingsElement::gpsTimeZone() const {\n  return TimeZone::decode(getUInt8(Offset::gpsTimeZone()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) {\n  setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::dmrTalkPermit() const {\n  return getBit(Offset::talkPermit(), 0);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDMRTalkPermit(bool enable) {\n  return setBit(Offset::talkPermit(), 0, enable);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::fmTalkPermit() const {\n  return getBit(Offset::talkPermit(), 1);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableFMTalkPermit(bool enable) {\n  return setBit(Offset::talkPermit(), 1, enable);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::dmrResetTone() const {\n  return getUInt8(Offset::dmrResetTone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDMRResetTone(bool enable) {\n  return setUInt8(Offset::dmrResetTone(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::idleChannelTone() const {\n  return getUInt8(Offset::idleChannelTone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) {\n  return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::menuExitTime() const {\n  return Interval::fromSeconds(5 + 5*((unsigned) getUInt8(Offset::menuExitTime())));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMenuExitTime(Interval intv) {\n  setUInt8(Offset::menuExitTime(), (std::max(5ULL, intv.seconds())-5)/5);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::filterOwnID() const {\n  return getUInt8(Offset::filterOwnID());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) {\n  setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::startupTone() const {\n  return getUInt8(Offset::startupTone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableStartupTone(bool enable) {\n  return setUInt8(Offset::startupTone(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::callEndPrompt() const {\n  return getUInt8(Offset::callEndPrompt());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableCallEndPrompt(bool enable) {\n  return setUInt8(Offset::callEndPrompt(), (enable ? 0x01 : 0x00));\n}\n\nLevel\nD578UVCodeplug::GeneralSettingsElement::maxSpeakerVolume() const {\n  return Level::fromValue(getUInt8(Offset::maxSpeakerVolume()), Limit::volume());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMaxSpeakerVolume(Level level) {\n  setUInt8(Offset::maxSpeakerVolume(), level.mapTo(Limit::volume()));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::remoteStunKill() const {\n  return getUInt8(Offset::remoteStunKill());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) {\n  setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::remoteMonitor() const {\n  return getUInt8(Offset::remoteMonitor());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) {\n  setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::getGPSPosition() const {\n  return getUInt8(Offset::getGPSPosition());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableGetGPSPosition(bool enable) {\n  return setUInt8(Offset::getGPSPosition(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::longPressDuration() const {\n  return Interval::fromSeconds(((unsigned)getUInt8(Offset::longPressDuration()))+1);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval ms) {\n  setUInt8(Offset::longPressDuration(), std::max(1ULL,ms.seconds())-1);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::volumeChangePrompt() const {\n  return getUInt8(Offset::volumeChangePrompt());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableVolumeChangePrompt(bool enable) {\n  setUInt8(Offset::volumeChangePrompt(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionA() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction) getUInt8(Offset::autoRepeaterDirA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirA(), (unsigned)dir);\n}\n\nAnytoneDMRSettingsExtension::SlotMatch\nD578UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const {\n  return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) {\n  setUInt8(Offset::monSlotMatch(), (unsigned)match);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const {\n  return getUInt8(Offset::monColorCodeMatch());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) {\n  setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::monitorIDMatch() const {\n  return getUInt8(Offset::monIDMatch());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) {\n  setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const {\n  return getUInt8(Offset::monTimeSlotHold());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) {\n  setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneDisplaySettingsExtension::LastCallerDisplayMode\nD578UVCodeplug::GeneralSettingsElement::lastCallerDisplayMode() const {\n  return (AnytoneDisplaySettingsExtension::LastCallerDisplayMode)getUInt8(Offset::lastCallerDisplay());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) {\n  setUInt8(Offset::lastCallerDisplay(), (unsigned)mode);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::fmCallHold() const {\n  return getUInt8(Offset::fmCallHold());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) {\n  setUInt8(Offset::fmCallHold(), sec);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::displayClock() const {\n  return getUInt8(Offset::showClock());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDisplayClock(bool enable) {\n  setUInt8(Offset::showClock(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const {\n  return getUInt8(Offset::enableGPSMessage());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) {\n  setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::enhanceAudio() const {\n  return getUInt8(Offset::enhanceAudio());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableEnhancedAudio(bool enable) {\n  setUInt8(Offset::enhanceAudio(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyUHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanUHF()))*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::minVFOScanUHF(), freq.inHz()/10);\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyUHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanUHF()))*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::maxVFOScanUHF(), freq.inHz()/10);\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyVHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanVHF()))*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::minVFOScanVHF(), freq.inHz()/10);\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyVHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanVHF()))*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::maxVFOScanVHF(), freq.inHz()/10);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexUHF() const {\n  return 0xff != autoRepeaterOffsetFrequencyIndexUHF();\n}\nunsigned\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexUHF() const {\n  return getUInt8(Offset::autoRepOffsetUHF());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) {\n  setUInt8(Offset::autoRepOffsetUHF(), idx);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexUHF() {\n  setAutoRepeaterOffsetFrequenyIndexUHF(0xff);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexVHF() const {\n  return 0xff != autoRepeaterOffsetFrequencyIndexVHF();\n}\nunsigned\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexVHF() const {\n  return getUInt8(Offset::autoRepOffsetVHF());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) {\n  setUInt8(Offset::autoRepOffsetVHF(), idx);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexVHF() {\n  setAutoRepeaterOffsetFrequenyIndexVHF(0xff);\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10);\n}\nFrequency\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10);\n}\n\nFrequency\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10);\n}\nFrequency\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10);\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::callToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::callToneTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::callToneDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setCallToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::callToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::callToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::idleToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double frequency = getUInt16_le(Offset::idleToneTones()+2*i);\n    unsigned int duration  = getUInt16_le(Offset::idleToneDurations()+2*i);\n    if (duration) tones.append({frequency, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setIdleToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::idleToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::idleToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::resetToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double frequency = getUInt16_le(Offset::resetToneTones()+2*i);\n    unsigned int duration  = getUInt16_le(Offset::resetToneDurations()+2*i);\n    if (duration) tones.append({frequency, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setResetToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::resetToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::resetToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const {\n  return getUInt8(Offset::priorityZoneA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneA(), idx);\n}\nunsigned\nD578UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const {\n  return getUInt8(Offset::priorityZoneB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneB(), idx);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::displayCall() const {\n  return getUInt8(Offset::callDisplayMode());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDisplayCall(bool enable) {\n  setUInt8(Offset::callDisplayMode(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::bluetooth() const {\n  return 0 != getUInt8(Offset::bluetooth());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableBluetooth(bool enable) {\n  setUInt8(Offset::bluetooth(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::btAndInternalMic() const {\n  return 0 != getUInt8(Offset::btAndInternalMic());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableBTAndInternalMic(bool enable) {\n  setUInt8(Offset::btAndInternalMic(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::btAndInternalSpeaker() const {\n  return 0 != getUInt8(Offset::btAndInternalSpeaker());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableBTAndInternalSpeaker(bool enable) {\n  setUInt8(Offset::btAndInternalSpeaker(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::pluginRecTone() const {\n  return 0 != getUInt8(Offset::pluginRecTone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enablePluginRecTone(bool enable) {\n  setUInt8(Offset::pluginRecTone(), enable ? 0x01 : 0x00);\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const {\n  return Interval::fromSeconds(getUInt8(Offset::gpsRangingInterval()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) {\n  setUInt8(Offset::gpsRangingInterval(), intv.seconds());\n}\n\nunsigned int\nD578UVCodeplug::GeneralSettingsElement::btMicGain() const {\n  return (getUInt8(Offset::btMicGain())+1)*2;\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBTMicGain(unsigned int gain) {\n  gain = std::min(10U, std::max(2U, gain));\n  setUInt8(Offset::btMicGain(), gain/2-1);\n}\n\nunsigned int\nD578UVCodeplug::GeneralSettingsElement::btSpeakerGain() const {\n  return (getUInt8(Offset::btSpeakerGain())+1)*2;\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBTSpeakerGain(unsigned int gain) {\n  gain = std::min(10U, std::max(2U, gain));\n  setUInt8(Offset::btSpeakerGain(), gain/2-1);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::displayChannelNumber() const {\n  return getUInt8(Offset::showChannelNumber());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDisplayChannelNumber(bool enable) {\n  setUInt8(Offset::showChannelNumber(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::showCurrentContact() const {\n  return getUInt8(Offset::showCurrentContact());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) {\n  setUInt8(Offset::showCurrentContact(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::autoRoamPeriod() const {\n  return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRoamPeriod(Interval intv) {\n  setUInt8(Offset::autoRoamPeriod(), intv.minutes());\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::GeneralSettingsElement::callDisplayColor() const {\n  return NameColor::decode(getUInt8(Offset::callColor()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::callColor(), NameColor::encode(color));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const {\n  return getUInt8(Offset::gpsUnits());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) {\n  setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::knobLock() const {\n  return getBit(Offset::knobLock());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {\n  setBit(Offset::knobLock(), enable);\n}\nbool\nD578UVCodeplug::GeneralSettingsElement::keypadLock() const {\n  return getBit(Offset::keypadLock());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) {\n  setBit(Offset::keypadLock(), enable);\n}\nbool\nD578UVCodeplug::GeneralSettingsElement::sidekeysLock() const {\n  return getBit(Offset::sideKeyLock());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) {\n  setBit(Offset::sideKeyLock(), enable);\n}\nbool\nD578UVCodeplug::GeneralSettingsElement::keyLockForced() const {\n  return getBit(Offset::forceKeyLock());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) {\n  setBit(Offset::forceKeyLock(), enable);\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::autoRoamDelay() const {\n  return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRoamDelay(Interval intv) {\n  setUInt8(Offset::autoRoamDelay(), intv.seconds());\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::GeneralSettingsElement::standbyTextColor() const {\n  return TextColor::decode(getUInt8(Offset::standbyTextColor()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::standbyTextColor(), TextColor::encode(color));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::GeneralSettingsElement::standbyBackgroundColor() const {\n  return TextColor::decode(getUInt8(Offset::standbyBackground()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::standbyBackground(), TextColor::encode(color));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::showLastHeard() const {\n  return getUInt8(Offset::showLastHeard());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) {\n  setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00));\n}\n\n\nSMSExtension::Format\nD578UVCodeplug::GeneralSettingsElement::smsFormat() const {\n  switch ((SMSFormat) getUInt8(Offset::smsFormat())) {\n  case SMSFormat::Motorola: return SMSExtension::Format::Motorola;\n  case SMSFormat::Hytera: return SMSExtension::Format::Hytera;\n  case SMSFormat::DMR: return SMSExtension::Format::DMR;\n  }\n  return SMSExtension::Format::Motorola;\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setSMSFormat(SMSExtension::Format fmt) {\n  switch (fmt) {\n  case SMSExtension::Format::Motorola:\n    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Motorola);\n    break;\n  case SMSExtension::Format::Hytera:\n    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Hytera);\n    break;\n  case SMSExtension::Format::DMR:\n    setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::DMR);\n    break;\n  }\n}\n\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nD578UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirB(), (unsigned)dir);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const {\n  return 0 != getUInt8(Offset::fmSendIDAndContact());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) {\n  setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::defaultChannel() const {\n  return getUInt8(Offset::defaultChannels());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) {\n  setUInt8(Offset::defaultChannels(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const {\n  return getUInt8(Offset::defaultZoneA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned idx) {\n  setUInt8(Offset::defaultZoneA(), idx);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const {\n  return getUInt8(Offset::defaultZoneB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned idx) {\n  setUInt8(Offset::defaultZoneB(), idx);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const {\n  return 0xff == defaultChannelAIndex();\n}\nunsigned\nD578UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const {\n  return getUInt8(Offset::defaultChannelA());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelA(), idx);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() {\n  setDefaultChannelAIndex(0xff);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const {\n  return 0xff == defaultChannelBIndex();\n}\nunsigned\nD578UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const {\n  return getUInt8(Offset::defaultChannelB());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelB(), idx);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() {\n  setDefaultChannelBIndex(0xff);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::defaultRoamingZoneIndex() const {\n  return getUInt8(Offset::defaultRoamingZone());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setDefaultRoamingZoneIndex(unsigned idx) {\n  setUInt8(Offset::defaultRoamingZone(), idx);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::repeaterRangeCheck() const {\n  return getUInt8(Offset::repRangeCheck());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRepeaterRangeCheck(bool enable) {\n  setUInt8(Offset::repRangeCheck(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::repeaterRangeCheckInterval() const {\n  return Interval::fromSeconds(((unsigned)getUInt8(Offset::rangeCheckInterval()))*5);\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckInterval(Interval intv) {\n  setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5);\n}\n\nunsigned\nD578UVCodeplug::GeneralSettingsElement::repeaterRangeCheckCount() const {\n  return getUInt8(Offset::rangeCheckCount());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckCount(unsigned n) {\n  setUInt8(Offset::rangeCheckCount(), n);\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nD578UVCodeplug::GeneralSettingsElement::roamingStartCondition() const {\n  return (AnytoneRoamingSettingsExtension::RoamStart)getUInt8(Offset::roamStartCondition());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) {\n  setUInt8(Offset::roamStartCondition(), (unsigned)cond);\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::separateDisplay() const {\n  return getUInt8(Offset::displaySeparator());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableSeparateDisplay(bool enable) {\n  setUInt8(Offset::displaySeparator(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::keepLastCaller() const {\n  return getUInt8(Offset::keepLastCaller());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) {\n  setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::GeneralSettingsElement::channelNameColor() const {\n  return NameColor::decode(getUInt8(Offset::channelNameColor()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setChannelNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelNameColor(), NameColor::encode(color));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::repeaterCheckNotification() const {\n  return getUInt8(Offset::repCheckNotify());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRepeaterCheckNotification(bool enable) {\n  setUInt8(Offset::repCheckNotify(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::roaming() const {\n  return getUInt8(Offset::roaming());\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::enableRoaming(bool enable) {\n  setUInt8(Offset::roaming(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey3Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey3Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey3Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey3Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey4Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey4Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey4Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey4Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey5Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey5Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey5Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey5Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey6Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey6Short()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey6Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey6Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyAShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyBShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyCShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyDShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyDShort()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyDShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyDShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey3Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey3Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey3Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey3Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey4Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey4Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey4Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey4Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey5Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey5Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey5Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey5Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKey6Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey6Long()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKey6Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey6Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyALong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyBLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyCLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::GeneralSettingsElement::funcKeyDLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyDLong()));\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setFuncKeyDLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyDLong(), KeyFunction::encode(func));\n}\n\n\nunsigned int\nD578UVCodeplug::GeneralSettingsElement::repeaterCheckNumNotifications() const {\n  return getUInt8(Offset::repCheckNumNotify())+1;\n}\nvoid\nD578UVCodeplug::GeneralSettingsElement::setRepeaterCheckNumNotifications(unsigned int n) {\n  n = Limit::repeaterOORNotificationCount().limit(n);\n  setUInt8(Offset::repCheckNumNotify(), n-1);\n}\n\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::txBacklightDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::txBacklightDuration()));\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setTXBacklightDuration(Interval intv) {\n  auto seconds = std::min(30ULL, intv.seconds());\n  setUInt8(Offset::txBacklightDuration(), seconds);\n}\n\n\nbool\nD578UVCodeplug::GeneralSettingsElement::btHoldTimeEnabled() const {\n  return 0x00 != getUInt8(Offset::btHoldTime());\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::btHoldTimeInfinite() const {\n  return 121U == getUInt8(Offset::btHoldTime());\n}\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::btHoldTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::btHoldTime()));\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBTHoldTime(Interval interval) {\n  unsigned int seconds = std::min(120ULL, std::max(1ULL, interval.seconds()));\n  setUInt8(Offset::btHoldTime(), seconds);\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBTHoldTimeInfinite() {\n  setUInt8(Offset::btHoldTime(), 121);\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::disableBTHoldTime() {\n  setUInt8(Offset::btHoldTime(), 0);\n}\n\n\nInterval\nD578UVCodeplug::GeneralSettingsElement::btRXDelay() const {\n  if (0 == getUInt8(Offset::btRXDelay()))\n    return Interval::fromMilliseconds(30);\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::btRXDelay())+1)*500);\n}\n\nvoid\nD578UVCodeplug::GeneralSettingsElement::setBTRXDelay(Interval delay) {\n  if (500 >= delay.milliseconds()) {\n    setUInt8(Offset::btRXDelay(), 0);\n  } else {\n    unsigned int millis = std::min(5500ULL, std::max(500ULL, delay.milliseconds()));\n    setUInt8(Offset::btRXDelay(), (millis-500)/500);\n  }\n}\n\n\nbool\nD578UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  // Encode tone settings\n  enableIdleChannelTone(\n    ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::DMR));\n\n  // Set transmit timeout\n  setTransmitTimeout(ctx.config()->settings()->tot());\n\n  // Set measurement system based on system locale (0x00==Metric)\n  enableGPSUnitsImperial(GNSSSettings::Units::Archaic == ctx.config()->settings()->gnss()->units());\n\n  // DMR settings\n  setGroupCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n  setPrivateCallHangTime(ctx.config()->settings()->dmr()->privateCallHangTime());\n  setSMSFormat(ctx.config()->smsExtension()->format());\n\n\n  // Handle D578UV specific settings\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) { // <- if extension is not set\n    setPriorityZoneAIndex(0xff);\n    setPriorityZoneBIndex(0xff);\n    return true;\n  }\n\n  // Encode boot settings\n  if (ext->bootSettings()->priorityZoneA()->isNull())\n    setPriorityZoneAIndex(0xff);\n  else\n    setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as<Zone>()));\n  if (ext->bootSettings()->priorityZoneB()->isNull())\n    setPriorityZoneBIndex(0xff);\n  else\n    setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as<Zone>()));\n  if (! ext->roamingSettings()->defaultZone()->isNull())\n    setDefaultRoamingZoneIndex(ctx.index(ext->roamingSettings()->defaultZone()->as<RoamingZone>()));\n\n  // Encode key settings\n  enableKnobLock(ext->keySettings()->knobLockEnabled());\n  enableKeypadLock(ext->keySettings()->keypadLockEnabled());\n  enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled());\n  enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled());\n  setFuncKey3Short(ext->keySettings()->funcKey3Short());\n  setFuncKey3Long(ext->keySettings()->funcKey3Long());\n  setFuncKey4Short(ext->keySettings()->funcKey4Short());\n  setFuncKey4Long(ext->keySettings()->funcKey4Long());\n  setFuncKey5Short(ext->keySettings()->funcKey5Short());\n  setFuncKey5Long(ext->keySettings()->funcKey5Long());\n  setFuncKey6Short(ext->keySettings()->funcKey6Short());\n  setFuncKey6Long(ext->keySettings()->funcKey6Long());\n  setFuncKeyDShort(ext->keySettings()->funcKeyDShort());\n  setFuncKeyDLong(ext->keySettings()->funcKeyDLong());\n\n  // Encode display settings\n  setCallDisplayColor(ext->displaySettings()->callColor());\n  setLanguage(ext->displaySettings()->language());\n  enableDisplayChannelNumber(ext->displaySettings()->showChannelNumberEnabled());\n  enableShowCurrentContact(ext->displaySettings()->showContact());\n  setStandbyTextColor(ext->displaySettings()->standbyTextColor());\n  enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled());\n  setChannelNameColor(ext->displaySettings()->channelNameColor());\n  enableShowCurrentContact(ext->displaySettings()->showContact());\n  setTXBacklightDuration(ext->displaySettings()->backlightDurationTX());\n\n  // Encode menu settings\n  enableSeparateDisplay(ext->menuSettings()->separatorEnabled());\n\n  // Encode auto-repeater settings\n  setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB());\n  setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin());\n  setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax());\n  setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin());\n  setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax());\n\n  // Encode DMR settings\n  setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod());\n  enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled());\n  setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch());\n  enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled());\n  enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled());\n  enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled());\n\n  // Encode GPS settings\n  setGPSTimeZone(ext->gpsSettings()->timeZone());\n  enableGPSMessage(ext->gpsSettings()->positionReportingEnabled());\n  setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod());\n\n  // Encode ranging/roaming settings.\n  setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod());\n  setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay());\n  enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled());\n  setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval());\n  setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount());\n  setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition());\n  enableRepeaterCheckNotification(ext->roamingSettings()->notificationEnabled());\n  setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount());\n\n  // Encode other settings\n  enableKeepLastCaller(ext->keepLastCallerEnabled());\n  setSTEType(ext->steType());\n  setSTEFrequency(ext->steFrequency());\n  setTBSTFrequency(ext->tbstFrequency());\n\n  return true;\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  ctx.config()->settings()->setTOT(transmitTimeout());\n\n  // decode tone settings\n  ctx.config()->settings()->tone()->setChannelIdle(\n    idleChannelTone() ? Channel::Type::DMR : Channel::Type::None\n  );\n\n  // decode GNSS settings\n  ctx.config()->settings()->gnss()->setUnits(\n        this->gpsUnitsImperial() ? GNSSSettings::Units::Archaic :\n                                   GNSSSettings::Units::Metric);\n\n  // decode DMR settings\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(this->groupCallHangTime());\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(this->privateCallHangTime());\n  ctx.config()->smsExtension()->setFormat(this->smsFormat());\n\n  // Handle D578UV specific extension\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Decode key settings\n  ext->keySettings()->enableKnobLock(this->knobLock());\n  ext->keySettings()->enableKeypadLock(this->keypadLock());\n  ext->keySettings()->enableSideKeysLock(this->sidekeysLock());\n  ext->keySettings()->enableForcedKeyLock(this->keyLockForced());\n  ext->keySettings()->setFuncKey3Short(funcKey3Short());\n  ext->keySettings()->setFuncKey3Long(funcKey3Long());\n  ext->keySettings()->setFuncKey4Short(funcKey4Short());\n  ext->keySettings()->setFuncKey4Long(funcKey4Long());\n  ext->keySettings()->setFuncKey5Short(funcKey5Short());\n  ext->keySettings()->setFuncKey5Long(funcKey5Long());\n  ext->keySettings()->setFuncKey6Short(funcKey6Short());\n  ext->keySettings()->setFuncKey6Long(funcKey6Long());\n  ext->keySettings()->setFuncKeyDShort(funcKeyDShort());\n  ext->keySettings()->setFuncKeyDLong(funcKeyDLong());\n\n  // Decode display settings\n  ext->displaySettings()->setCallColor(this->callDisplayColor());\n  ext->displaySettings()->setLanguage(this->language());\n  ext->displaySettings()->enableShowChannelNumber(this->displayChannelNumber());\n  ext->displaySettings()->enableShowContact(this->showCurrentContact());\n  ext->displaySettings()->setStandbyTextColor(this->standbyTextColor());\n  ext->displaySettings()->enableShowLastHeard(this->showLastHeard());\n  ext->displaySettings()->setChannelNameColor(this->channelNameColor());\n  ext->displaySettings()->enableShowContact(this->showCurrentContact());\n  ext->displaySettings()->setBacklightDurationTX(this->txBacklightDuration());\n\n  // Decode menu settings\n  ext->menuSettings()->enableSeparator(this->separateDisplay());\n\n  // Decode auto-repeater settings\n  ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB());\n  ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF());\n  ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF());\n  ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF());\n  ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF());\n\n  // Encode dmr settings\n  ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod());\n  ext->dmrSettings()->enableFilterOwnID(this->filterOwnID());\n  ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch());\n  ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch());\n  ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch());\n  ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold());\n\n  // Encode GPS settings\n  ext->gpsSettings()->setTimeZone(this->gpsTimeZone());\n  ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled());\n  ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod());\n\n  // Encode ranging/roaming settings\n  ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod());\n  ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay());\n  ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheck());\n  ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval());\n  ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount());\n  ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition());\n  ext->roamingSettings()->enableNotification(this->repeaterCheckNotification());\n  ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications());\n\n  // Decode other settings\n  ext->enableKeepLastCaller(this->keepLastCaller());\n  ext->setSTEType(this->steType());\n  ext->setSTEFrequency(this->steFrequency());\n  ext->setTBSTFrequency(this->tbstFrequency());\n\n  return true;\n}\n\nbool\nD578UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err))\n    return false;\n\n  AnytoneSettingsExtension *ext = settings->anytoneExtension();\n\n  if (0xff != priorityZoneAIndex()) {\n    if (! ctx.has<Zone>(priorityZoneAIndex())) {\n      errMsg(err) << \"Cannot link priority zone A index \" << priorityZoneAIndex()\n                  << \": Zone with that index not defined.\";\n      return false;\n    }\n    ext->bootSettings()->priorityZoneA()->set(ctx.get<Zone>(priorityZoneAIndex()));\n  }\n  if (0xff != priorityZoneBIndex()) {\n    if (! ctx.has<Zone>(priorityZoneBIndex())) {\n      errMsg(err) << \"Cannot link priority zone B index \" << priorityZoneBIndex()\n                  << \": Zone with that index not defined.\";\n      return false;\n    }\n    ext->bootSettings()->priorityZoneB()->set(ctx.get<Zone>(priorityZoneBIndex()));\n  }\n  if (ctx.has<RoamingZone>(defaultRoamingZoneIndex())) {\n    ext->roamingSettings()->defaultZone()->set(ctx.get<RoamingZone>(this->defaultRoamingZoneIndex()));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::ExtendedSettingsElement\n * ******************************************************************************************** */\nD578UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned int size)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::clear() {\n  AnytoneCodeplug::ExtendedSettingsElement::clear();\n}\n\n\nAnytoneDMRSettingsExtension::TalkerAliasSource\nD578UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const {\n  return (AnytoneDMRSettingsExtension::TalkerAliasSource)getUInt8(Offset::talkerAliasDisplay());\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode) {\n  setUInt8(Offset::talkerAliasDisplay(), (unsigned)mode);\n}\n\n\nDMRSettings::TalkerAliasEncoding\nD578UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const {\n  switch ((TalkerAliasEncoding)getUInt8(Offset::talkerAliasEncoding())) {\n  case TalkerAliasEncoding::ISO7: return DMRSettings::TalkerAliasEncoding::Iso7;\n  case TalkerAliasEncoding::ISO8: return DMRSettings::TalkerAliasEncoding::Iso8;\n  case TalkerAliasEncoding::Unicode: return DMRSettings::TalkerAliasEncoding::Unicode;\n  }\n  return DMRSettings::TalkerAliasEncoding::Iso8;\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding enc) {\n  switch (enc) {\n  case DMRSettings::TalkerAliasEncoding::Iso7:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO7);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Iso8:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO8);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Unicode:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::Unicode);\n    break;\n  }\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::weatherAlarmEnabled() const {\n  return 0 != getUInt8(Offset::weatherAlarm());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableWeatherAlarm(bool enabled) {\n  setUInt8(Offset::weatherAlarm(), enabled ? 0x01 : 0x00);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::repeaterEnabled() const {\n  return 0 != getUInt8(Offset::repeater());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableRepeater(bool enabled) {\n  setUInt8(Offset::repeater(), enabled ? 0x01 : 0x00);\n}\n\n\nAnytoneAudioSettingsExtension::Speaker\nD578UVCodeplug::ExtendedSettingsElement::speaker() const {\n  switch ((Speaker) getUInt8(Offset::speakers())) {\n  case Speaker::Microphone: return AnytoneAudioSettingsExtension::Speaker::Handset;\n  case Speaker::Radio: return AnytoneAudioSettingsExtension::Speaker::Radio;\n  case Speaker::Both: return AnytoneAudioSettingsExtension::Speaker::Both;\n  }\n  return AnytoneAudioSettingsExtension::Speaker::Radio;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setSpeaker(AnytoneAudioSettingsExtension::Speaker speaker) {\n  switch (speaker) {\n  case AnytoneAudioSettingsExtension::Speaker::Handset:\n    setUInt8(Offset::speakers(), (unsigned)Speaker::Microphone);\n    break;\n  case AnytoneAudioSettingsExtension::Speaker::Radio:\n    setUInt8(Offset::speakers(), (unsigned)Speaker::Radio);\n    break;\n  case AnytoneAudioSettingsExtension::Speaker::Both:\n    setUInt8(Offset::speakers(), (unsigned)Speaker::Both);\n    break;\n  }\n}\n\n\nAnytoneAudioSettingsExtension::HandsetSpeakerSource\nD578UVCodeplug::ExtendedSettingsElement::micSpeakerSource() const {\n  switch ((SpeakerSource) getUInt8(Offset::micSpeakerSource())) {\n  case SpeakerSource::MainChannel:\n    return AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel;\n  case SpeakerSource::SubChannel:\n    return AnytoneAudioSettingsExtension::HandsetSpeakerSource::SubChannel;\n  }\n  return AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setMicSpeakerSource(AnytoneAudioSettingsExtension::HandsetSpeakerSource source) {\n  switch (source) {\n  case AnytoneAudioSettingsExtension::HandsetSpeakerSource::MainChannel:\n    setUInt8(Offset::micSpeakerSource(), (unsigned) SpeakerSource::MainChannel);\n    break;\n  case AnytoneAudioSettingsExtension::HandsetSpeakerSource::SubChannel:\n    setUInt8(Offset::micSpeakerSource(), (unsigned) SpeakerSource::SubChannel);\n    break;\n  }\n}\n\n\nGNSSSettings::Systems\nD578UVCodeplug::ExtendedSettingsElement::gnss() const {\n  switch ((GPSMode)getUInt8(Offset::gpsMode())) {\n  case GPSMode::GPS: return GNSSSettings::System::GPS;\n  case GPSMode::Beidou: return GNSSSettings::System::Beidou;\n  case GPSMode::GPS_Beidou: return GNSSSettings::System::GPS | GNSSSettings::System::Beidou;\n  }\n  return GNSSSettings::System::GPS;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setGNSS(GNSSSettings::Systems mode) {\n  if (mode.testFlag(GNSSSettings::System::GPS))\n    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::GPS);\n  if (mode.testFlag(GNSSSettings::System::Beidou))\n    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::Beidou);\n  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou))\n    setUInt8(Offset::gpsMode(), (unsigned)GPSMode::GPS_Beidou);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::bluetoothPTTLatch() const {\n  return getUInt8(Offset::btPTTLatch());\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableBluetoothPTTLatch(bool enable) {\n  setUInt8(Offset::btPTTLatch(), enable ? 0x01 : 0x00);\n}\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::infiniteBluetoothPTTSleepDelay() const {\n  return  bluetoothPTTSleepDelay().isNull();\n}\nInterval\nD578UVCodeplug::ExtendedSettingsElement::bluetoothPTTSleepDelay() const {\n  return Interval::fromMinutes(getUInt8(Offset::btPTTSleepDelay()));\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBluetoothPTTSleepDelay(Interval delay) {\n  unsigned int t = std::min(Limit::maxBluetoothPTTSleepDelay(), (unsigned int)delay.minutes());\n  setUInt8(Offset::btPTTSleepDelay(), t);\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setInfiniteBluetoothPTTSleepDelay() {\n  setBluetoothPTTSleepDelay(Interval::fromMinutes(0));\n}\n\n\nAnytoneSettingsExtension::FanControl\nD578UVCodeplug::ExtendedSettingsElement::fanControl() const {\n  switch ((FanControl) getUInt8(Offset::fanControl())) {\n  case FanControl::PTT: return AnytoneSettingsExtension::FanControl::PTT;\n  case FanControl::Temperature: return AnytoneSettingsExtension::FanControl::Temperature;\n  case FanControl::Both: return AnytoneSettingsExtension::FanControl::Both;\n  }\n  return AnytoneSettingsExtension::FanControl::PTT;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setFanControl(AnytoneSettingsExtension::FanControl ctrl) {\n  switch (ctrl) {\n  case AnytoneSettingsExtension::FanControl::PTT:\n    setUInt8(Offset::fanControl(), (unsigned) FanControl::PTT);\n    break;\n  case AnytoneSettingsExtension::FanControl::Temperature:\n    setUInt8(Offset::fanControl(), (unsigned) FanControl::Temperature);\n    break;\n  case AnytoneSettingsExtension::FanControl::Both:\n    setUInt8(Offset::fanControl(), (unsigned) FanControl::Both);\n    break;\n  }\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::weatherChannelIndex() const {\n  return getUInt8(Offset::weatherChannelIndex());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setWeatherChannelIndex(unsigned int idx) {\n  setUInt8(Offset::weatherChannelIndex(), std::min(Limit::maxWeatherChannelIndex(), idx));\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::infiniteManDialGroupCallHangTime() const {\n  return manDialGroupCallHangTime().isNull();\n}\n\nInterval\nD578UVCodeplug::ExtendedSettingsElement::manDialGroupCallHangTime() const {\n  unsigned int t = getUInt8(Offset::manGrpCallHangTime())+1;\n  if (t<=30)\n    return Interval::fromSeconds(t);\n  else if (31==t)\n    return Interval::fromMinutes(30);\n  return Interval();\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTime(Interval dur) {\n  unsigned int t = dur.seconds();\n  if (t > 30)\n    t = 31;  // = 30min\n  else if (0 == t)\n    t = 32;  // = infinite\n  setUInt8(Offset::manGrpCallHangTime(), t-1);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTimeInfinite() {\n  setManDialGroupCallHangTime(Interval::fromSeconds(0));\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::infiniteManDialPrivateCallHangTime() const {\n  return manDialPrivateCallHangTime().isNull();\n}\n\nInterval\nD578UVCodeplug::ExtendedSettingsElement::manDialPrivateCallHangTime() const {\n  unsigned int t = getUInt8(Offset::manPrivCallHangTime())+1;\n  if (t<=30)\n    return Interval::fromSeconds(t);\n  else if (31==t)\n    return Interval::fromMinutes(30);\n  return Interval();\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTime(Interval dur) {\n  unsigned int t = dur.seconds();\n  if (t > 30)\n    t = 31;  // = 30min\n  else if (0 == t)\n    t = 32;  // = infinite\n  setUInt8(Offset::manPrivCallHangTime(), t-1);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTimeInfinite() {\n  setManDialPrivateCallHangTime(Interval::fromSeconds(0));\n}\n\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::chKnobShortPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::chKnobShortPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setChKnobShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::chKnobShortPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::chKnobLongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::chKnobLongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setChKnobLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::chKnobLongPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::ExtendedSettingsElement::channelBNameColor() const {\n  return NameColor::decode(getUInt8(Offset::channelBNameColor()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelBNameColor(), NameColor::encode(color));\n}\n\n\nAnytoneDMRSettingsExtension::EncryptionType\nD578UVCodeplug::ExtendedSettingsElement::encryption() const {\n  return (AnytoneDMRSettingsExtension::EncryptionType)getUInt8(Offset::encryptionType());\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode) {\n  setUInt8(Offset::encryptionType(), (unsigned int)mode);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::professionalMode() const {\n  return 1 == getUInt8(Offset::uiMode());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableProfessionalMode(bool enable) {\n  setUInt8(Offset::uiMode(), enable ? 0x01 : 0x00);\n}\n\n\nInterval\nD578UVCodeplug::ExtendedSettingsElement::steDuration() const {\n  return Interval::fromMilliseconds((getUInt8(Offset::steDuration())+1)*10);\n}\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setSTEDuration(Interval dur) {\n  unsigned int t = std::max(10U, std::min(1000U, (unsigned int)dur.milliseconds()));\n  setUInt8(Offset::steDuration(), t/10-1);\n}\n\n\nAnytoneAudioSettingsExtension::HandsetType\nD578UVCodeplug::ExtendedSettingsElement::micType() const {\n  switch ((MicType)getUInt8(Offset::micType())) {\n  case MicType::AnyTone: return AnytoneAudioSettingsExtension::HandsetType::Anytone;\n  case MicType::Generic: return AnytoneAudioSettingsExtension::HandsetType::Generic;\n  }\n  return AnytoneAudioSettingsExtension::HandsetType::Anytone;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setMicType(AnytoneAudioSettingsExtension::HandsetType type) {\n  switch (type) {\n  case AnytoneAudioSettingsExtension::HandsetType::Anytone:\n    setUInt8(Offset::micType(), (unsigned) MicType::AnyTone);\n    break;\n  case AnytoneAudioSettingsExtension::HandsetType::Generic:\n    setUInt8(Offset::micType(), (unsigned) MicType::Generic);\n    break;\n  }\n}\n\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::ExtendedSettingsElement::zoneANameColor() const {\n  return NameColor::decode(getUInt8(Offset::zoneANameColor()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneANameColor(), NameColor::encode(color));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD578UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const {\n  return NameColor::decode(getUInt8(Offset::zoneBNameColor()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneBNameColor(), NameColor::encode(color));\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::resetAutoShutdownOnCall() const {\n  return 0x00 != getUInt8(Offset::autoShutdownMode());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableResetAutoShutdownOnCall(bool enable) {\n  setUInt8(Offset::autoShutdownMode(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::showColorCode() const {\n  return getBit(Offset::displayColorCode());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableShowColorCode(bool enable) {\n  setBit(Offset::displayColorCode(), enable);\n}\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::showTimeSlot() const {\n  return getBit(Offset::displayTimeSlot());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableShowTimeSlot(bool enable) {\n  setBit(Offset::displayTimeSlot(), enable);\n}\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::showChannelType() const {\n  return getBit(Offset::displayChannelType());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableShowChannelType(bool enable) {\n  setBit(Offset::displayChannelType(), enable);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::fmIdleTone() const {\n  return 0x01 == getUInt8(Offset::fmIdleTone());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableFMIdleTone(bool enable) {\n  setUInt8(Offset::fmIdleTone(), enable ? 0x01 : 0x00);\n}\n\n\nAnytoneDisplaySettingsExtension::DateFormat\nD578UVCodeplug::ExtendedSettingsElement::dateFormat() const {\n  return (AnytoneDisplaySettingsExtension::DateFormat)getUInt8(Offset::dateFormat());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format) {\n  setUInt8(Offset::dateFormat(), (unsigned int)format);\n}\n\n\nLevel\nD578UVCodeplug::ExtendedSettingsElement::fmMicGain() const {\n  return Level::fromValue(getUInt8(Offset::analogMicGain())+1, Limit::micGain());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setFMMicGain(Level gain) {\n  setUInt8(Offset::analogMicGain(), gain.mapTo(Limit::micGain())-1);\n}\n\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK1ShortPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK1ShortPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK1ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK1ShortPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK2ShortPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK2ShortPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK2ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK2ShortPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK3ShortPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK3ShortPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK3ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK3ShortPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK1LongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK1LongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK1LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK1LongPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK2LongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK2LongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK2LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK2LongPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK3LongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK3LongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK3LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK3LongPressFunction(), KeyFunction::encode(func));\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetMicGain() const {\n  return 2*(getUInt8(Offset::btHSMicGain())+1);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetMicGain(unsigned int gain) {\n  gain = std::min(10U, std::max(1U, gain));\n  setUInt8(Offset::btHSMicGain(), (gain-1)/2);\n}\n\n\nInterval\nD578UVCodeplug::ExtendedSettingsElement::btHandsetBacklightDuration() const {\n  unsigned int val = getUInt8(Offset::btHSBacklightDuration());\n\n  if ((1 <= val) && (5 >= val))\n    return Interval::fromSeconds(5 * val);\n  else if ((6 <= val) && (10 >= val))\n    return Interval::fromMinutes(val-5);\n  else if (11 == val)\n    return Interval::fromMinutes(15);\n  else if (12 == val)\n    return Interval::fromMinutes(35);\n  else if (13 == val)\n    return Interval::fromMinutes(45);\n  else if (14 == val)\n    return Interval::fromMinutes(60);\n\n  return Interval::infinity();\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetBacklightDuration(Interval dur) {\n  if (dur.seconds() < 5) setUInt8(Offset::btHSBacklightDuration(), 1);\n  else if (dur.seconds() < 30) setUInt8(Offset::btHSBacklightDuration(), dur.seconds()/5);\n  else if (dur.minutes() < 1) setUInt8(Offset::btHSBacklightDuration(), 6);\n  else if (dur.minutes() < 10) setUInt8(Offset::btHSBacklightDuration(), 5+dur.minutes());\n  else if (dur.minutes() <= 15) setUInt8(Offset::btHSBacklightDuration(), 11);\n  else if (dur.minutes() <= 35) setUInt8(Offset::btHSBacklightDuration(), 12);\n  else if (dur.minutes() <= 45) setUInt8(Offset::btHSBacklightDuration(), 13);\n  else if (dur.minutes() <= 60) setUInt8(Offset::btHSBacklightDuration(), 14);\n  else setUInt8(Offset::btHSBacklightDuration(), 0);\n}\n\n\nAnytoneKeySettingsExtension::UpDownKeyFunction\nD578UVCodeplug::ExtendedSettingsElement::micUpDownKeyFunction() const {\n  switch ((UpDownKeyFunction)getUInt8(Offset::upDownKeyFunction())) {\n  case UpDownKeyFunction::Channel: return AnytoneKeySettingsExtension::UpDownKeyFunction::Channel;\n  case UpDownKeyFunction::Volume: return AnytoneKeySettingsExtension::UpDownKeyFunction::Volume;\n  }\n  return AnytoneKeySettingsExtension::UpDownKeyFunction::Channel;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setMicUpDownKeyFunction(AnytoneKeySettingsExtension::UpDownKeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::UpDownKeyFunction::Channel:\n    setUInt8(Offset::upDownKeyFunction(), (unsigned)UpDownKeyFunction::Channel);\n    break;\n  case AnytoneKeySettingsExtension::UpDownKeyFunction::Volume:\n    setUInt8(Offset::upDownKeyFunction(), (unsigned)UpDownKeyFunction::Volume);\n    break;\n  }\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::totNotification() const {\n  return 0x00 != getUInt8(Offset::totNotification());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableTOTNotification(bool enable) {\n  setUInt8(Offset::totNotification(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::gpsRoaming() const {\n  return 0x00 != getUInt8(Offset::gpsRoaming());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableGPSRoaming(bool enable) {\n  setUInt8(Offset::gpsRoaming(), enable ? 0x01 : 0x00);\n}\n\n\nAnytoneRepeaterSettingsExtension::ColorCode\nD578UVCodeplug::ExtendedSettingsElement::repColorCodeMatch() const {\n  switch ((RepeaterColorCodeMatch)getUInt8(Offset::repeaterColorCode())) {\n  case RepeaterColorCodeMatch::None: return AnytoneRepeaterSettingsExtension::ColorCode::Ignored;\n  case RepeaterColorCodeMatch::VFO_A: return AnytoneRepeaterSettingsExtension::ColorCode::VFOA;\n  case RepeaterColorCodeMatch::VFO_B: return AnytoneRepeaterSettingsExtension::ColorCode::VFOB;\n  }\n  return AnytoneRepeaterSettingsExtension::ColorCode::Ignored;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setRepColorCodeMatch(AnytoneRepeaterSettingsExtension::ColorCode mode) {\n  switch (mode) {\n  case AnytoneRepeaterSettingsExtension::ColorCode::Ignored:\n    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::None);\n    break;\n  case AnytoneRepeaterSettingsExtension::ColorCode::VFOA:\n    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::VFO_A);\n    break;\n  case AnytoneRepeaterSettingsExtension::ColorCode::VFOB:\n    setUInt8(Offset::repeaterColorCode(), (unsigned) RepeaterColorCodeMatch::VFO_B);\n    break;\n  }\n}\n\n\nAnytoneRepeaterSettingsExtension::TimeSlot\nD578UVCodeplug::ExtendedSettingsElement::repTimeSlotAMatch() const {\n  switch ((RepeaterTimeSlotMatch)getUInt8(Offset::repeaterATimeslot())) {\n  case RepeaterTimeSlotMatch::Any: return AnytoneRepeaterSettingsExtension::TimeSlot::Any;\n  case RepeaterTimeSlotMatch::RX1_TX2: return AnytoneRepeaterSettingsExtension::TimeSlot::TS1;\n  case RepeaterTimeSlotMatch::RX2_TX1: return AnytoneRepeaterSettingsExtension::TimeSlot::TS2;\n  }\n  return AnytoneRepeaterSettingsExtension::TimeSlot::Any;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setRepTimeSlotAMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode) {\n  switch (mode) {\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS1:\n    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::RX1_TX2);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS2:\n    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::RX2_TX1);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::Any:\n  default:\n    setUInt8(Offset::repeaterATimeslot(), (unsigned) RepeaterTimeSlotMatch::Any);\n    break;\n  }\n}\n\nAnytoneRepeaterSettingsExtension::TimeSlot\nD578UVCodeplug::ExtendedSettingsElement::repTimeSlotBMatch() const {\n  switch ((RepeaterTimeSlotMatch)getUInt8(Offset::repeaterBTimeslot())) {\n  case RepeaterTimeSlotMatch::Any: return AnytoneRepeaterSettingsExtension::TimeSlot::Any;\n  case RepeaterTimeSlotMatch::RX1_TX2: return AnytoneRepeaterSettingsExtension::TimeSlot::TS1;\n  case RepeaterTimeSlotMatch::RX2_TX1: return AnytoneRepeaterSettingsExtension::TimeSlot::TS2;\n  }\n  return AnytoneRepeaterSettingsExtension::TimeSlot::Any;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setRepTimeSlotBMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode) {\n  switch (mode) {\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS1:\n    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::RX1_TX2);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS2:\n    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::RX2_TX1);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::Any:\n  default:\n    setUInt8(Offset::repeaterBTimeslot(), (unsigned) RepeaterTimeSlotMatch::Any);\n    break;\n  }\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetSquelch() const {\n  if (0 == getUInt8(Offset::btHSRxNoiseReduction()))\n    return 0;\n  return (getUInt8(Offset::btHSRxNoiseReduction())*10)/9;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetSquelch(unsigned int level) {\n  level = std::min(10U, level);\n  if (1 >= level)\n    setUInt8(Offset::btHSRxNoiseReduction(), level);\n  else\n    setUInt8(Offset::btHSRxNoiseReduction(), (level*9)/10);\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::btHandsetAutoPowerOffEnabled() const {\n  return 0x01 == getUInt8(Offset::btHSShutDown());\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::enableBtHandsetAutoPowerOff(bool enable) {\n  setUInt8(Offset::btHSShutDown(), enable ? 0x01 : 0x00);\n}\n\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK1VeryLongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK1VeryLongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK1VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK1VeryLongPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK2VeryLongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK2VeryLongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK2VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK2VeryLongPressFunction(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD578UVCodeplug::ExtendedSettingsElement::btSK3VeryLongPressFunction() const {\n  return KeyFunction::decode(getUInt8(Offset::btSK3VeryLongPressFunction()));\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtSK3VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::btSK3VeryLongPressFunction(), KeyFunction::encode(func));\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetTxNoiseRedLevel() const {\n  if (0 == getUInt8(Offset::btHSTxNoiseReduction()))\n    return 0;\n  return (getUInt8(Offset::btHSTxNoiseReduction())*10)/9;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetTxNoiseRedLevel(unsigned int level) {\n  level = std::min(10U, level);\n  if (1 >= level)\n    setUInt8(Offset::btHSTxNoiseReduction(), level);\n  else\n    setUInt8(Offset::btHSTxNoiseReduction(), (level*9)/10);\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetVOXLevel() const {\n  return ((getUInt8(Offset::btHSVOXLevel())+1)*10)/9;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetVOXLevel(unsigned int level) {\n  level = std::max(1U, std::min(10U, level));\n  setUInt8(Offset::btHSVOXLevel(), ((level-1)*8)/9);\n}\n\n\nInterval\nD578UVCodeplug::ExtendedSettingsElement::btHandsetVOXDelay() const {\n  return Interval::fromMilliseconds((unsigned int)(1+getUInt8(Offset::btHSVOXDelay()))*500);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetVOXDelay(Interval delay) {\n  auto ms = std::max(500ull, delay.milliseconds());\n  setUInt8(Offset::btHSVOXDelay(), (ms/500)-1);\n}\n\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetVolumeA() const {\n  unsigned int val = getUInt8(Offset::btHSVolumeA());\n  if (0 == val)\n    return 0;\n  return 1+((val-1)*9)/31;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetVolumeA(unsigned int vol) {\n  setUInt8(Offset::btHSVolumeA(), (std::min(10u, vol)*32)/10);\n}\n\nunsigned int\nD578UVCodeplug::ExtendedSettingsElement::btHandsetVolumeB() const {\n  unsigned int val = getUInt8(Offset::btHSVolumeB());\n  if (0 == val)\n    return 0;\n  return 1+((val-1)*9)/31;\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setBtHandsetVolumeB(unsigned int vol) {\n  setUInt8(Offset::btHSVolumeB(), (std::min(10u, vol)*32)/10);\n}\n\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::callEndToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::callEndTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::callEndDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setCallEndToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::callEndTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::callEndDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::allCallToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::allCallTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::allCallDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\n\nvoid\nD578UVCodeplug::ExtendedSettingsElement::setAllCallToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::allCallTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::allCallDurations()+2*i, tones.at(i).second);\n  }\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (! flags.updateCodeplug())\n    this->clear();\n\n  if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  // Encode tone settings\n  enableFMIdleTone(ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::FM));\n  setCallEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());\n\n  // Encode GPS settings\n  setGNSS(ctx.config()->settings()->gnss()->systems());\n  // Encode audio settings\n  if (ctx.config()->settings()->audio()->fmMicGainEnabled())\n    setFMMicGain(ctx.config()->settings()->audio()->fmMicGain());\n  else\n    setFMMicGain(ctx.config()->settings()->audio()->micGain());\n\n  setTalkerAliasEncoding(ctx.config()->settings()->dmr()->talkerAliasEncoding());\n\n  if (nullptr == ctx.config()->settings()->anytoneExtension())\n    return true;\n\n  // Get extension\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n\n  // Encode DMR settings\n  setTalkerAliasSource(ext->dmrSettings()->talkerAliasSource());\n\n  // Some general settings\n  setSTEDuration(ext->steDuration());\n  enableProfessionalMode(ext->proModeEnabled());\n  setFanControl(ext->fan());\n\n  // Key settings\n  setChKnobShortPressFunction(ext->keySettings()->funcKnobShort());\n  setChKnobLongPressFunction(ext->keySettings()->funcKnobLong());\n  setMicUpDownKeyFunction(ext->keySettings()->upDownKeyFunction());\n\n  // Power save settings\n  enableResetAutoShutdownOnCall(ext->powerSaveSettings()->resetAutoShutdownOnCall());\n\n  // Encode tone settings\n  enableTOTNotification(ext->toneSettings()->totNotification());\n\n  // Encode audio settings\n  setSpeaker(ext->audioSettings()->speaker());\n  setMicSpeakerSource(ext->audioSettings()->handsetSpeaker());\n  setMicType(ext->audioSettings()->handsetType());\n\n  // Encode DMR settings\n  setManDialGroupCallHangTime(ext->dmrSettings()->manualGroupCallHangTime());\n  setManDialPrivateCallHangTime(ext->dmrSettings()->manualPrivateCallHangTime());\n  setEncryption(ext->dmrSettings()->encryption());\n\n  // Encode display settings\n  enableShowColorCode(ext->displaySettings()->showColorCode());\n  enableShowTimeSlot(ext->displaySettings()->showTimeSlot());\n  enableShowChannelType(ext->displaySettings()->showChannelType());\n  setDateFormat(ext->displaySettings()->dateFormat());\n\n  // Encode roaming settings\n  enableGPSRoaming(ext->roamingSettings()->gpsRoaming());\n\n  // Encode bluetooth settings\n  enableBluetoothPTTLatch(ext->bluetoothSettings()->pttLatch());\n  setBluetoothPTTSleepDelay(ext->bluetoothSettings()->pttSleepTimer());\n\n  // Encode BT handset settings\n  enableBtHandsetAutoPowerOff(ext->bluetoothSettings()->handset()->shutdownEnabled());\n  setBtHandsetBacklightDuration(ext->bluetoothSettings()->handset()->backlight());\n  setBtHandsetVolumeA(ext->bluetoothSettings()->handset()->volumeLevelA());\n  setBtHandsetVolumeB(ext->bluetoothSettings()->handset()->volumeLevelB());\n  setBtHandsetMicGain(ext->bluetoothSettings()->handset()->micGain());\n  setBtHandsetSquelch(ext->bluetoothSettings()->handset()->squelch());\n  setBtHandsetTxNoiseRedLevel(ext->bluetoothSettings()->handset()->txNoiseReduction());\n  setBtHandsetVOXLevel(ext->bluetoothSettings()->handset()->voxLevel());\n  setBtHandsetVOXDelay(ext->bluetoothSettings()->handset()->voxDelay());\n  setBtSK1ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyAShort());\n  setBtSK2ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyBShort());\n  setBtSK3ShortPressFunction(ext->bluetoothSettings()->handset()->funcKeyCShort());\n  setBtSK1LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyALong());\n  setBtSK2LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyBLong());\n  setBtSK3LongPressFunction(ext->bluetoothSettings()->handset()->funcKeyCLong());\n  setBtSK1VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyAVeryLong());\n  setBtSK2VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyBVeryLong());\n  setBtSK3VeryLongPressFunction(ext->bluetoothSettings()->handset()->funcKeyCVeryLong());\n\n  // Encode repeater settings\n  enableRepeater(ext->repeaterSettings()->enabled());\n  setRepColorCodeMatch(ext->repeaterSettings()->colorCode());\n  setRepTimeSlotAMatch(ext->repeaterSettings()->timeSlot());\n  setRepTimeSlotBMatch(ext->repeaterSettings()->secTimeSlot());\n\n  return true;\n}\n\n\nbool\nD578UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  // Store GPS settings\n  ctx.config()->settings()->gnss()->setSystems(this->gnss());\n\n  // Store audio settings\n  if (ctx.config()->settings()->audio()->micGain() == fmMicGain())\n    ctx.config()->settings()->audio()->disableFMMicGain();\n  else\n    ctx.config()->settings()->audio()->setFMMicGain(fmMicGain());\n\n  // Store tone settings\n  ctx.config()->settings()->tone()->setChannelIdle(\n    ctx.config()->settings()->tone()->channelIdle()\n      | (fmIdleTone() ? Channel::Type::FM : Channel::Type::None));\n  this->callEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());\n\n  // Store DMR settings\n  ctx.config()->config()->settings()->dmr()->setTalkerAliasEncoding(talkerAliasEncoding());\n\n  // Get or add extension if not present\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Store DMR settings\n  ext->dmrSettings()->setTalkerAliasSource(talkerAliasSource());\n\n  // Some general settings\n  ext->setSTEDuration(this->steDuration());\n  ext->enableProMode(this->professionalMode());\n  ext->setFan(this->fanControl());\n\n  // Some key settings\n  ext->keySettings()->setFuncKnobShort(chKnobShortPressFunction());\n  ext->keySettings()->setFuncKnobLong(chKnobLongPressFunction());\n  ext->keySettings()->setUpDownKeyFunction(micUpDownKeyFunction());\n\n  // Some power-save settings\n  ext->powerSaveSettings()->enableResetAutoShutdownOnCall(this->resetAutoShutdownOnCall());\n\n  // Store tone settings\n  ext->toneSettings()->enableTOTNotification(this->totNotification());\n\n  // Store FM mic gain separately, if different\n  ext->audioSettings()->setSpeaker(speaker());\n  ext->audioSettings()->setHandsetSpeaker(micSpeakerSource());\n  ext->audioSettings()->setHandsetType(micType());\n\n  // Store display settings\n  ext->displaySettings()->enableShowColorCode(this->showColorCode());\n  ext->displaySettings()->enableShowTimeSlot(this->showTimeSlot());\n  ext->displaySettings()->enableShowChannelType(this->showChannelType());\n  ext->displaySettings()->setDateFormat(this->dateFormat());\n\n  // Store some DMR settings\n  ext->dmrSettings()->setManualGroupCallHangTime(this->manDialGroupCallHangTime());\n  ext->dmrSettings()->setManualPrivateCallHangTime(this->manDialPrivateCallHangTime());\n  ext->dmrSettings()->setEncryption(this->encryption());\n\n  // Store roaming settings\n  ext->roamingSettings()->enableGPSRoaming(this->gpsRoaming());\n\n  // Store bluetooth settings\n  ext->bluetoothSettings()->enablePTTLatch(this->bluetoothPTTLatch());\n  ext->bluetoothSettings()->setPTTSleepTimer(this->bluetoothPTTSleepDelay());\n\n  // Store BT handset settings\n  ext->bluetoothSettings()->handset()->enableShutdown(this->btHandsetAutoPowerOffEnabled());\n  ext->bluetoothSettings()->handset()->setBacklight(this->btHandsetBacklightDuration());\n  ext->bluetoothSettings()->handset()->setVolumeLevelA(this->btHandsetVolumeA());\n  ext->bluetoothSettings()->handset()->setVolumeLevelB(this->btHandsetVolumeB());\n  ext->bluetoothSettings()->handset()->setMicGain(this->btHandsetMicGain());\n  ext->bluetoothSettings()->handset()->setSquelch(this->btHandsetSquelch());\n  ext->bluetoothSettings()->handset()->setTxNoiseReduction(this->btHandsetTxNoiseRedLevel());\n  ext->bluetoothSettings()->handset()->setVoxLevel(this->btHandsetVOXLevel());\n  ext->bluetoothSettings()->handset()->setVoxDelay(this->btHandsetVOXDelay());\n  ext->bluetoothSettings()->handset()->setFuncKeyAShort(this->btSK1ShortPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyBShort(this->btSK2ShortPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyCShort(this->btSK3ShortPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyALong(this->btSK1LongPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyBLong(this->btSK2LongPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyCLong(this->btSK3LongPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyAVeryLong(this->btSK1VeryLongPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyBVeryLong(this->btSK2VeryLongPressFunction());\n  ext->bluetoothSettings()->handset()->setFuncKeyCVeryLong(this->btSK3VeryLongPressFunction());\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::HotKeySettingsElement\n * ******************************************************************************************** */\nD578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr, size_t size)\n  : AnytoneCodeplug::HotKeySettingsElement(ptr, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::HotKeySettingsElement::HotKeySettingsElement(uint8_t *ptr)\n  : AnytoneCodeplug::HotKeySettingsElement(ptr, HotKeySettingsElement::size())\n{\n  // pass...\n}\n\nuint8_t *\nD578UVCodeplug::HotKeySettingsElement::hotKeySetting(unsigned int n) const {\n  if (n >= Limit::numEntries())\n    return nullptr;\n  return _data + Offset::hotKeySettings() + n*Offset::betweenHotKeySettings();\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::AirBandChannelElement\n * ******************************************************************************************** */\nD578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\n\nD578UVCodeplug::AirBandChannelElement::AirBandChannelElement(uint8_t *ptr)\n  : Element(ptr, AirBandChannelElement::size())\n{\n  // pass...\n}\n\n\nvoid\nD578UVCodeplug::AirBandChannelElement::clear() {\n  memset(_data, 0, _size);\n}\n\n\nFrequency\nD578UVCodeplug::AirBandChannelElement::frequency() const {\n  return Frequency::fromHz(((unsigned long long)getBCD8_be(Offset::frequency()))*10);\n}\n\nvoid\nD578UVCodeplug::AirBandChannelElement::setFrequency(Frequency freq) {\n  setBCD8_be(Offset::frequency(), freq.inHz()/10);\n}\n\n\nQString\nD578UVCodeplug::AirBandChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nD578UVCodeplug::AirBandChannelElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nbool\nD578UVCodeplug::AirBandChannelElement::encode(AMChannel *am, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  if (nullptr == am)\n    return false;\n\n  setName(am->name());\n  setFrequency(am->rxFrequency());\n\n  return true;\n}\n\n\nAMChannel *\nD578UVCodeplug::AirBandChannelElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  auto am = new AMChannel();\n  am->setName(name());\n  am->setRXFrequency(frequency());\n\n  return am;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug::AirBandBitmapElement\n * ******************************************************************************************** */\nD578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD578UVCodeplug::AirBandBitmapElement::AirBandBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, AirBandBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D578UVCodeplug\n * ******************************************************************************************** */\nD578UVCodeplug::D578UVCodeplug(const QString &label, QObject *parent)\n  : D878UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nD578UVCodeplug::D578UVCodeplug(QObject *parent)\n  : D878UVCodeplug(\"AnyTone AT-D578UV Codeplug\", parent)\n{\n  // pass...\n}\n\nbool\nD578UVCodeplug::allocateBitmaps() {\n  if (! D878UVCodeplug::allocateBitmaps())\n    return false;\n\n  image(0).addElement(Offset::airBandChannelBitmap(), AirBandBitmapElement::size());\n  image(0).addElement(Offset::airBandScanBitmap(), AirBandBitmapElement::size());\n\n  return true;\n}\n\n\nvoid\nD578UVCodeplug::setBitmaps(Context &ctx) {\n  D878UVCodeplug::setBitmaps(ctx);\n\n  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap())),\n    air_scan_bitmap(data(Offset::airBandScanBitmap()));\n  unsigned int num_am_channels = std::min(Limit::airBandChannels(), ctx.count<AMChannel>());\n  air_band_bitmap.clear(); air_band_bitmap.enableFirst(num_am_channels);\n  air_scan_bitmap.clear(); air_scan_bitmap.enableFirst(num_am_channels);\n}\n\n\nvoid\nD578UVCodeplug::allocateForEncoding() {\n  // First allocate everything common\n  D878UVCodeplug::allocateForEncoding();\n  this->allocateAirBandChannels();\n}\n\nvoid\nD578UVCodeplug::allocateForDecoding() {\n  // First allocate everything common\n  D878UVCodeplug::allocateForDecoding();\n  this->allocateAirBandChannels();\n}\n\n\nvoid\nD578UVCodeplug::allocateHotKeySettings() {\n  image(0).addElement(Offset::hotKeySettings(), HotKeySettingsElement::size());\n}\n\n\nConfig *\nD578UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  // Apply base preprocessing\n  auto intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot apply preprocessing for D578UV.\";\n    return nullptr;\n  }\n\n  // Split A/B zones into two.\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Split multi-VFO zones.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  // Keep 16bit DMR, 128 bit AES and 256 bit AES keys.\n  EncryptionKeyFilterVisitor filter(\n    { EncryptionKeyFilterVisitor::Filter(BasicEncryptionKey::staticMetaObject, 16, 16),\n      EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 128, 128),\n      EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 256, 256),});\n  if (! filter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot remove unsupported exncryption.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nD578UVCodeplug:: encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! D878UVCodeplug::encodeElements(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode elements for D578UV.\";\n    return false;\n  }\n\n  if (! encodeAirBandChannels(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode AM channels.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nD578UVCodeplug::createElements(Context &ctx, const ErrorStack &err) {\n  if (! D878UVCodeplug::createElements(ctx, err)) {\n    errMsg(err) << \"Cannot create elements for D578UV.\";\n    return false;\n  }\n\n  if (! createAirBandChannels(ctx, err)) {\n    errMsg(err) << \"Cannot create AM channels.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nD578UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  // Encode channels\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    // enable channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                    + idx*ChannelElement::size();\n    ChannelElement ch(data(addr));\n    ch.fromChannelObj(ctx.get<Channel>(i), ctx);\n\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n    ext.clear();\n  }\n  return true;\n}\n\nbool\nD578UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n\n  // Create channels\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (Channel *obj = ch.toChannelObj(ctx)) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i);\n    }\n  }\n  return true;\n}\n\nbool\nD578UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n\n  // Link channel objects\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (ctx.has<Channel>(i))\n      ch.linkChannelObj(ctx.get<Channel>(i), ctx);\n  }\n\n  return true;\n}\n\n\nvoid\nD578UVCodeplug::allocateContacts() {\n  /* Allocate contacts */\n  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));\n  unsigned contactCount=0;\n  for (uint16_t i=0; i<Limit::numContacts(); i++) {\n    if (! contact_bitmap.isEncoded(i))\n      continue;\n    contactCount++;\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*Offset::betweenContactBlocks();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, Offset::betweenContactBlocks());\n      memset(data(addr), 0x00, Offset::betweenContactBlocks());\n    }\n  }\n  if (contactCount) {\n    image(0).addElement(Offset::contactIndex(), align_size(4*contactCount, 16));\n    memset(data(Offset::contactIndex()), 0xff, align_size(4*contactCount, 16));\n    image(0).addElement(Offset::contactIdTable(), align_size(ContactMapElement::size()*(1+contactCount), 16));\n    memset(data(Offset::contactIdTable()), 0xff, align_size(ContactMapElement::size()*(1+contactCount), 16));\n  }\n}\n\nbool\nD578UVCodeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  QVector<DMRContact*> contacts;\n  // Encode contacts and also collect id<->index map\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size();\n    ContactElement con(data(addr));\n    DMRContact *contact = ctx.get<DMRContact>(i);\n    if(! con.fromContactObj(contact, ctx))\n      return false;\n    ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i);\n    contacts.append(contact);\n  }\n  // encode index map for contacts\n  std::sort(contacts.begin(), contacts.end(),\n            [](DMRContact *a, DMRContact *b) {\n    return a->number() < b->number();\n  });\n  for (int i=0; i<contacts.size(); i++) {\n    ContactMapElement el(data(Offset::contactIdTable() + i*ContactMapElement::size()));\n    el.setID(contacts[i]->number(), (DMRContact::GroupCall==contacts[i]->type()));\n    el.setIndex(ctx.index(contacts[i]));\n  }\n  return true;\n}\n\n\nvoid\nD578UVCodeplug::allocateGeneralSettings() {\n  // override allocation of general settings for D878UV code-plug. General settings are larger!\n  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());\n  image(0).addElement(Offset::gpsMessages(), DMRAPRSMessageElement::size());\n  image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size());\n\n}\n\nbool\nD578UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err);\n  DMRAPRSMessageElement(data(Offset::gpsMessages())).fromConfig(flags, ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx);\n  return true;\n}\n\nbool\nD578UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n  DMRAPRSMessageElement(data(Offset::gpsMessages())).updateConfig(ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx);\n  return true;\n}\n\nbool\nD578UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nD578UVCodeplug::allocateAirBandChannels() {\n  // Allocate valid air-band channels\n  AirBandBitmapElement bitmap(data(Offset::airBandChannelBitmap()));\n  for (unsigned int i=0; i<Limit::airBandChannels(); i++)  {\n    if (! bitmap.isEncoded(i))\n      continue;\n    image(0).addElement(Offset::airBandChannels() + i*AirBandChannelElement::size(),\n                        AirBandChannelElement::size());\n  }\n  // allocate air-band VFO channel\n  image(0).addElement(Offset::airBandVFO(), AirBandChannelElement::size());\n  return true;\n}\n\n\nbool\nD578UVCodeplug::encodeAirBandChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap()));\n  for (unsigned int i=0; i<Limit::airBandChannels(); i++) {\n    if (! air_band_bitmap.isEncoded(i))\n      continue;\n    if (! ctx.has<AMChannel>(i)) {\n      errMsg(err) << \"Cannot resolve AM channel at index \" << i << \". Not defined!\";\n      return false;\n    }\n    AirBandChannelElement ch(data(Offset::airBandChannels() + i*AirBandChannelElement::size()));\n    if (! ch.encode(ctx.get<AMChannel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode AM channel at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nD578UVCodeplug::createAirBandChannels(Context &ctx, const ErrorStack &err) {\n  AirBandBitmapElement air_band_bitmap(data(Offset::airBandChannelBitmap()));\n  for (unsigned int i=0; i<Limit::airBandChannels(); i++) {\n    if (! air_band_bitmap.isEncoded(i))\n      continue;\n    AirBandChannelElement ch(data(Offset::airBandChannels() + i*AirBandChannelElement::size()));\n    auto am = ch.decode(ctx, err);\n    if (nullptr == am) {\n      errMsg(err) << \"Cannot decode AM channel at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->channelList()->add(am);\n    ctx.add(am, i);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/d578uv_codeplug.hh",
    "content": "#ifndef D578UV_CODEPLUG_HH\n#define D578UV_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"d878uv_codeplug.hh\"\n\nclass Channel;\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\n\n\n/** Represents the device specific binary codeplug for Anytone AT-D578UV radios.\n *\n * Matches firmware/CPS version 1.21.\n *\n * @ingroup d578uv */\nclass D578UVCodeplug : public D878UVCodeplug\n{\n  Q_OBJECT\n\nprotected:\n  /** Device specific key function encoding and decoding. */\n  struct KeyFunction {\n  public:\n    /** Encodes key function. */\n    static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone);\n    /** Decodes key function. */\n    static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code);\n\n  protected:\n    /** Encoded key functions. */\n    typedef enum {\n      Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04,\n      Encryption = 0x05, Call = 0x06, ToggleVFO = 0x07, Scan = 0x08, WFM = 0x09, Alarm = 0x0a,\n      RecordSwitch = 0x0b, Record = 0x0c, SMS = 0x0d, Dial = 0x0e, GPSInformation=0x0f, Monitor = 0x10,\n      ToggleMainChannel = 0x11, HotKey1 = 0x12, HotKey2 = 0x13, HotKey3 = 0x14, HotKey4 = 0x15,\n      HotKey5 = 0x16, HotKey6 = 0x17, WorkAlone = 0x18, SkipChannel = 0x19, DMRMonitor = 0x1a,\n      SubChannel = 0x1b, PriorityZone = 0x1c, VFOScan = 0x1d, MICSoundQuality = 0x1e,\n      LastCallReply = 0x1f, ChannelType = 0x20, Ranging=0x21, Roaming = 0x22, ChannelRanging = 0x23,\n      MaxVolume = 0x24, Slot = 0x25, APRSTypeSwitch=0x26, Zone = 0x27, MuteA = 0x28, MuteB = 0x29,\n      RoamingSet = 0x2a, APRSSet = 0x2b, ZoneUp = 0x2c, ZoneDown = 0x2d, Exit = 0x2e, Menu = 0x2f,\n      XBandRepeater = 0x30, Speaker = 0x31, ChannelName = 0x32, Bluetooth = 0x33,\n      GPS = 0x34, CDTScan = 0x35, TBSTSend = 0x36, APRSSend = 0x37, APRSInfo = 0x38,\n      GPSRoaming = 0x39, Squelch=0x3a, NoiseReductionTX=0x3b\n    } KeyFunctionCode;\n  };\n\n\npublic:\n  /** Represents the actual channel encoded within the binary code-plug.\n   * Matches firmware/CPS version 1.21. */\n  class ChannelElement: public D878UVCodeplug::ChannelElement\n  {\n  public:\n    /** Possible FM scrambler carrier frequencies. */\n    enum class FMScramblerFrequency {\n      Off = 0, Hz3300 = 1, Hz3200 = 2, Hz3100 = 3, Hz3000 = 4, Hz2900 = 5, Hz2800 = 6, Hz2700 = 7,\n      Hz2600 = 8, Hz2500 = 9, Hz4095 = 10, Hz3458 = 11, Custom = 12\n    };\n\n    /** Possible interrupt priorities. */\n    enum class InterruptPriority {\n      None = 0, Low = 1, High = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns @c true if bluetooth hands-free is enabled. */\n    virtual bool bluetoothEnabled() const;\n    /** Enables/disables hands-free. */\n    virtual void enableBluetooth(bool enable);\n\n    bool roamingEnabled() const override;\n    void enableRoaming(bool enable) override;\n\n    /** Returns the interrupt priority. */\n    virtual InterruptPriority interruptPriority() const;\n    /** Sets the interrupt priority. */\n    virtual void setInterruptPriority(InterruptPriority pri);\n\n    /** Returns @c true if noise reduction is enabled. */\n    virtual bool noiseReductionEnabled() const;\n    /** Enables/disables noise reduction. */\n    virtual void enableNoiseReduction(bool enable);\n\n    bool multipleKeyEncryption() const override;\n    void enableMultipleKeyEncryption(bool enable) override;\n\n    bool randomKey() const override;\n    void enableRandomKey(bool enable) override;\n\n    bool sms() const override;\n    void enableSMS(bool enable) override;\n\n    bool dataACK() const override;\n    void enableDataACK(bool enable) override;\n\n    bool autoScan() const override;\n    void enableAutoScan(bool enable) override;\n\n    bool sendTalkerAlias() const override;\n    void enableSendTalkerAlias(bool enable) override;\n\n    AdvancedEncryptionType advancedEncryptionType() const override;\n    void setEncryptionType(AdvancedEncryptionType type) override;\n\n    /** Returns @c true if the analog scambler is enabled. */\n    virtual bool analogScamblerEnabled() const;\n    /** If enabled, returns the analog scrambler frequency. */\n    virtual Frequency analogScramblerFrequency() const;\n    /** Sets the analog scambler frequency and enables the scrambler. */\n    virtual void setAnalogScamberFrequency(Frequency f);\n    /** Disables the scambler*/\n    virtual void clearAnalogScambler();\n\n    unsigned int fmAPRSFrequencyIndex() const override;\n    void setFMAPRSFrequencyIndex(unsigned int idx) override;\n\n    bool hasARC4EncryptionKeyIndex() const override;\n    unsigned int arc4EncryptionKeyIndex() const override;\n    void setARC4EncryptionKeyIndex(unsigned int index) override;\n    void clearARC4EncryptionKeyIndex() override;\n\n    Channel *toChannelObj(Context &ctx) const override;\n    bool fromChannelObj(const Channel *ch, Context &ctx) override;\n\n  protected:\n    /** Internal offsets within the channel element. */\n    struct Offset: public D878UVCodeplug::ChannelElement::Offset\n    {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit bluetooth()                     { return {0x0034, 2}; }\n      static constexpr Bit noiseReduction()                { return {0x0034, 3}; }\n      static constexpr Bit interruptPriority()             { return {0x0034, 4}; }\n      static constexpr Bit roaming()                       { return {0x0034, 6}; }\n      static constexpr unsigned int fmScrambler()          { return 0x003a; }\n      static constexpr unsigned int customScrambler()      { return 0x003b; }\n      static constexpr Bit multipleKeyEncryption()         { return {0x003d, 0}; }\n      static constexpr Bit randomKey()                     { return {0x003d, 1}; }\n      static constexpr Bit sms()                           { return {0x003d, 2}; }\n      static constexpr Bit dataACK()                       { return {0x003d, 3}; }\n      static constexpr Bit autoScan()                      { return {0x003d, 4}; }\n      static constexpr Bit talkerAlias()                   { return {0x003d, 5}; }\n      static constexpr Bit advancedEncryptionType()        { return {0x003d, 6}; }\n      static constexpr unsigned int fmAPRSFrequencyIndex() { return 0x003e; }\n      static constexpr unsigned int arc4KeyIndex()         { return 0x003f; }\n      // Deleted\n      static constexpr Bit muteFMAPRS()                    { return {0x0000, 0}; }\n      static constexpr Bit ctcssPhaseReversal()            { return {0x0000, 0}; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the a channel extension element within the binary codeplug.\n   * Matches firmware/CPS version 1.21. */\n  class ChannelExtensionElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelExtensionElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor from data. */\n    ChannelExtensionElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    void clear() override;\n\n    /** Returns the index of the 5-tone ID send at the start of the transmission. */\n    virtual unsigned int fiveToneIdIndexBOT() const;\n    /** Sets the index of the 5-tone ID send at the start of the transmission. */\n    virtual void setFiveToneIdIndexBOT(unsigned int idx);\n\n    /** Returns the index of the 5-tone ID send at the end of the transmission. */\n    virtual unsigned int fiveToneIdIndexEOT() const;\n    /** Sets the index of the 5-tone ID send at the end of the transmission. */\n    virtual void setFiveToneIdIndexEOT(unsigned int idx);\n\n  protected:\n    /** Internal offsets within element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int fiveToneIdIndexBOT() { return 0x0000; }\n      static constexpr unsigned int fiveToneIdIndexEOT() { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the general config of the radio within the D578UV binary codeplug.\n   * This covers the CPS version 1.21. */\n  class GeneralSettingsElement: public AnytoneCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific time zones. */\n    struct TimeZone {\n    public:\n      /** Encodes time zone. */\n      static uint8_t encode(const QTimeZone& zone);\n      /** Decodes time zone. */\n      static QTimeZone decode(uint8_t code);\n\n    protected:\n      /** Vector of possible time-zones. */\n      static QVector<QTimeZone> _timeZones;\n    };\n\n    /** TBST (open repeater) frequencies. */\n    enum class TBSTFrequency {\n      Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3\n    };\n\n    /** All possible STE (squelch tail eliminate) frequencies. */\n    enum class STEFrequency {\n      Off = 0, Hz55_2  = 1, Hz259_2 = 2\n    };\n\n    /** Encoded VFO step sizes. */\n    enum class VFOStepSize {\n      Hz2500 = 0, Hz5000 = 1, Hz6250 = 2, Hz8330 = 3, kHz10 = 4, Hz12500 = 5, kHz20 = 6,\n      kHz25 = 7, kHz30 = 8, kHz50=9\n    };\n\n    /** Possible SMS formats. */\n    enum class SMSFormat {\n      Motorola = 0, Hytera = 1, DMR = 2\n    };\n\n\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00f0; }\n\n    bool keyToneEnabled() const override;\n    void enableKeyTone(bool enable) override;\n\n    /** Returns the transmit timeout in seconds. */\n    virtual Interval transmitTimeout() const;\n    /** Sets the transmit timeout in seconds. */\n    virtual void setTransmitTimeout(const Interval &tot);\n\n    /** Returns the UI language. */\n    virtual AnytoneDisplaySettingsExtension::Language language() const;\n    /** Sets the UI language. */\n    virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang);\n\n    /** Returns the VFO step size. */\n    virtual Frequency vfoStepSize() const;\n    /** Sets the VFO step size. */\n    virtual void setVFOStepSize(const Frequency &f);\n\n    AnytoneSettingsExtension::VFOScanType vfoScanType() const override;\n    void setVFOScanType(AnytoneSettingsExtension::VFOScanType type) override;\n\n    Level dmrMicGain() const override;\n    void setDMRMicGain(Level gain) override;\n\n    bool vfoModeA() const override;\n    void enableVFOModeA(bool enable) override;\n    bool vfoModeB() const override;\n    void enableVFOModeB(bool enable) override;\n\n    /** Returns the STE (squelch tail eliminate) type. */\n    virtual AnytoneSettingsExtension::STEType steType() const;\n    /** Sets the STE (squelch tail eliminate) type. */\n    virtual void setSTEType(AnytoneSettingsExtension::STEType type);\n    /** Returns the STE (squelch tail eliminate) frequency setting in Hz.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual double steFrequency() const;\n    /** Sets the STE (squelch tail eliminate) frequency setting.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual void setSTEFrequency(double freq);\n\n    /** Returns the group call hang time in seconds. */\n    virtual Interval groupCallHangTime() const;\n    /** Sets the group call hang time in seconds. */\n    virtual void setGroupCallHangTime(Interval sec);\n    /** Returns the private call hang time in seconds. */\n    virtual Interval privateCallHangTime() const;\n    /** Sets the private call hang time in seconds. */\n    virtual void setPrivateCallHangTime(Interval sec);\n\n    /** Returns the wake head-period in ms. */\n    virtual Interval wakeHeadPeriod() const;\n    /** Sets the wake head-period in ms. */\n    virtual void setWakeHeadPeriod(Interval ms);\n\n    /** Returns the wide-FM (broadcast) channel index. */\n    virtual unsigned wfmChannelIndex() const;\n    /** Sets the wide-FM (broadcast) channel index. */\n    virtual void setWFMChannelIndex(unsigned idx);\n    /** Returns @c true if the WFM RX is in VFO mode. */\n    virtual bool wfmVFOEnabled() const;\n    /** Enables/disables VFO mode for WFM RX. */\n    virtual void enableWFMVFO(bool enable);\n\n    unsigned memoryZoneA() const override;\n    void setMemoryZoneA(unsigned zone) override;\n    unsigned memoryZoneB() const override;\n    void setMemoryZoneB(unsigned zone) override;\n\n    /** Returns @c true, if the WFM/Airband receiver is enabled. */\n    virtual bool wfmEnabled() const;\n    /** Enables/disables WFM/Airband receiver. */\n    virtual void enableWFM(bool enable);\n\n    bool recording() const override;\n    void enableRecording(bool enable) override;\n\n    unsigned brightness() const override;\n    void setBrightness(unsigned level) override;\n\n    bool gps() const override;\n    void enableGPS(bool enable) override;\n\n    bool smsAlert() const override;\n    void enableSMSAlert(bool enable) override;\n\n    /** Returns @c true if WFM monitor is enabled. */\n    virtual bool wfmMonitor() const;\n    /** Enables/disables WFM monitor. */\n    virtual void enableWFMMonitor(bool enable);\n\n    bool activeChannelB() const override;\n    void enableActiveChannelB(bool enable) override;\n\n    bool subChannel() const override;\n    void enableSubChannel(bool enable) override;\n\n    /** Returns the TBST frequency. */\n    virtual Frequency tbstFrequency() const;\n    /** Sets the TBST frequency. */\n    virtual void setTBSTFrequency(Frequency freq);\n\n    bool callAlert() const override;\n    void enableCallAlert(bool enable) override;\n\n    QTimeZone gpsTimeZone() const override;\n    void setGPSTimeZone(const QTimeZone &zone) override;\n\n    bool dmrTalkPermit() const override;\n    void enableDMRTalkPermit(bool enable) override;\n    bool fmTalkPermit() const override;\n    void enableFMTalkPermit(bool enable) override;\n    bool dmrResetTone() const override;\n    void enableDMRResetTone(bool enable) override;\n    bool idleChannelTone() const override;\n    void enableIdleChannelTone(bool enable) override;\n\n    Interval menuExitTime() const override;\n    void setMenuExitTime(Interval intv) override;\n\n    /** Returns @c true if the own ID is filtered in call lists. */\n    virtual bool filterOwnID() const;\n    /** Enables/disables filter of own ID in call lists. */\n    virtual void enableFilterOwnID(bool enable);\n\n    bool startupTone() const override;\n    void enableStartupTone(bool enable) override;\n\n    bool callEndPrompt() const override;\n    void enableCallEndPrompt(bool enable) override;\n\n    Level maxSpeakerVolume() const override;\n    void setMaxSpeakerVolume(Level level) override;\n\n    /** Returns @c true remote stun/kill is enabled. */\n    virtual bool remoteStunKill() const;\n    /** Enables/disables remote stun/kill. */\n    virtual void enableRemoteStunKill(bool enable);\n\n    /** Returns @c true remote monitor is enabled. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables remote monitor. */\n    virtual void enableRemoteMonitor(bool enable);\n\n    bool getGPSPosition() const override;\n    void enableGetGPSPosition(bool enable) override;\n\n    Interval longPressDuration() const override;\n    void setLongPressDuration(Interval ms) override;\n\n    bool volumeChangePrompt() const override;\n    void enableVolumeChangePrompt(bool enable) override;\n\n    AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const override;\n    void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) override;\n\n    /** Returns the monitor slot match. */\n    virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const;\n    /** Sets the monitor slot match. */\n    virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match);\n    /** Returns @c true if the monitor matches color code. */\n    virtual bool monitorColorCodeMatch() const;\n    /** Enables/disables monitor color code match. */\n    virtual void enableMonitorColorCodeMatch(bool enable);\n    /** Returns @c true if the monitor matches ID. */\n    virtual bool monitorIDMatch() const;\n    /** Enables/disables monitor ID match. */\n    virtual void enableMonitorIDMatch(bool enable);\n    /** Returns @c true if the monitor holds the time slot. */\n    virtual bool monitorTimeSlotHold() const;\n    /** Enables/disables monitor time slot hold. */\n    virtual void enableMonitorTimeSlotHold(bool enable);\n\n    AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const override;\n    void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) override;\n\n    /** Returns the analog call hold in seconds. */\n    virtual unsigned fmCallHold() const;\n    /** Sets the analog call hold in seconds. */\n    virtual void setFMCallHold(unsigned sec);\n\n    bool displayClock() const override;\n    void enableDisplayClock(bool enable) override;\n\n    /** Returns @c true if the GPS range reporting is enabled. */\n    virtual bool gpsMessageEnabled() const;\n    /** Enables/disables GPS range reporting. */\n    virtual void enableGPSMessage(bool enable);\n\n    bool enhanceAudio() const override;\n    void enableEnhancedAudio(bool enable) override;\n\n    Frequency minVFOScanFrequencyUHF() const override;\n    void setMinVFOScanFrequencyUHF(Frequency hz) override;\n    Frequency maxVFOScanFrequencyUHF() const override;\n    void setMaxVFOScanFrequencyUHF(Frequency hz) override;\n    Frequency minVFOScanFrequencyVHF() const override;\n    void setMinVFOScanFrequencyVHF(Frequency hz) override;\n    Frequency maxVFOScanFrequencyVHF() const override;\n    void setMaxVFOScanFrequencyVHF(Frequency hz) override;\n\n    bool hasAutoRepeaterOffsetFrequencyIndexUHF() const override;\n    unsigned autoRepeaterOffsetFrequencyIndexUHF() const override;\n    void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) override;\n    void clearAutoRepeaterOffsetFrequencyIndexUHF() override;\n    bool hasAutoRepeaterOffsetFrequencyIndexVHF() const override;\n    unsigned autoRepeaterOffsetFrequencyIndexVHF() const override;\n    void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) override;\n    void clearAutoRepeaterOffsetFrequencyIndexVHF() override;\n    Frequency autoRepeaterMinFrequencyVHF() const override;\n    void setAutoRepeaterMinFrequencyVHF(Frequency Hz) override;\n    Frequency autoRepeaterMaxFrequencyVHF() const override;\n    void setAutoRepeaterMaxFrequencyVHF(Frequency Hz) override;\n    Frequency autoRepeaterMinFrequencyUHF() const override;\n    void setAutoRepeaterMinFrequencyUHF(Frequency Hz) override;\n    Frequency autoRepeaterMaxFrequencyUHF() const override;\n    void setAutoRepeaterMaxFrequencyUHF(Frequency Hz) override;\n\n    void callToneMelody(Melody &melody) const override;\n    void setCallToneMelody(const Melody &melody) override;\n    void idleToneMelody(Melody &melody) const override;\n    void setIdleToneMelody(const Melody &melody) override;\n    void resetToneMelody(Melody &melody) const override;\n    void setResetToneMelody(const Melody &melody) override;\n\n    /** Returns the priority Zone A index. */\n    virtual unsigned priorityZoneAIndex() const;\n    /** Sets the priority zone A index. */\n    virtual void setPriorityZoneAIndex(unsigned idx);\n    /** Returns the priority Zone B index. */\n    virtual unsigned priorityZoneBIndex() const;\n    /** Sets the priority zone B index. */\n    virtual void setPriorityZoneBIndex(unsigned idx);\n\n    bool displayCall() const override;\n    void enableDisplayCall(bool enable) override;\n\n    /** Returns @c true if bluetooth is enabled. */\n    virtual bool bluetooth() const;\n    /** Enables/disables bluetooth. */\n    virtual void enableBluetooth(bool enable);\n\n    /** Returns @c true if the internal mic is additionally active when BT is active. */\n    virtual bool btAndInternalMic() const;\n    /** Enables/disables the internal mic when BT is active. */\n    virtual void enableBTAndInternalMic(bool enable);\n\n    /** Returns @c true if the internal speaker is additionally active when BT is active. */\n    virtual bool btAndInternalSpeaker() const;\n    /** Enables/disables the internal speaker when BT is active. */\n    virtual void enableBTAndInternalSpeaker(bool enable);\n\n    /** Returns @c true if the plug-in record tone is enabled. */\n    virtual bool pluginRecTone() const;\n    /** Enables/disables the plug-in record tone. */\n    virtual void enablePluginRecTone(bool enable);\n\n    /** Returns the GPS ranging interval in seconds. */\n    virtual Interval gpsUpdatePeriod() const;\n    /** Sets the GPS ranging interval in seconds. */\n    virtual void setGPSUpdatePeriod(Interval sec);\n\n    /** Returns the bluetooth microphone gain [1,10]. */\n    virtual unsigned int btMicGain() const;\n    /** Sets the bluetooth microphone gain [1,10]. */\n    virtual void setBTMicGain(unsigned int gain);\n    /** Returns the bluetooth speaker gain [1,10]. */\n    virtual unsigned int btSpeakerGain() const;\n    /** Sets the bluetooth speaker gain [1,10]. */\n    virtual void setBTSpeakerGain(unsigned int gain);\n\n    /** Returns @c true if the channel number is displayed. */\n    virtual bool displayChannelNumber() const;\n    /** Enables/disables display of channel number. */\n    virtual void enableDisplayChannelNumber(bool enable);\n\n    bool showCurrentContact() const override;\n    void enableShowCurrentContact(bool enable) override;\n\n    /** Returns the auto roaming period in minutes. */\n    virtual Interval autoRoamPeriod() const;\n    /** Sets the auto roaming period in minutes. */\n    virtual void setAutoRoamPeriod(Interval min);\n\n    AnytoneDisplaySettingsExtension::Color callDisplayColor() const override;\n    void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) override;\n\n    bool gpsUnitsImperial() const override;\n    void enableGPSUnitsImperial(bool enable) override;\n\n    bool knobLock() const override;\n    void enableKnobLock(bool enable) override;\n    bool keypadLock() const override;\n    void enableKeypadLock(bool enable) override;\n    bool sidekeysLock() const override;\n    void enableSidekeysLock(bool enable) override;\n    bool keyLockForced() const override;\n    void enableKeyLockForced(bool enable) override;\n\n    /** Returns the auto-roam delay in seconds. */\n    virtual Interval autoRoamDelay() const;\n    /** Sets the auto-roam delay in seconds. */\n    virtual void setAutoRoamDelay(Interval sec);\n\n    /** Returns the standby text color. */\n    virtual AnytoneDisplaySettingsExtension::Color standbyTextColor() const;\n    /** Sets the standby text color. */\n    virtual void setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color);\n    /** Returns the standby image color. */\n    virtual AnytoneDisplaySettingsExtension::Color standbyBackgroundColor() const;\n    /** Sets the standby image color. */\n    virtual void setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color);\n\n    bool showLastHeard() const override;\n    void enableShowLastHeard(bool enable) override;\n\n    /** Returns the SMS format. */\n    virtual SMSExtension::Format smsFormat() const;\n    /** Sets the SMS format. */\n    virtual void setSMSFormat(SMSExtension::Format fmt);\n\n    AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const override;\n    void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) override;\n\n    /** If enabled, the FM ID is sent together with selected contact. */\n    virtual bool fmSendIDAndContact() const;\n    /** Enables/disables sending contact with FM ID. */\n    virtual void enableFMSendIDAndContact(bool enable);\n\n    bool defaultChannel() const override;\n    void enableDefaultChannel(bool enable) override;\n    unsigned defaultZoneIndexA() const override;\n    void setDefaultZoneIndexA(unsigned idx) override;\n    unsigned defaultZoneIndexB() const override;\n    void setDefaultZoneIndexB(unsigned idx) override;\n    bool defaultChannelAIsVFO() const override;\n    unsigned defaultChannelAIndex() const override;\n    void setDefaultChannelAIndex(unsigned idx) override;\n    void setDefaultChannelAToVFO() override;\n    bool defaultChannelBIsVFO() const override;\n    unsigned defaultChannelBIndex() const override;\n    void setDefaultChannelBIndex(unsigned idx) override;\n    void setDefaultChannelBToVFO() override;\n\n    /** Returns the default roaming zone index. */\n    virtual unsigned defaultRoamingZoneIndex() const;\n    /** Sets the default roaming zone index. */\n    virtual void setDefaultRoamingZoneIndex(unsigned idx);\n\n    /** Returns @c true if repeater range check is enabled. */\n    virtual bool repeaterRangeCheck() const;\n    /** Enables/disables repeater range check. */\n    virtual void enableRepeaterRangeCheck(bool enable);\n    /** Returns the repeater range check period in seconds. */\n    virtual Interval repeaterRangeCheckInterval() const;\n    /** Sets the repeater range check interval in seconds. */\n    virtual void setRepeaterRangeCheckInterval(Interval sec);\n    /** Returns the number of repeater range checks. */\n    virtual unsigned repeaterRangeCheckCount() const;\n    /** Sets the number of repeater range checks. */\n    virtual void setRepeaterRangeCheckCount(unsigned n);\n    /** Returns the roaming start condition. */\n    virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const;\n    /** Sets the roaming start condition. */\n    virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond);\n\n    /** Returns @c true if the \"separate display\" is enabled. */\n    virtual bool separateDisplay() const;\n    /** Enables/disables \"separate display. */\n    virtual void enableSeparateDisplay(bool enable);\n\n    bool keepLastCaller() const override;\n    void enableKeepLastCaller(bool enable) override;\n\n    /** Returns the channel name color. */\n    virtual AnytoneDisplaySettingsExtension::Color channelNameColor() const;\n    /** Sets the channel name color. */\n    virtual void setChannelNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns @c true if repeater check notification is enabled. */\n    virtual bool repeaterCheckNotification() const;\n    /** Enables/disables repeater check notification. */\n    virtual void enableRepeaterCheckNotification(bool enable);\n\n    /** Returns @c true if roaming is enabled. */\n    virtual bool roaming() const;\n    /** Enables/disables repeater check notification. */\n    virtual void enableRoaming(bool enable);\n\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const override;\n    void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const override;\n    void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    /** Returns the function for programmable function key 3 short press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey3Short() const;\n    /** Sets the function for programmable function key 3 short press. */\n    virtual void setFuncKey3Short(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 4 short press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey4Short() const;\n    /** Sets the function for programmable function key 4 short press. */\n    virtual void setFuncKey4Short(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 5 short press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey5Short() const;\n    /** Sets the function for programmable function key 5 short press. */\n    virtual void setFuncKey5Short(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 6 short press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey6Short() const;\n    /** Sets the function for programmable function key 6 short press. */\n    virtual void setFuncKey6Short(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const override;\n    void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const override;\n    void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const override;\n    void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    /** Returns the function for programmable function key D short press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyDShort() const;\n    /** Sets the function for programmable function key D short press. */\n    virtual void setFuncKeyDShort(AnytoneKeySettingsExtension::KeyFunction func);\n\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const override;\n    void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const override;\n    void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n    /** Returns the function for programmable function key 3 long press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey3Long() const;\n    /** Sets the function for programmable function key 3 long press. */\n    virtual void setFuncKey3Long(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 4 long press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey4Long() const;\n    /** Sets the function for programmable function key 4 long press. */\n    virtual void setFuncKey4Long(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 5 long press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey5Long() const;\n    /** Sets the function for programmable function key 5 long press. */\n    virtual void setFuncKey5Long(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the function for programmable function key 6 long press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKey6Long() const;\n    /** Sets the function for programmable function key 6 long press. */\n    virtual void setFuncKey6Long(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const override;\n    void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const override;\n    void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const override;\n    void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    /** Returns the function for programmable function key D long press. */\n    virtual AnytoneKeySettingsExtension::KeyFunction funcKeyDLong() const;\n    /** Sets the function for programmable function key D long press. */\n    virtual void setFuncKeyDLong(AnytoneKeySettingsExtension::KeyFunction func);\n\n    /** Returns the number of repeater check notifications. */\n    virtual unsigned repeaterCheckNumNotifications() const;\n    /** Sets the number of repeater check notifications. */\n    virtual void setRepeaterCheckNumNotifications(unsigned num);\n\n    /** Returns the backlight duration during TX in seconds. */\n    virtual Interval txBacklightDuration() const;\n    /** Sets the backlight duration during TX in seconds. */\n    virtual void setTXBacklightDuration(Interval sec);\n\n    /** Returns @c true, if the bluetooth hold time is enabled. */\n    virtual bool btHoldTimeEnabled() const;\n    /** Returns @c true, if the bluetooth hold time is infinite. */\n    virtual bool btHoldTimeInfinite() const;\n    /** Returns the bluetooth hold time. */\n    virtual Interval btHoldTime() const;\n    /** Sets the Bluetooth hold time (1-120s). */\n    virtual void setBTHoldTime(Interval interval);\n    /** Sets the Bluetooth hold time to infinite. */\n    virtual void setBTHoldTimeInfinite();\n    /** Sets the Bluetooth hold time to infinite. */\n    virtual void disableBTHoldTime();\n\n    /** Returns the bluetooth RX delay in ms. */\n    virtual Interval btRXDelay() const;\n    /** Sets the bluetooth RX delay in ms. */\n    virtual void setBTRXDelay(Interval delay);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) override;\n    bool updateConfig(Context &ctx, const ErrorStack &err) override;\n    bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) override;\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: D878UVCodeplug::GeneralSettingsElement::Limit {\n      // pass...\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: AnytoneCodeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int enableKeyTone()       { return 0x0000; }\n      static constexpr unsigned int transmitTimeout()     { return 0x0004; }\n      static constexpr unsigned int language()            { return 0x0005; }\n      static constexpr unsigned int vfoStepSize()         { return 0x0008; }\n      static constexpr unsigned int vfoScanType()         { return 0x000b; }\n      static constexpr unsigned int dmrMicGain()          { return 0x000c; }\n      static constexpr unsigned int vfoModeA()            { return 0x000d; }\n      static constexpr unsigned int vfoModeB()            { return 0x000e; }\n      static constexpr unsigned int steType()             { return 0x000f; }\n\n      static constexpr unsigned int steFrequency()        { return 0x0010; }\n      static constexpr unsigned int groupCallHangTime()   { return 0x0011; }\n      static constexpr unsigned int privateCallHangTime() { return 0x0012; }\n      static constexpr unsigned int preWaveDelay()        { return 0x0013; }\n      static constexpr unsigned int wakeHeadPeriod()      { return 0x0014; }\n      static constexpr unsigned int wfmChannelIndex()     { return 0x0015; }\n      static constexpr unsigned int wfmVFOEnabled()       { return 0x0016; }\n      static constexpr unsigned int memZoneA()            { return 0x0017; }\n      static constexpr unsigned int memZoneB()            { return 0x0018; }\n      static constexpr unsigned int wfmEnable()           { return 0x0019; }\n      static constexpr unsigned int enableRecoding()      { return 0x001a; }\n      static constexpr unsigned int displayBrightness()   { return 0x001d; }\n      static constexpr unsigned int gpsEnable()           { return 0x001f; }\n\n      static constexpr unsigned int smsAlert()            { return 0x0020; }\n      static constexpr unsigned int wfmMonitor()          { return 0x0022; }\n      static constexpr unsigned int activeChannelB()      { return 0x0023; }\n      static constexpr unsigned int subChannel()          { return 0x0024; }\n      static constexpr unsigned int tbstFrequency()       { return 0x0025; }\n      static constexpr unsigned int callAlert()           { return 0x0026; }\n      static constexpr unsigned int gpsTimeZone()         { return 0x0027; }\n      static constexpr unsigned int talkPermit()          { return 0x0028; }\n      static constexpr unsigned int dmrResetTone()        { return 0x0029; }\n      static constexpr unsigned int idleChannelTone()     { return 0x002a; }\n      static constexpr unsigned int menuExitTime()        { return 0x002b; }\n      static constexpr unsigned int filterOwnID()         { return 0x002c; }\n      static constexpr unsigned int startupTone()         { return 0x002d; }\n      static constexpr unsigned int callEndPrompt()       { return 0x002e; }\n      static constexpr unsigned int maxSpeakerVolume()    { return 0x002f; }\n\n      static constexpr unsigned int remoteStunKill()      { return 0x0030; }\n      static constexpr unsigned int remoteMonitor()       { return 0x0031; }\n      static constexpr unsigned int getGPSPosition()      { return 0x0032; }\n      static constexpr unsigned int longPressDuration()   { return 0x0033; }\n      static constexpr unsigned int volumeChangePrompt()  { return 0x0034; }\n      static constexpr unsigned int autoRepeaterDirA()    { return 0x0035; }\n      static constexpr unsigned int monSlotMatch()        { return 0x0036; }\n      static constexpr unsigned int monColorCodeMatch()   { return 0x0037; }\n      static constexpr unsigned int monIDMatch()          { return 0x0038; }\n      static constexpr unsigned int monTimeSlotHold()     { return 0x0039; }\n      static constexpr unsigned int lastCallerDisplay()   { return 0x003a; }\n      static constexpr unsigned int fmCallHold()          { return 0x003c; }\n      static constexpr unsigned int showClock()           { return 0x003d; }\n      static constexpr unsigned int enableGPSMessage()    { return 0x003e; }\n      static constexpr unsigned int enhanceAudio()        { return 0x003f; }\n\n      static constexpr unsigned int minVFOScanUHF()       { return 0x0040; }\n      static constexpr unsigned int maxVFOScanUHF()       { return 0x0044; }\n      static constexpr unsigned int minVFOScanVHF()       { return 0x0048; }\n      static constexpr unsigned int maxVFOScanVHF()       { return 0x004c; }\n\n      static constexpr unsigned int autoRepMinVHF()       { return 0x0050; }\n      static constexpr unsigned int autoRepMaxVHF()       { return 0x0054; }\n      static constexpr unsigned int autoRepMinUHF()       { return 0x0058; }\n      static constexpr unsigned int autoRepMaxUHF()       { return 0x005c; }\n\n      static constexpr unsigned int callToneTones()       { return 0x0060; }\n      static constexpr unsigned int callToneDurations()   { return 0x006a; }\n      static constexpr unsigned int idleToneTones()       { return 0x0074; }\n      static constexpr unsigned int idleToneDurations()   { return 0x007e; }\n      static constexpr unsigned int resetToneTones()      { return 0x0088; }\n      static constexpr unsigned int resetToneDurations()  { return 0x0092; }\n\n      static constexpr unsigned int autoRepOffsetUHF()    { return 0x009c; }\n      static constexpr unsigned int autoRepOffsetVHF()    { return 0x009d; }\n      static constexpr unsigned int priorityZoneA()       { return 0x009f; }\n\n      static constexpr unsigned int priorityZoneB()       { return 0x00a0; }\n      static constexpr unsigned int callDisplayMode()     { return 0x00a2; }\n      static constexpr unsigned int bluetooth()           { return 0x00a4; }\n      static constexpr unsigned int btAndInternalMic()    { return 0x00a5; }\n      static constexpr unsigned int btAndInternalSpeaker(){ return 0x00a6; }\n      static constexpr unsigned int pluginRecTone()       { return 0x00a7; }\n      static constexpr unsigned int gpsRangingInterval()  { return 0x00a8; }\n      static constexpr unsigned int btMicGain()           { return 0x00a9; }\n      static constexpr unsigned int btSpeakerGain()       { return 0x00aa; }\n      static constexpr unsigned int showChannelNumber()   { return 0x00ab; }\n      static constexpr unsigned int showCurrentContact()  { return 0x00ac; }\n      static constexpr unsigned int autoRoamPeriod()      { return 0x00ad; }\n      static constexpr unsigned int callColor()           { return 0x00ae; }\n      static constexpr unsigned int gpsUnits()            { return 0x00af; }\n\n      static constexpr Bit knobLock()                     { return {0x00b0, 0}; }\n      static constexpr Bit keypadLock()                   { return {0x00b0, 1}; }\n      static constexpr Bit sideKeyLock()                  { return {0x00b0, 3}; }\n      static constexpr Bit forceKeyLock()                 { return {0x00b0, 4}; }\n      static constexpr unsigned int autoRoamDelay()       { return 0x00b1; }\n      static constexpr unsigned int standbyTextColor()    { return 0x00b2; }\n      static constexpr unsigned int standbyBackground()   { return 0x00b3; }\n      static constexpr unsigned int showLastHeard()       { return 0x00b4; }\n      static constexpr unsigned int smsFormat()           { return 0x00b5; }\n      static constexpr unsigned int autoRepeaterDirB()    { return 0x00b6; }\n      static constexpr unsigned int fmSendIDAndContact()  { return 0x00b7; }\n      static constexpr unsigned int defaultChannels()     { return 0x00b8; }\n      static constexpr unsigned int defaultZoneA()        { return 0x00b9; }\n      static constexpr unsigned int defaultZoneB()        { return 0x00ba; }\n      static constexpr unsigned int defaultChannelA()     { return 0x00bb; }\n      static constexpr unsigned int defaultChannelB()     { return 0x00bc; }\n      static constexpr unsigned int defaultRoamingZone()  { return 0x00bd; }\n      static constexpr unsigned int repRangeCheck()       { return 0x00be; }\n      static constexpr unsigned int rangeCheckInterval()  { return 0x00bf; }\n\n      static constexpr unsigned int rangeCheckCount()     { return 0x00c0; }\n      static constexpr unsigned int roamStartCondition()  { return 0x00c1; }\n      static constexpr unsigned int displaySeparator()    { return 0x00c3; }\n      static constexpr unsigned int keepLastCaller()      { return 0x00c4; }\n      static constexpr unsigned int channelNameColor()    { return 0x00c5; }\n      static constexpr unsigned int repCheckNotify()      { return 0x00c6; }\n      static constexpr unsigned int txBacklightDuration() { return 0x00c7; }\n      static constexpr unsigned int roaming()             { return 0x00c8; }\n      static constexpr unsigned int progFuncKey1Short()   { return 0x00c9; }\n      static constexpr unsigned int progFuncKey2Short()   { return 0x00ca; }\n      static constexpr unsigned int progFuncKey3Short()   { return 0x00cb; }\n      static constexpr unsigned int progFuncKey4Short()   { return 0x00cc; }\n      static constexpr unsigned int progFuncKey5Short()   { return 0x00cd; }\n      static constexpr unsigned int progFuncKey6Short()   { return 0x00ce; }\n      static constexpr unsigned int progFuncKeyAShort()   { return 0x00cf; }\n\n      static constexpr unsigned int progFuncKeyBShort()   { return 0x00d0; }\n      static constexpr unsigned int progFuncKeyCShort()   { return 0x00d1; }\n      static constexpr unsigned int progFuncKeyDShort()   { return 0x00d2; }\n      static constexpr unsigned int progFuncKey1Long()    { return 0x00d3; }\n      static constexpr unsigned int progFuncKey2Long()    { return 0x00d4; }\n      static constexpr unsigned int progFuncKey3Long()    { return 0x00d5; }\n      static constexpr unsigned int progFuncKey4Long()    { return 0x00d6; }\n      static constexpr unsigned int progFuncKey5Long()    { return 0x00d7; }\n      static constexpr unsigned int progFuncKey6Long()    { return 0x00d8; }\n      static constexpr unsigned int progFuncKeyALong()    { return 0x00d9; }\n      static constexpr unsigned int progFuncKeyBLong()    { return 0x00da; }\n      static constexpr unsigned int progFuncKeyCLong()    { return 0x00db; }\n      static constexpr unsigned int progFuncKeyDLong()    { return 0x00dc; }\n      static constexpr unsigned int repCheckNumNotify()   { return 0x00de; }\n\n      static constexpr unsigned int btHoldTime()          { return 0x00e1; }\n      static constexpr unsigned int btRXDelay()           { return 0x00e2; }\n      /// @endcond\n    };\n  };\n\n\n  /** General settings extension element for the D578UV. */\n  class ExtendedSettingsElement: public AnytoneCodeplug::ExtendedSettingsElement\n  {\n  protected:\n    /* Encoding of possible speakers. */\n    enum class Speaker {\n      Microphone=0, Radio=1, Both=2\n    };\n\n    /** Encoding of microphone-speaker source. */\n    enum class SpeakerSource {\n      MainChannel = 0, SubChannel = 1\n    };\n\n    /** Encoding of possible GPS modes. */\n    enum class GPSMode {\n      GPS = 0, Beidou=1, GPS_Beidou=2\n    };\n\n    /** Encoding of possible fan-control settings. */\n    enum class FanControl {\n      PTT=0, Temperature=1, Both=2\n    };\n\n    /** Possible mic types. */\n    enum class MicType {\n      AnyTone = 0, Generic = 1\n    };\n\n    /** Encoding of up/down key functions. */\n    enum class UpDownKeyFunction {\n      Channel = 0, Volume = 1\n    };\n\n    /** Encoding of repeater color code match. */\n    enum class RepeaterColorCodeMatch {\n      None = 0, VFO_A = 1, VFO_B = 2\n    };\n\n    /** Encoding of repeater timeslot match. */\n    enum class RepeaterTimeSlotMatch {\n      Any = 0, RX1_TX2 = 1, RX2_TX1 = 2\n    };\n\n    /** Talker alias encoding. */\n    enum class TalkerAliasEncoding {\n      ISO8 = 0, ISO7 = 1, Unicode = 2,\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    ExtendedSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ExtendedSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00000200; }\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns the talker alias source. */\n    virtual AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const;\n    /** Sets the talker alias source. */\n    virtual void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode);\n\n    /** Returns the talker alias encoding. */\n    virtual DMRSettings::TalkerAliasEncoding talkerAliasEncoding() const;\n    /** Sets the talker alias encoding. */\n    virtual void setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding encoding);\n\n    /** Returns @c true if the weather alarm is enabled. */\n    virtual bool weatherAlarmEnabled() const;\n    /** Enables/disables the weather alarm. */\n    virtual void enableWeatherAlarm(bool enable);\n\n    /** Returns @c true if the repeater function is enabled. */\n    virtual bool repeaterEnabled() const;\n    /** Enables/disables the repeater function. */\n    virtual void enableRepeater(bool enable);\n\n    /** Returns the speaker setting. */\n    virtual AnytoneAudioSettingsExtension::Speaker speaker() const;\n    /** Sets the speaker setting. */\n    virtual void setSpeaker(AnytoneAudioSettingsExtension::Speaker speaker);\n\n    /** Returns the microphone-speaker source. */\n    virtual AnytoneAudioSettingsExtension::HandsetSpeakerSource micSpeakerSource() const;\n    /** Sets the microphone-speaker source. */\n    virtual void setMicSpeakerSource(AnytoneAudioSettingsExtension::HandsetSpeakerSource source);\n\n    /** Returns the GPS mode. */\n    virtual GNSSSettings::Systems gnss() const;\n    /** Sets the GPS mode. */\n    virtual void setGNSS(GNSSSettings::Systems mode);\n\n    /** Returns @c true if the BT PTT latch is enabled. */\n    virtual bool bluetoothPTTLatch() const;\n    /** Enables/disables bluetooth PTT latch. */\n    virtual void enableBluetoothPTTLatch(bool enable);\n\n    /** Returns @c true if the bluetooth PTT sleep delay is disabled (infinite). */\n    virtual bool infiniteBluetoothPTTSleepDelay() const;\n    /** Returns the bluetooth PTT sleep delay in minutes, 0=off. */\n    virtual Interval bluetoothPTTSleepDelay() const;\n    /** Sets the bluetooth PTT sleep delay in minutes. */\n    virtual void setBluetoothPTTSleepDelay(Interval delay);\n    /** Sets the bluetooth PTT sleep delay to infinite/disabled. */\n    virtual void setInfiniteBluetoothPTTSleepDelay();\n\n    /** Returns the fan-control setting. */\n    virtual AnytoneSettingsExtension::FanControl fanControl() const;\n    /** Sets the fan-control setting. */\n    virtual void setFanControl(AnytoneSettingsExtension::FanControl ctrl);\n\n    /** Returns the weather channel index. */\n    virtual unsigned int weatherChannelIndex() const;\n    /** Sets the weather channel index. */\n    virtual void setWeatherChannelIndex(unsigned int idx);\n\n    /** Returns @c true if the manual dialed group call hang time is infinite. */\n    virtual bool infiniteManDialGroupCallHangTime() const;\n    /** Returns the manual dial group call hang time. */\n    virtual Interval manDialGroupCallHangTime() const;\n    /** Sets the manual dial group call hang time. */\n    virtual void setManDialGroupCallHangTime(Interval dur);\n    /** Sets the manual dial group call hang time to infinite. */\n    virtual void setManDialGroupCallHangTimeInfinite();\n\n    /** Returns @c true if the manual dialed private call hang time is infinite. */\n    virtual bool infiniteManDialPrivateCallHangTime() const;\n    /** Returns the manual dial private call hang time. */\n    virtual Interval manDialPrivateCallHangTime() const;\n    /** Sets the manual dial private call hang time. */\n    virtual void setManDialPrivateCallHangTime(Interval dur);\n    /** Sets the manual dial private call hang time to infinite. */\n    virtual void setManDialPrivateCallHangTimeInfinite();\n\n    /** Returns the short-press function for the channel knob. */\n    virtual AnytoneKeySettingsExtension::KeyFunction chKnobShortPressFunction() const;\n    /** Sets the channel knob short-press function. */\n    virtual void setChKnobShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the long-press function for the channel knob. */\n    virtual AnytoneKeySettingsExtension::KeyFunction chKnobLongPressFunction() const;\n    /** Sets the channel knob long-press function. */\n    virtual void setChKnobLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n\n    AnytoneDisplaySettingsExtension::Color channelBNameColor() const;\n    void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns the encryption mode. */\n    virtual AnytoneDMRSettingsExtension::EncryptionType encryption() const;\n    /** Sets the encryption mode. */\n    virtual void setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode);\n\n    /** Returns @c true if the professional mode is enabled. */\n    virtual bool professionalMode() const;\n    /** Enables/disables professional mode. */\n    virtual void enableProfessionalMode(bool enable);\n\n    /** Returns the STE (squelch tail elimination) duration. */\n    virtual Interval steDuration() const;\n    /** Sets the STE (squelch tail elimination) duration. */\n    virtual void setSTEDuration(Interval dur);\n\n    /** Returns the microphone type. */\n    virtual AnytoneAudioSettingsExtension::HandsetType micType() const;\n    /** Sets the microphone type. */\n    virtual void setMicType(AnytoneAudioSettingsExtension::HandsetType type);\n\n    AnytoneDisplaySettingsExtension::Color zoneANameColor() const;\n    void setZoneANameColor(AnytoneDisplaySettingsExtension::Color color);\n    AnytoneDisplaySettingsExtension::Color zoneBNameColor() const;\n    void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns @c true if the auto-shutdown timer is reset on a call. */\n    virtual bool resetAutoShutdownOnCall() const;\n    /** Enables/disables reset on call of the auto-shutdown timer. */\n    virtual void enableResetAutoShutdownOnCall(bool enable);\n\n    /** Returns @c true if the color code is shown. */\n    virtual bool showColorCode() const;\n    /** Enables/disables display of color code. */\n    virtual void enableShowColorCode(bool enable);\n    /** Returns @c true if the time slot is shown. */\n    virtual bool showTimeSlot() const;\n    /** Enables/disables display of time slot. */\n    virtual void enableShowTimeSlot(bool enable);\n    /** Returns @c true if the channel type is shown. */\n    virtual bool showChannelType() const;\n    /** Enables/disables display of channel type. */\n    virtual void enableShowChannelType(bool enable);\n\n    /** Returns @c true if the FM idle channel tone is enabled. */\n    virtual bool fmIdleTone() const;\n    /** Enables/disables FM idle channel tone. */\n    virtual void enableFMIdleTone(bool enable);\n\n    /** Returns the date format. */\n    virtual AnytoneDisplaySettingsExtension::DateFormat dateFormat() const;\n    /** Sets the date format. */\n    virtual void setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format);\n\n    /** Returns the FM Mic gain [1,10]. */\n    virtual Level fmMicGain() const;\n    /** Sets the analog mic gain [1,10]. */\n    virtual void setFMMicGain(Level gain);\n\n    /** Returns the short-press function for SK1 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK1ShortPressFunction() const;\n    /** Sets the SK1 short-press function of the BT handset. */\n    virtual void setBtSK1ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the short-press function for SK2 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK2ShortPressFunction() const;\n    /** Sets the SK2 short-press function of the BT handset. */\n    virtual void setBtSK2ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the short-press function for SK3 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK3ShortPressFunction() const;\n    /** Sets the SK3 short-press function of the BT handset. */\n    virtual void setBtSK3ShortPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n\n    /** Returns the lone-press function for SK1 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK1LongPressFunction() const;\n    /** Sets the SK1 long-press function of the BT handset. */\n    virtual void setBtSK1LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the long-press function for SK2 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK2LongPressFunction() const;\n    /** Sets the SK2 long-press function of the BT handset. */\n    virtual void setBtSK2LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the long-press function for SK3 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK3LongPressFunction() const;\n    /** Sets the SK3 long-press function of the BT handset. */\n    virtual void setBtSK3LongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n\n    /** Returns the BT handset mic gain [1-10]. */\n    virtual unsigned int btHandsetMicGain() const;\n    /** Sets the BT handset mic gain. */\n    virtual void setBtHandsetMicGain(unsigned int gain);\n\n    /** Returns the bluetooth handset backlight duration. */\n    virtual Interval btHandsetBacklightDuration() const;\n    /** Sets the bluetooth handset backlight duration. */\n    virtual void setBtHandsetBacklightDuration(Interval delay);\n\n    /** Returns the function of up/down keys on microphone. */\n    virtual AnytoneKeySettingsExtension::UpDownKeyFunction micUpDownKeyFunction() const;\n    /** Sets the microphone up/down key function. */\n    virtual void setMicUpDownKeyFunction(AnytoneKeySettingsExtension::UpDownKeyFunction func);\n\n    /** Returns @c true if the transmit timeout notification is enabled. */\n    virtual bool totNotification() const;\n    /** Enables/disables transmit timeout notification. */\n    virtual void enableTOTNotification(bool enable);\n\n    /** Returns @c true if the GPS roaming is enabled. */\n    virtual bool gpsRoaming() const;\n    /** Enables/disables GPS roaming. */\n    virtual void enableGPSRoaming(bool enable);\n\n    /** Returns the repeater colorcode match mode. */\n    virtual AnytoneRepeaterSettingsExtension::ColorCode repColorCodeMatch() const;\n    /** Sets the repeater colorcode match mode. */\n    virtual void setRepColorCodeMatch(AnytoneRepeaterSettingsExtension::ColorCode mode);\n\n    /** Returns the repeater timeslots for VFO A. */\n    virtual AnytoneRepeaterSettingsExtension::TimeSlot repTimeSlotAMatch() const;\n    /** Sets the repeater timeslots for VFO A. */\n    virtual void setRepTimeSlotAMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode);\n    /** Returns the repeater timeslots for VFO B. */\n    virtual AnytoneRepeaterSettingsExtension::TimeSlot repTimeSlotBMatch() const;\n    /** Sets the repeater timeslots for VFO B. */\n    virtual void setRepTimeSlotBMatch(AnytoneRepeaterSettingsExtension::TimeSlot mode);\n\n    /** Returns the BT handset squelch level [0, 1-10]. */\n    virtual unsigned int btHandsetSquelch() const;\n    /** Sets the BT handset squelch level [0, 1-10]. */\n    virtual void setBtHandsetSquelch(unsigned int level);\n\n    /** If @c true, the BT handset to shut off automatically, if the device powers down. */\n    virtual bool btHandsetAutoPowerOffEnabled() const;\n    /** Enables/disables the BT handset to shut off automatically, if the device powers down. */\n    virtual void enableBtHandsetAutoPowerOff(bool enable);\n\n    /** Returns the very-long-press function for SK1 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK1VeryLongPressFunction() const;\n    /** Sets the SK1 very-long-press function of the BT handset. */\n    virtual void setBtSK1VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the very-long-press function for SK2 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK2VeryLongPressFunction() const;\n    /** Sets the SK2 very-long-press function of the BT handset. */\n    virtual void setBtSK2VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n    /** Returns the very-long-press function for SK3 of the BT handset. */\n    virtual AnytoneKeySettingsExtension::KeyFunction btSK3VeryLongPressFunction() const;\n    /** Sets the SK3 very-long-press function of the BT handset. */\n    virtual void setBtSK3VeryLongPressFunction(AnytoneKeySettingsExtension::KeyFunction func);\n\n    /** Returns the BT handset TX noise reduction level [0,1-10]. */\n    virtual unsigned int btHandsetTxNoiseRedLevel() const;\n    /** Sets the BT handset TX noise reduction level [0,1-10]. */\n    virtual void setBtHandsetTxNoiseRedLevel(unsigned int level);\n\n    /** Returns the BT handset VOX level [0,1-10]. */\n    virtual unsigned int btHandsetVOXLevel() const;\n    /** Sets the BT handset VOX level [0,1-10]. */\n    virtual void setBtHandsetVOXLevel(unsigned int level);\n\n    /** Returns the VOX delay for the BT handset. */\n    virtual Interval btHandsetVOXDelay() const;\n    /** Sets the VOX delay for the BT handset. */\n    virtual void setBtHandsetVOXDelay(Interval delay);\n\n    /** Returns the BT handset volume for VFO A [0,1,10]. */\n    virtual unsigned int btHandsetVolumeA() const;\n    /** Sets the BF handset volume for VFO A [0,1,10]. */\n    virtual void setBtHandsetVolumeA(unsigned int vol);\n    /** Returns the BT handset volume for VFO B [0,1,10]. */\n    virtual unsigned int btHandsetVolumeB() const;\n    /** Sets the BF handset volume for VFO B [0,1,10]. */\n    virtual void setBtHandsetVolumeB(unsigned int vol);\n\n    /** Returns the call-end tone melody. */\n    virtual void callEndToneMelody(Melody &melody) const;\n    /** Sets the call-end tone melody. */\n    virtual void setCallEndToneMelody(const Melody &melody);\n    /** Returns the all-call tone melody. */\n    virtual void allCallToneMelody(Melody &melody) const;\n    /** Sets the all-call tone melody. */\n    virtual void setAllCallToneMelody(const Melody &melody);\n\n\n    /** Encodes the settings from the config. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Update config from settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: AnytoneCodeplug::ExtendedSettingsElement::Limit {\n      static constexpr unsigned int maxBluetoothPTTSleepDelay() { return 4; }     ///< Maximum delay in minutes.\n      static constexpr unsigned int maxWeatherChannelIndex()    { return 9; }     ///< Maximum weather channel index.\n      static constexpr Range<unsigned int> micGain()            { return {1,5}; } ///< Valid range for mic gain settings.\n    };\n\n  protected:\n    /** Internal used offset within the element. */\n    struct Offset : public AnytoneCodeplug::ExtendedSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int talkerAliasDisplay()           { return 0x001e; }\n      static constexpr unsigned int talkerAliasEncoding()          { return 0x001f; }\n\n      static constexpr unsigned int weatherAlarm()                 { return 0x0020; }\n      static constexpr unsigned int repeater()                     { return 0x0021; }\n      static constexpr unsigned int speakers()                     { return 0x0023; }\n      static constexpr unsigned int micSpeakerSource()             { return 0x0025; }\n      static constexpr unsigned int gpsMode()                      { return 0x0026; }\n      static constexpr unsigned int btPTTLatch()                   { return 0x0027; }\n      static constexpr unsigned int btPTTSleepDelay()              { return 0x0028; }\n      static constexpr unsigned int fanControl()                   { return 0x0029; }\n      static constexpr unsigned int weatherChannelIndex()          { return 0x002a; }\n      static constexpr unsigned int manGrpCallHangTime()           { return 0x002b; }\n      static constexpr unsigned int manPrivCallHangTime()          { return 0x002c; }\n      static constexpr unsigned int chKnobShortPressFunction()     { return 0x002d; }\n      static constexpr unsigned int chKnobLongPressFunction()      { return 0x002e; }\n      static constexpr unsigned int channelBNameColor()            { return 0x002f; }\n\n      static constexpr unsigned int encryptionType()               { return 0x0030; }\n      static constexpr unsigned int uiMode()                       { return 0x0031; }\n      static constexpr unsigned int steDuration()                  { return 0x0032; }\n      static constexpr unsigned int micType()                      { return 0x0033; }\n      static constexpr unsigned int zoneANameColor()               { return 0x0034; }\n      static constexpr unsigned int zoneBNameColor()               { return 0x0035; }\n      static constexpr unsigned int autoShutdownMode()             { return 0x0036; }\n      static constexpr Bit displayColorCode()                      { return {0x003b, 2}; }\n      static constexpr Bit displayTimeSlot()                       { return {0x003b, 1}; }\n      static constexpr Bit displayChannelType()                    { return {0x003b, 0}; }\n      static constexpr unsigned int fmIdleTone()                   { return 0x003c; }\n      static constexpr unsigned int dateFormat()                   { return 0x003d; }\n      static constexpr unsigned int analogMicGain()                { return 0x003e; }\n      static constexpr unsigned int btSK1ShortPressFunction()      { return 0x003f; }\n\n      static constexpr unsigned int btSK2ShortPressFunction()      { return 0x0040; }\n      static constexpr unsigned int btSK3ShortPressFunction()      { return 0x0041; }\n      static constexpr unsigned int btSK1LongPressFunction()       { return 0x0042; }\n      static constexpr unsigned int btSK2LongPressFunction()       { return 0x0043; }\n      static constexpr unsigned int btSK3LongPressFunction()       { return 0x0044; }\n      static constexpr unsigned int btHSMicGain()                  { return 0x0045; }\n      static constexpr unsigned int btHSBacklightDuration()        { return 0x0047; }\n      static constexpr unsigned int upDownKeyFunction()            { return 0x0048; }\n      static constexpr unsigned int totNotification()              { return 0x0049; }\n      static constexpr unsigned int gpsRoaming()                   { return 0x004a; }\n      static constexpr unsigned int repeaterColorCode()            { return 0x004b; }\n      static constexpr unsigned int repeaterATimeslot()            { return 0x004c; }\n      static constexpr unsigned int repeaterBTimeslot()            { return 0x004d; }\n      static constexpr unsigned int btHSRxNoiseReduction()         { return 0x004e; }\n      static constexpr unsigned int btHSShutDown()                 { return 0x004f; }\n\n      static constexpr unsigned int btSK1VeryLongPressFunction()   { return 0x0050; }\n      static constexpr unsigned int btSK2VeryLongPressFunction()   { return 0x0051; }\n      static constexpr unsigned int btSK3VeryLongPressFunction()   { return 0x0052; }\n      static constexpr unsigned int btHSTxNoiseReduction()         { return 0x0053; }\n      static constexpr unsigned int btHSVOXLevel()                 { return 0x0054; }\n      static constexpr unsigned int btHSVOXDelay()                 { return 0x0055; }\n      static constexpr unsigned int btHSVolumeA()                  { return 0x0056; }\n      static constexpr unsigned int btHSVolumeB()                  { return 0x0057; }\n\n      static constexpr unsigned int callEndTones()                 { return 0x0058; }\n      static constexpr unsigned int callEndDurations()             { return 0x0062; }\n      static constexpr unsigned int allCallTones()                 { return 0x006c; }\n      static constexpr unsigned int allCallDurations()             { return 0x0076; }\n      static constexpr unsigned int headerRep()                    { return 0x0080; }\n      /// @endcond\n    };\n  };\n\n\n\n  /** Represents the hot-key settings of the radio within the D578UV binary codeplug.\n   *\n   * This class extends the common @c AnytoneCodeplug::HotKeySettings element, encoding 24 instead\n   * of 17 @c HotKeySettingsElement. */\n  class HotKeySettingsElement: public AnytoneCodeplug::HotKeySettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    HotKeySettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    HotKeySettingsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0970; }\n\n    uint8_t *hotKeySetting(unsigned int n) const;\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      static constexpr unsigned int numEntries() { return 24; }   ///< Maximum number of hot-key entries.\n    };\n\n  };\n\n  /** Implements the air-band receiver channel.\n   *\n   * Memory layout of the air-band channel list (size 0x0020 bytes):\n   * @verbinclude d578uv_airbandchannel.txt */\n  class AirBandChannelElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AirBandChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AirBandChannelElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the element. */\n    void clear();\n\n    /** The channel frequency. */\n    virtual Frequency frequency() const;\n    /** Sets the channel frequency. */\n    virtual void setFrequency(Frequency freq);\n\n    /** The name of the channel. */\n    virtual QString name() const;\n    /** Sets the name of the channel. */\n    virtual void setName(const QString &name);\n\n    /** Encodes the given AM channel. */\n    virtual bool encode(AMChannel *ch, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes the given AM channel. */\n    virtual AMChannel *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  public:\n    /** Some limits of the channel. */\n    struct Limit {\n      static constexpr unsigned int nameLength() { return 16; }     ///< Maximum name length.\n    };\n\n  public:\n    /** Internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int frequency() { return 0x0000; }\n      static constexpr unsigned int name()      { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap indicating which channels are valid and which are included in the\n   *  air-band scan. */\n  class AirBandBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    AirBandBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AirBandBitmapElement(uint8_t *ptr);\n\n    /** The element size. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\nprotected:\n  /** Hidden constructor. */\n  explicit D578UVCodeplug(const QString &label, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit D578UVCodeplug(QObject *parent = nullptr);\n\n  Config *preprocess(Config *config, const ErrorStack &err) const override;\n\nprotected:\n  bool allocateBitmaps() override;\n  void setBitmaps(Context &ctx) override;\n  void allocateForDecoding() override;\n  void allocateForEncoding() override;\n\n  void allocateHotKeySettings() override;\n\n  bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool createElements(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\n  /** Allocate Air band channels. */\n  virtual bool allocateAirBandChannels();\n  /** Encode all defined air band channels. */\n  virtual bool encodeAirBandChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decode all air band channels. */\n  virtual bool createAirBandChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateContacts() override;\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\n  void allocateGeneralSettings() override;\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit: D878UVCodeplug::Limit {\n    /// Maximum number of air-band channels.\n    static constexpr unsigned int airBandChannels() { return 100; }\n  };\n\nprotected:\n  /** Internal used offsets within the codeplug. */\n  struct Offset: D878UVCodeplug::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int contactIdTable()       { return 0x04800000; }\n    static constexpr unsigned int settings()             { return 0x02500000; }\n    static constexpr unsigned int gpsMessages()          { return 0x02501280; }\n    static constexpr unsigned int settingsExtension()    { return 0x02501400; }\n    static constexpr unsigned int airBandChannels()      { return 0x02BC0000; }\n    static constexpr unsigned int airBandVFO()           { return 0x02BC1000; }\n    static constexpr unsigned int airBandChannelBitmap() { return 0x02BC1020; }\n    static constexpr unsigned int airBandScanBitmap()    { return 0x02BC1040; }\n    /// @endcond\n  };\n};\n\n#endif // D578UV_CODEPLUG_HH\n"
  },
  {
    "path": "lib/d578uv_limits.cc",
    "content": "#include \"d578uv_limits.hh\"\n\n#include \"anytone_codeplug.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nD578UVLimits::D578UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                           const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                           const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V111\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 500000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                       unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n            {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n            {\"password\", new RadioLimitPin(D578UVCodeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n            { \"number\", new RadioLimitString(1, 14, RadioLimitString::DTMF) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                           unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                           unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, true)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { AMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(\n                {{Frequency::fromMHz(108), Frequency::fromMHz(136)}},\n                RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"rxOnly\", new RadioLimitBool()},\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/d578uv_limits.hh",
    "content": "#ifndef D578UVLIMITS_HH\n#define D578UVLIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the AnyTone AT-D878UV.\n * @ingroup d578 */\nclass D578UVLimits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  D578UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n               const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n               const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D578UVLIMITS_HH\n"
  },
  {
    "path": "lib/d868uv.cc",
    "content": "#include \"d868uv.hh\"\n\n#include \"userdatabase.hh\"\n#include \"d868uv_codeplug.hh\"\n#include \"d868uv_callsigndb.hh\"\n#include \"d868uv_limits.hh\"\n\n#include \"config.hh\"\n#include \"logger.hh\"\n\n#define RBSIZE 16\n#define WBSIZE 16\n\n\nD868UV::D868UV(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"Anytone AT-D868UV\", device, parent), _limits(nullptr)\n{\n  _codeplug = new D868UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D868UVCallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x01:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(470.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(440.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new D868UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Ignore frequency limits.\";\n    _limits = new D868UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nD868UV::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nD868UV::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::D868UVE, \"d868uve\", \"AT-D868UVE\", \"AnyTone\", {AnytoneGD32Interface::interfaceInfo()},\n        QList<RadioInfo>{\n          RadioInfo(RadioInfo::D868UV, \"d868uv\", \"AT-D868UV\", \"AnyTone\", {AnytoneGD32Interface::interfaceInfo()})\n        });\n}\n"
  },
  {
    "path": "lib/d868uv.hh",
    "content": "/** @defgroup d868uv Anytone AT-D868UV\n * Device specific classes for Anytone AT-D868UV.\n *\n * \\image html d878uv.jpg \"AT-D868UV\" width=200px\n * \\image latex d878uv.jpg \"AT-D868UV\" width=200px\n *\n * @ingroup anytone */\n#ifndef __D868UV_HH__\n#define __D868UV_HH__\n\n#include \"radio.hh\"\n#include \"anytone_radio.hh\"\n\n\n/** Implements an interface to Anytone AT-D868UV VHF/UHF 7W DMR (Tier I & II) radios.\n *\n * The reverse-engineering of the D868UVCodeplug was quiet hard as it is huge and the radio\n * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the\n * windows CPS does not directly relate to the data being written to the device. These two issues\n * (a lot of features and a huge codeplug) require that the transfer of the codeplug to the\n * device is performed in 4 steps.\n *\n * First only the bitmaps of all lists are downloaded from the device. Then all elements that are\n * not touched or only updated by the common code-plug config are downloaded. Then, the common\n * config gets applied to the binary codeplug. That is, all channels, contacts, zones, group-lists\n * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config\n * gets updated from the common codeplug settings. Finally, the resulting binary codeplug gets\n * written back to the device.\n *\n * This rather complex method of writing a codeplug to the device is needed to maintain all\n * settings within the radio that are not defined within the common codeplug config while keeping\n * the amount of data being read from and written to the device small.\n *\n * @ingroup d868uv */\nclass D868UV: public AnytoneRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit D868UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprotected:\n  /** Holds the limits for this radio.*/\n  RadioLimits *_limits;\n};\n\n#endif // __D868UV_HH__\n"
  },
  {
    "path": "lib/d868uv_callsigndb.cc",
    "content": "#include \"userdatabase.hh\"\n#include \"d868uv_callsigndb.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVCallsignDB::EntryElement\n * ********************************************************************************************* */\nD868UVCallsignDB::EntryElement::EntryElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD868UVCallsignDB::EntryElement::EntryElement(uint8_t *ptr)\n  : Element(ptr, Limit::totalLength())\n{\n  // pass...\n}\n\nvoid\nD868UVCallsignDB::EntryElement::clear() {\n  // Clear header and all strings.\n  memset(_data, 0x00, 12);\n}\n\nvoid\nD868UVCallsignDB::EntryElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::PrivateCall: setUInt8(Offset::callType(), (unsigned int)CallType::Private); break;\n  case DMRContact::GroupCall: setUInt8(Offset::callType(), (unsigned int)CallType::Group); break;\n  case DMRContact::AllCall: setUInt8(Offset::callType(), (unsigned int)CallType::All); break;\n  }\n}\n\nvoid\nD868UVCallsignDB::EntryElement::setNumber(unsigned num) {\n  setBCD8_be(Offset::number(), num);\n}\n\nvoid\nD868UVCallsignDB::EntryElement::setFriendFlag(bool set) {\n  setBit(Offset::friendFlag(), set);\n}\n\nvoid\nD868UVCallsignDB::EntryElement::setRingTone(RingTone tone) {\n  setUInt2(Offset::ringTone(), (unsigned)tone);\n}\n\nvoid\nD868UVCallsignDB::EntryElement::setContent(\n    const QString &name, const QString &city, const QString &call, const QString &state,\n    const QString &country, const QString &comment)\n{\n  unsigned addr = Offset::name(), flen;\n  flen = std::min(qsizetype(Limit::nameLength()), name.size()); writeASCII(addr, name, flen+1); addr += flen+1;\n  flen = std::min(qsizetype(Limit::cityLength()), city.size()); writeASCII(addr, city, flen+1); addr += flen+1;\n  flen = std::min(qsizetype(Limit::callLength()), call.size()); writeASCII(addr, call, flen+1); addr += flen+1;\n  flen = std::min(qsizetype(Limit::stateLength()), state.size()); writeASCII(addr, state, flen+1); addr += flen+1;\n  flen = std::min(qsizetype(Limit::countryLength()), country.size()); writeASCII(addr, country, flen+1); addr += flen+1;\n  flen = std::min(qsizetype(Limit::commentLength()), comment.size()); writeASCII(addr, comment, flen+1);\n}\n\nunsigned\nD868UVCallsignDB::EntryElement::fromUser(const UserDatabase::User &user) {\n  clear();\n  setCallType(DMRContact::PrivateCall);\n  setNumber(user.id);\n  setRingTone(RingTone::Off);\n  setContent(user.name, user.city, user.call, user.state, user.country, \"\");\n  return size(user);\n}\n\nunsigned\nD868UVCallsignDB::EntryElement::size(const UserDatabase::User &user) {\n  return Limit::headerLength() // header\n         + std::min(qsizetype(Limit::nameLength()), user.name.size())+1       // name\n         + std::min(qsizetype(Limit::cityLength()), user.city.size())+1       // city\n         + std::min(qsizetype(Limit::callLength()), user.call.size())+1       // call\n         + std::min(qsizetype(Limit::stateLength()), user.state.size())+1     // state\n         + std::min(qsizetype(Limit::countryLength()), user.country.size())+1 // country\n         + 1; // no comment but 0x00 terminator\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVCallsignDB::LimitsElement\n * ********************************************************************************************* */\nD868UVCallsignDB::LimitsElement::LimitsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD868UVCallsignDB::LimitsElement::LimitsElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nD868UVCallsignDB::LimitsElement::clear() {\n  memset(_data, 0x00, _size);\n  setTotalSize(0);\n}\n\nunsigned\nD868UVCallsignDB::LimitsElement::count() const {\n  return getUInt32_le(Offset::count());\n}\nvoid\nD868UVCallsignDB::LimitsElement::setCount(unsigned count) {\n  setUInt32_le(Offset::count(), count);\n}\n\nunsigned\nD868UVCallsignDB::LimitsElement::endOfDB() const {\n  return getUInt32_le(Offset::endOfDB());\n}\nvoid\nD868UVCallsignDB::LimitsElement::setEndOfDB(unsigned addr) {\n  setUInt32_le(Offset::endOfDB(), addr);\n}\nvoid\nD868UVCallsignDB::LimitsElement::setTotalSize(unsigned size) {\n  setEndOfDB(D868UVCallsignDB::Offset::callsigns() + size);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVCallsignDB\n * ********************************************************************************************* */\nD868UVCallsignDB::D868UVCallsignDB(QObject *parent)\n  : CallsignDB(parent)\n{\n  // allocate and clear DB memory\n  addImage(\"AnyTone AT-D878UV Callsign database.\");\n}\n\nbool D868UVCallsignDB::encode(UserDatabase *db, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Determine size of call-sign DB in memory\n  qint64 n = std::min(db->count(), qint64(Limit::entries()));\n  // If DB size is limited by settings\n  if (selection.hasCountLimit())\n    n = std::min(n, (qint64)selection.countLimit());\n  logDebug() << \"Encode \" << n << \" entries.\";\n\n  // Select n users and sort them in ascending order of their IDs\n  QVector<UserDatabase::User> users;\n  users.reserve(n);\n  for (unsigned i=0; i<n; i++)\n    users.append(db->user(i));\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n\n  logDebug() << \"Encode call-signs from \" << users.first().id << \": \" << users.first().call << \", \"\n             << users.first().name << \" in \" << users.first().city << \" to \" << users.last().id\n             << \": \" << users.last().call << \", \" << users.last().name << \" in \" << users.last().city << \".\";\n\n  // Compute total size of callsign db entries\n  size_t dbSize = 0;\n  size_t indexSize = n*IndexEntryElement::size();\n  for (qint64 i=0; i<n; i++)\n    dbSize += EntryElement::size(users[i]);\n\n  // Allocate DB limits\n  image(0).addElement(Offset::limits(), LimitsElement::size());\n  // Store DB limits\n  LimitsElement limits(data(Offset::limits()));\n  limits.clear();\n  limits.setCount(n);\n  limits.setTotalSize(dbSize);\n\n  // Allocate index banks\n  for (int i=0; 0<indexSize; i++, indexSize-=std::min(indexSize, size_t(IndexBankElement::size()))) {\n    size_t addr = Offset::index() + i*Offset::betweenIndexBanks();\n    size_t size = align_size(std::min(indexSize, size_t(IndexBankElement::size())), 16);\n    image(0).addElement(addr, size);\n    memset(data(addr), 0xff, size);\n  }\n\n  // Allocate entry banks\n  for (int i=0; 0<dbSize; i++, dbSize-=std::min(dbSize, size_t(EntryBankElement::size()))) {\n    size_t addr = Offset::callsigns() + i*Offset::betweenCallsignBanks();\n    size_t size = align_size(std::min(dbSize, size_t(EntryBankElement::size())), 16);\n    image(0).addElement(addr, size);\n    memset(data(addr), 0x00, size);\n  }\n\n  // Fill index, the offset of the entry is not the real memory offset,\n  // but a virtual one without the gaps.\n  uint32_t entry_offset = 0;\n  uint32_t index_offset = 0;\n  uint32_t index_bank   = 0;\n  for (qint64 i=0; i<n; i++, index_offset+=IndexEntryElement::size()) {\n    if (IndexBankElement::size() <= index_offset) {\n      index_offset = 0; index_bank += 1;\n    }\n    IndexEntryElement index(data(Offset::index()+index_bank*Offset::betweenIndexBanks()+index_offset));\n    index.setID(users[i].id, false);\n    index.setIndex(entry_offset);\n    entry_offset += EntryElement::size(users[i]);\n  }\n\n  // Then store DB entries\n  uint32_t entry_bank = 0;\n  entry_offset = 0;\n  for (qint64 i=0; i<n; i++) {\n    // Get size of current entry\n    uint32_t entry_size = EntryElement::size(users[i]);\n    // Check if entry fits into bank\n    if (EntryBankElement::size() < (entry_offset+entry_size)) {\n      // If not, split\n      uint8_t buffer[100]; EntryElement(buffer).fromUser(users[i]);\n      uint32_t n1 = (EntryBankElement::size()-entry_offset);\n      uint32_t n2 = entry_size-n1;\n      if (0 != n1) {\n        // Copy first half\n        uint8_t *ptr = data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset);\n        memcpy(ptr, buffer, n1);\n      }\n      // advance bank counter\n      entry_bank++; entry_offset = 0;\n      // copy second half\n      uint8_t *ptr = data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset);\n      memcpy(ptr, buffer+n1, n2);\n      // Update offset\n      entry_offset += n2;\n    } else {\n      // when it fits, just add\n      EntryElement(data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset))\n          .fromUser(users[i]);\n      entry_offset += entry_size;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/d868uv_callsigndb.hh",
    "content": "#ifndef D868UVCALLSIGNDB_HH\n#define D868UVCALLSIGNDB_HH\n\n#include \"callsigndb.hh\"\n#include \"userdatabase.hh\"\n#include \"d868uv_codeplug.hh\"\n\n/** Represents and encodes the binary format for the call-sign database within a AnyTone AT-D868UV\n * radio.\n *\n * <table>\n *  <tr><th colspan=\"3\">Callsign database</th></tr>\n *  <tr><th>Start</th>    <th>Size</th>        <th>Content</th></tr>\n *  <tr><td>04000000</td> <td>max. 186a00</th> <td>Index of callsign entries. Follows the same\n *   weird format as @c D868UVCodeplug::contact_map_t. Sorted by ID. Empty entries set to\n *   0xffffffffffffffff.</td></tr>\n *  <tr><td>044c0000</td> <td>unknown</td>     <td>Database limits, see @c limits_t.</td></tr>\n *  <tr><td>04500000</td> <td>unknown</td>     <td>The actual DB entries, each entry is of\n *   variable size but shares the same header, see @c entry_t. Order arbitrary.\n *   Filled with 0x00.</td></tr>\n * </table>\n *\n * @ingroup d868uv */\nclass D868UVCallsignDB : public CallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Represents the header of an entry in the callsign database.\n   * Each entry is of variable size. That is, every entry has a common header containing the\n   * number, call-type etc. All strings, that is contact name, city, callsign, state, country and\n   * comment are 0x00 terminated strings in a lists.\n   *\n   * Max length for name is 16, city is 15, callsign is 8, state is 16, country is 16 and\n   * comment is 16, excluding terminating 0x00.\n   */\n  class EntryElement: public Codeplug::Element\n  {\n  public:\n    /** Notification tones for callsign entry. */\n    enum class RingTone {\n      Off = 0,\n      Tone = 1,\n      Online = 2\n    };\n    /** Possible call types. */\n    enum class CallType {\n      Private = 0, Group = 1, All = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    EntryElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit EntryElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Sets the call type. */\n    virtual void setCallType(DMRContact::Type type);\n    /** Sets the DMR ID number. */\n    virtual void setNumber(unsigned num);\n    /** Set/clear friend flag. */\n    virtual void setFriendFlag(bool set);\n    /** Sets the ring tone. */\n    virtual void setRingTone(RingTone tone);\n    /** Sets the entry content. */\n    virtual void setContent(const QString &name, const QString &city, const QString &call,\n                            const QString &state, const QString &country, const QString &comment);\n\n    /** Constructs a database entry from the given user.\n     * @returns The size of the entry. */\n    virtual unsigned fromUser(const UserDatabase::User &user);\n\n    /** Computes the size of the database entry for the given user. */\n    static unsigned size(const UserDatabase::User &user);\n\n  public:\n    /** Some limits for the entry. */\n    struct Limit: Element::Limit {\n      static constexpr unsigned int headerLength()  { return  6; } ///< Header length (fixed).\n      static constexpr unsigned int nameLength()    { return 16; } ///< Maximum name length.\n      static constexpr unsigned int cityLength()    { return 15; } ///< Maximum city length.\n      static constexpr unsigned int callLength()    { return  8; } ///< Maximum call length.\n      static constexpr unsigned int stateLength()   { return 16; } ///< Maximum state length.\n      static constexpr unsigned int countryLength() { return 16; } ///< Maximum country length.\n      static constexpr unsigned int commentLength() { return  0; } ///< Maximum comment length.\n      /** Maximum entry size. */\n      static constexpr unsigned int totalLength()   {\n        return headerLength() + nameLength()+1 + cityLength()+1 + callLength()+1 + stateLength()+1\n            + countryLength()+1 + commentLength()+1; }\n    };\n\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int callType()   { return 0x0000; }\n      static constexpr unsigned int number()     { return 0x0001; }\n      static constexpr Bit          friendFlag() { return {0x0005, 4}; }\n      static constexpr Bit          ringTone()   { return { 0x0005, 0}; }\n      static constexpr unsigned int name()       { return Limit::headerLength(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents a bank of call-sign DB entries. */\n  class EntryBankElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EntryBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit EntryBankElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x000186a0; }\n\n    void clear();\n\n    /** Returns the i-th element of the bank. */\n    uint8_t *entry(unsigned int i) const;\n  };\n\n\n  /** Same index entry used by the codeplug to map normal digital contacts to an contact index. Here\n   * it maps to the byte offset within the database entries. */\n  typedef D868UVCodeplug::ContactMapElement IndexEntryElement;\n\n  /** Represents a bank of index entries. */\n  class IndexBankElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    IndexBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit IndexBankElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0001f400; }\n\n    void clear();\n\n    /** Returns the i-th element of the bank. */\n    uint8_t *entry(unsigned int i) const;\n  };\n\n\n  /** Stores some basic limits of the callsign db. */\n  class LimitsElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    LimitsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    LimitsElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Resets the limits. */\n    void clear();\n\n    /** Returns the number of entries in the DB. */\n    virtual unsigned count() const;\n    /** Sets the number of entries. */\n    virtual void setCount(unsigned count);\n\n    /** Returns the end-of-db address. */\n    virtual unsigned endOfDB() const;\n    /** Sets the end-of-db address. */\n    virtual void setEndOfDB(unsigned addr);\n    /** Sets the total size of the DB (updated end-of-db address). */\n    virtual void setTotalSize(unsigned size);\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()   { return 0x0000; }\n      static constexpr unsigned int endOfDB() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  /** Constructor, does not allocate any memory yet. */\n  explicit D868UVCallsignDB(QObject *parent=nullptr);\n\n  /** Tries to encode as many entries of the given user-database. */\n  bool encode(UserDatabase *db, const Flags &selection=Flags(),\n              const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits for the call-sign DB. */\n  struct Limit {\n    /// Specifies the max number of entries in the DB.\n    static constexpr unsigned int entries() { return 200000; }\n  };\n\nprotected:\n  /** Some internal used offsets within the DB. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int index()                { return 0x04000000; }\n    static constexpr unsigned int betweenIndexBanks()    { return 0x00040000; }\n    static constexpr unsigned int callsigns()            { return 0x04500000; }\n    static constexpr unsigned int betweenCallsignBanks() { return 0x00040000; }\n    static constexpr unsigned int limits()               { return 0x044C0000; }\n    /// @endcond\n  };\n};\n\n#endif // D868UVCALLSIGNDB_HH\n"
  },
  {
    "path": "lib/d868uv_codeplug.cc",
    "content": "#include \"d868uv_codeplug.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"channel.hh\"\n#include \"gpssystem.hh\"\n#include \"smsextension.hh\"\n#include \"config.h\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n#include \"intermediaterepresentation.hh\"\n#include <cmath>\n\n#include <QTimeZone>\n#include <QtEndian>\n#include <QSet>\n\n/* ******************************************************************************************** *\n * Implementation of D868UVCodeplug::Color\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nD868UVCodeplug::Color::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case White: return AnytoneDisplaySettingsExtension::Color::White;\n  case Red:   return AnytoneDisplaySettingsExtension::Color::Red;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::White;\n}\n\nuint8_t\nD868UVCodeplug::Color::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Red;\n  default: break;\n  }\n  return (uint8_t) White;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D868UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nD868UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nD868UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : AnytoneCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\n\nbool\nD868UVCodeplug::ChannelElement::ranging() const {\n  return getBit(Offset::ranging());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableRanging(bool enable) {\n  setBit(Offset::ranging(), enable);\n}\n\nbool\nD868UVCodeplug::ChannelElement::throughMode() const {\n  return getBit(Offset::throughMode());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableThroughMode(bool enable) {\n  setBit(Offset::throughMode(), enable);\n}\n\nbool\nD868UVCodeplug::ChannelElement::dataACK() const {\n  return !getBit(Offset::dataACK());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableDataACK(bool enable) {\n  setBit(Offset::dataACK(), !enable);\n}\n\nD868UVCodeplug::ChannelElement::APRSType\nD868UVCodeplug::ChannelElement::txAPRSType() const {\n  return (APRSType)getUInt8(Offset::txAPRSType());\n}\nvoid\nD868UVCodeplug::ChannelElement::setTXAPRSType(APRSType aprsType) {\n  setUInt8(Offset::txAPRSType(), (uint8_t)aprsType);\n}\n\nunsigned\nD868UVCodeplug::ChannelElement::digitalAPRSSystemIndex() const {\n  return getUInt8(Offset::digitalAPRSSystemIndex());\n}\nvoid\nD868UVCodeplug::ChannelElement::setDigitalAPRSSystemIndex(unsigned idx) {\n  setUInt8(Offset::digitalAPRSSystemIndex(), idx);\n}\n\n\nD868UVCodeplug::ChannelElement::DMREncryptionType\nD868UVCodeplug::ChannelElement::dmrEncryptionType() const {\n  return getBit(Offset::dmrEncryptionType()) ?\n        DMREncryptionType::Enhanced : DMREncryptionType::Basic;\n}\n\nvoid\nD868UVCodeplug::ChannelElement::setDMREncryptionType(DMREncryptionType type) {\n  setBit(Offset::dmrEncryptionType(), DMREncryptionType::Enhanced == type);\n}\n\n\nbool\nD868UVCodeplug::ChannelElement::hasDMREncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::dmrEncryptionKey());\n}\nunsigned\nD868UVCodeplug::ChannelElement::dmrEncryptionKeyIndex() const {\n  return getUInt8(Offset::dmrEncryptionKey()) - 1;\n}\nvoid\nD868UVCodeplug::ChannelElement::setDMREncryptionKeyIndex(unsigned idx) {\n  setUInt8(Offset::dmrEncryptionKey(), idx+1);\n}\nvoid\nD868UVCodeplug::ChannelElement::clearDMREncryptionKeyIndex() {\n  setUInt8(Offset::dmrEncryptionKey(), 0);\n}\n\nbool\nD868UVCodeplug::ChannelElement::multipleKeyEncryption() const {\n  return getBit(Offset::multipleKeyEncryption());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableMultipleKeyEncryption(bool enable) {\n  setBit(Offset::multipleKeyEncryption(), enable);\n}\n\nbool\nD868UVCodeplug::ChannelElement::randomKey() const {\n  return getBit(Offset::randomKey());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableRandomKey(bool enable) {\n  setBit(Offset::randomKey(), enable);\n}\nbool\nD868UVCodeplug::ChannelElement::sms() const {\n  return !getBit(Offset::sms());\n}\nvoid\nD868UVCodeplug::ChannelElement::enableSMS(bool enable) {\n  setBit(Offset::sms(), !enable);\n}\n\nChannel *\nD868UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const {\n  Channel *ch = AnytoneCodeplug::ChannelElement::toChannelObj(ctx);\n  if (nullptr == ch)\n    return nullptr;\n\n  if (ch->is<DMRChannel>()) {\n    DMRChannel *dch = ch->as<DMRChannel>();\n    dch->extended()->enableSMS(sms());\n    dch->extended()->enableDataConfirm(dataACK());\n\n    if (AnytoneDMRChannelExtension *ext = dch->anytoneChannelExtension()) {\n      ext->enableThroughMode(throughMode());\n    }\n  }\n\n  return ch;\n}\n\nbool\nD868UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const {\n  if (! AnytoneCodeplug::ChannelElement::linkChannelObj(c, ctx))\n    return false;\n\n  if (c->is<DMRChannel>()) {\n    DMRChannel *dc = c->as<DMRChannel>();\n    // Link to GPS system\n    if ((APRSType::Off != txAPRSType())  && (! ctx.has<DMRAPRSSystem>(digitalAPRSSystemIndex())))\n      logWarn() << \"Cannot link to DMR APRS system index \" << digitalAPRSSystemIndex() << \": undefined DMR APRS system.\";\n    else if (ctx.has<DMRAPRSSystem>(digitalAPRSSystemIndex()))\n      dc->setAPRS(ctx.get<DMRAPRSSystem>(digitalAPRSSystemIndex()));\n    // Link to encryption key (only basic implemented)\n    if (hasDMREncryptionKeyIndex() && (DMREncryptionType::Basic == dmrEncryptionType())) {\n      if (ctx.has<BasicEncryptionKey>(dmrEncryptionKeyIndex())) {\n        auto cex = dc->commercialExtension();\n        if (nullptr == cex)\n          dc->setCommercialExtension(cex = new CommercialChannelExtension());\n        cex->setEncryptionKey(ctx.get<BasicEncryptionKey>(dmrEncryptionKeyIndex()));\n      } else {\n        logWarn() << \"Cannot link DMR encryption: no key with index \"\n                  << dmrEncryptionKeyIndex() << \" found.\";\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nD868UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  if (! AnytoneCodeplug::ChannelElement::fromChannelObj(c, ctx))\n    return false;\n\n  if (c->is<DMRChannel>()) {\n    const DMRChannel *dc = c->as<const DMRChannel>();\n    // Set GPS system index\n    if (dc->aprs() && dc->aprs()->is<DMRAPRSSystem>()) {\n      setDigitalAPRSSystemIndex(ctx.index(dc->aprs()->as<DMRAPRSSystem>()));\n      setTXAPRSType(APRSType::DMR);\n      enableRXAPRS(false);\n    } else {\n      setTXAPRSType(APRSType::Off);\n      enableRXAPRS(false);\n    }\n\n    enableSMS(dc->extended()->sms());\n    enableDataACK(dc->extended()->dataConfirm());\n\n    clearDMREncryptionKeyIndex();\n    bool hasStrongEncryption = ctx.config()->settings()->anytoneExtension() &&\n        (AnytoneDMRSettingsExtension::EncryptionType::AES ==\n         ctx.config()->settings()->anytoneExtension()->dmrSettings()->encryption());\n\n    // Handle commercial extension\n    if (auto cex = dc->commercialExtension()) {\n      if (cex->encryptionKey() && cex->encryptionKey()->is<BasicEncryptionKey>() && (! hasStrongEncryption)) {\n        auto key = cex->encryptionKey()->as<BasicEncryptionKey>();\n        setDMREncryptionType(DMREncryptionType::Basic);\n        setDMREncryptionKeyIndex(ctx.index(key));\n      }\n    }\n\n    // Handle extension\n    if (AnytoneDMRChannelExtension *ext = dc->anytoneChannelExtension()) {\n      enableThroughMode(ext->throughMode());\n    }\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D868UVCodeplug::GeneralSettingsElement::KeyFunction\n * ******************************************************************************************** */\nuint8_t\nD868UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;\n  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;\n  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;\n  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;\n  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;\n  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;\n  case AnytoneKeySettingsExtension::KeyFunction::VOX:               return (uint8_t)KeyFunction::VOX;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;\n  case AnytoneKeySettingsExtension::KeyFunction::SubPTT:            return (uint8_t)KeyFunction::SubPTT;\n  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;\n  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;\n  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;\n  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;\n  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;\n  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;\n  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;\n  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;\n  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;\n  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;\n  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;\n  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;\n  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;\n  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;\n  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;\n  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;\n  default:                                                          return (uint8_t)KeyFunction::Off;\n  }\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) {\n  switch ((KeyFunction::KeyFunctionCode)code) {\n  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;\n  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;\n  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;\n  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;\n  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;\n  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;\n  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;\n  case KeyFunction::VOX:               return AnytoneKeySettingsExtension::KeyFunction::VOX;\n  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;\n  case KeyFunction::SubPTT:            return AnytoneKeySettingsExtension::KeyFunction::SubPTT;\n  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;\n  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;\n  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;\n  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;\n  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;\n  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;\n  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;\n  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;\n  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;\n  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;\n  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;\n  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;\n  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;\n  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;\n  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;\n  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;\n  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;\n  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;\n  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;\n  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;\n  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;\n  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;\n  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;\n  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;\n  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;\n  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;\n  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;\n  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;\n  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;\n  default:                             return AnytoneKeySettingsExtension::KeyFunction::Off;\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D868UVCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nD868UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass....\n}\n\nD868UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::clear() {\n  AnytoneCodeplug::GeneralSettingsElement::clear();\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::keyToneEnabled() const {\n  return 0x00 != getUInt8(Offset::enableKeyTone());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) {\n  setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00);\n}\n\nAnytonePowerSaveSettingsExtension::PowerSave\nD868UVCodeplug::GeneralSettingsElement::powerSave() const {\n  return (AnytonePowerSaveSettingsExtension::PowerSave)getUInt8(Offset::powerSaveMode());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setPowerSave(AnytonePowerSaveSettingsExtension::PowerSave mode) {\n  setUInt8(Offset::powerSaveMode(), (unsigned int)mode);\n}\n\nLevel\nD868UVCodeplug::GeneralSettingsElement::voxLevel() const {\n  return Level::fromValue(getUInt8(Offset::voxLevel()), Limit::vox());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setVOXLevel(Level level) {\n  setUInt8(Offset::voxLevel(), level.mapTo(Limit::vox()));\n}\n\nInterval\nD868UVCodeplug::GeneralSettingsElement::voxDelay() const {\n  return Interval::fromMilliseconds(100 + 500*((unsigned)getUInt8(Offset::voxDelay())));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setVOXDelay(Interval intv) {\n  setUInt8(Offset::voxDelay(), (std::max(100ULL, intv.milliseconds())-100)/500);\n}\n\nAnytoneSettingsExtension::VFOScanType\nD868UVCodeplug::GeneralSettingsElement::vfoScanType() const {\n  return (AnytoneSettingsExtension::VFOScanType) getUInt8(Offset::vfoScanType());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setVFOScanType(AnytoneSettingsExtension::VFOScanType type) {\n  setUInt8(Offset::vfoScanType(), (unsigned)type);\n}\n\nLevel\nD868UVCodeplug::GeneralSettingsElement::dmrMicGain() const {\n  return Level::fromValue(getUInt8(Offset::dmrMicGain()), Limit::micGain());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDMRMicGain(Level gain) {\n  setUInt8(Offset::dmrMicGain(), gain.mapTo(Limit::micGain()));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyAShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyBShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyCShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKey1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKey2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyALong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyBLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKeyCLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKey1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD868UVCodeplug::GeneralSettingsElement::funcKey2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));\n}\n\nInterval\nD868UVCodeplug::GeneralSettingsElement::longPressDuration() const {\n  return Interval::fromSeconds(((unsigned)getUInt8(Offset::longPressDuration()))+1);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval ms) {\n  setUInt8(Offset::longPressDuration(), std::max(1ULL,ms.seconds())-1);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::vfoModeA() const {\n  return getUInt8(Offset::vfoModeA());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) {\n  setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::vfoModeB() const {\n  return getUInt8(Offset::vfoModeB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) {\n  setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::memoryZoneA() const {\n  return getUInt8(Offset::memZoneA());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMemoryZoneA(unsigned zone) {\n  setUInt8(Offset::memZoneA(), zone);\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::memoryZoneB() const {\n  return getUInt8(Offset::memZoneB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMemoryZoneB(unsigned zone) {\n  setUInt8(Offset::memZoneB(), zone);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::recording() const {\n  return getUInt8(Offset::enableRecoding());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) {\n  setUInt8(Offset::enableRecoding(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::brightness() const {\n  return (getUInt8(Offset::displayBrightness())*10)/4;\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setBrightness(unsigned level) {\n  setUInt8(Offset::displayBrightness(), (level*4)/10);\n}\n\n\nbool\nD868UVCodeplug::GeneralSettingsElement::backlightPermanent() const {\n  return backlightDuration().isInfinite();\n}\n\nInterval\nD868UVCodeplug::GeneralSettingsElement::backlightDuration() const {\n  switch ((BacklightDuration)getUInt8(Offset::backlightDuration())) {\n  case BacklightDuration::Infinite: return Interval::infinity();\n  case BacklightDuration::_5s:  return Interval::fromSeconds(5);\n  case BacklightDuration::_10s: return Interval::fromSeconds(10);\n  case BacklightDuration::_15s: return Interval::fromSeconds(15);\n  case BacklightDuration::_20s: return Interval::fromSeconds(20);\n  case BacklightDuration::_25s: return Interval::fromSeconds(25);\n  case BacklightDuration::_30s: return Interval::fromSeconds(30);\n  case BacklightDuration::_1min: return Interval::fromMinutes(1);\n  case BacklightDuration::_2min: return Interval::fromMinutes(2);\n  case BacklightDuration::_3min: return Interval::fromMinutes(3);\n  case BacklightDuration::_4min: return Interval::fromMinutes(4);\n  case BacklightDuration::_5min: return Interval::fromMinutes(5);\n  }\n  return Interval::infinity();\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::setBacklightDuration(Interval intv) {\n  if (intv <= Interval::fromSeconds(5))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_5s);\n  else if (intv <= Interval::fromSeconds(10))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_10s);\n  else if (intv <= Interval::fromSeconds(15))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_15s);\n  else if (intv <= Interval::fromSeconds(20))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_20s);\n  else if (intv <= Interval::fromSeconds(25))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_25s);\n  else if (intv <= Interval::fromSeconds(30))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_30s);\n  else if (intv <= Interval::fromMinutes(1))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_1min);\n  else if (intv <= Interval::fromMinutes(2))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_2min);\n  else if (intv <= Interval::fromMinutes(3))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_3min);\n  else if (intv <= Interval::fromMinutes(4))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_4min);\n  else if (intv <= Interval::fromMinutes(5))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_5min);\n  else\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::Infinite);\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableBacklightPermanent() {\n  setBacklightDuration(Interval::infinity());\n}\n\n\nbool\nD868UVCodeplug::GeneralSettingsElement::gps() const {\n  return getUInt8(Offset::gpsEnable());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableGPS(bool enable) {\n  setUInt8(Offset::gpsEnable(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::smsAlert() const {\n  return getUInt8(Offset::smsAlert());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableSMSAlert(bool enable) {\n  setUInt8(Offset::smsAlert(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::activeChannelB() const {\n  return getUInt8(Offset::activeChannelB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableActiveChannelB(bool enable) {\n  setUInt8(Offset::activeChannelB(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::subChannel() const {\n  return getUInt8(Offset::subChannel());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableSubChannel(bool enable) {\n  setUInt8(Offset::subChannel(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::callAlert() const {\n  return getUInt8(Offset::callAlert());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableCallAlert(bool enable) {\n  setUInt8(Offset::callAlert(), (enable ? 0x01 : 0x00));\n}\n\nQTimeZone\nD868UVCodeplug::GeneralSettingsElement::gpsTimeZone() const {\n  return QTimeZone((((int)getUInt8(Offset::gpsTimeZone()))-12)*3600);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) {\n  int offset = zone.offsetFromUtc(QDateTime::currentDateTime());\n  setUInt8(Offset::gpsTimeZone(), (12 + offset/3600));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::dmrTalkPermit() const {\n  return getBit(Offset::talkPermit(), 0);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableDMRTalkPermit(bool enable) {\n  return setBit(Offset::talkPermit(), 0, enable);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::fmTalkPermit() const {\n  return getBit(Offset::talkPermit(), 1);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableFMTalkPermit(bool enable) {\n  return setBit(Offset::talkPermit(), 1, enable);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::dmrResetTone() const {\n  return getUInt8(Offset::dmrResetTone());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableDMRResetTone(bool enable) {\n  return setUInt8(Offset::dmrResetTone(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneAudioSettingsExtension::VoxSource\nD868UVCodeplug::GeneralSettingsElement::voxSource() const {\n  return (AnytoneAudioSettingsExtension::VoxSource)getUInt8(Offset::voxSource());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setVOXSource(AnytoneAudioSettingsExtension::VoxSource source) {\n  setUInt8(Offset::voxSource(), (unsigned)source);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::idleChannelTone() const {\n  return getUInt8(Offset::idleChannelTone());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) {\n  return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD868UVCodeplug::GeneralSettingsElement::menuExitTime() const {\n  return Interval::fromSeconds(5 + 5*((unsigned) getUInt8(Offset::menuExitTime())));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMenuExitTime(Interval intv) {\n  setUInt8(Offset::menuExitTime(), (std::max(5ULL, intv.seconds())-5)/5);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::startupTone() const {\n  return getUInt8(Offset::startupTone());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableStartupTone(bool enable) {\n  return setUInt8(Offset::startupTone(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::callEndPrompt() const {\n  return getUInt8(Offset::callEndPrompt());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableCallEndPrompt(bool enable) {\n  return setUInt8(Offset::callEndPrompt(), (enable ? 0x01 : 0x00));\n}\n\nLevel\nD868UVCodeplug::GeneralSettingsElement::maxSpeakerVolume() const {\n  return Level::fromValue(getUInt8(Offset::maxSpeakerVolume()), Limit::volume());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMaxSpeakerVolume(Level level) {\n  setUInt8(Offset::maxSpeakerVolume(), level.mapTo(Limit::volume()));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::getGPSPosition() const {\n  return getUInt8(Offset::getGPSPosition());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableGetGPSPosition(bool enable) {\n  return setUInt8(Offset::getGPSPosition(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::volumeChangePrompt() const {\n  return 0x01 == getUInt8(Offset::volumeChangePrompt());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableVolumeChangePrompt(bool enable) {\n  setUInt8(Offset::volumeChangePrompt(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionA() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction) getUInt8(Offset::autoRepeaterDirA());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirA(), (unsigned)dir);\n}\n\nAnytoneDisplaySettingsExtension::LastCallerDisplayMode\nD868UVCodeplug::GeneralSettingsElement::lastCallerDisplayMode() const {\n  return (AnytoneDisplaySettingsExtension::LastCallerDisplayMode)getUInt8(Offset::lastCallerDisplay());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode) {\n  setUInt8(Offset::lastCallerDisplay(), (unsigned)mode);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::displayClock() const {\n  return getUInt8(Offset::showClock());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableDisplayClock(bool enable) {\n  setUInt8(Offset::showClock(), (enable ? 0x01 : 0x00));\n}\n\nLevel\nD868UVCodeplug::GeneralSettingsElement::maxHeadphoneVolume() const {\n  return Level::fromValue((unsigned)getUInt8(Offset::maxHeadPhoneVolume()), Limit::volume());\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMaxHeadphoneVolume(Level max) {\n  setUInt8(Offset::maxHeadPhoneVolume(), max.mapTo(Limit::volume()));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::enhanceAudio() const {\n  return getUInt8(Offset::enhanceAudio());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableEnhancedAudio(bool enable) {\n  setUInt8(Offset::enhanceAudio(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyUHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanUHF()))*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::minVFOScanUHF(), freq.inHz()/10);\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyUHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanUHF()))*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::maxVFOScanUHF(), freq.inHz()/10);\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::minVFOScanFrequencyVHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::minVFOScanVHF()))*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMinVFOScanFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::minVFOScanVHF(), freq.inHz()/10);\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::maxVFOScanFrequencyVHF() const {\n  return Frequency::fromHz(((unsigned)getUInt32_le(Offset::maxVFOScanVHF()))*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setMaxVFOScanFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::maxVFOScanVHF(), freq.inHz()/10);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexUHF() const {\n  return 0xff != autoRepeaterOffsetFrequencyIndexUHF();\n}\nunsigned\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexUHF() const {\n  return getUInt8(Offset::autoRepOffsetUHF());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx) {\n  setUInt8(Offset::autoRepOffsetUHF(), idx);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexUHF() {\n  setAutoRepeaterOffsetFrequenyIndexUHF(0xff);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::hasAutoRepeaterOffsetFrequencyIndexVHF() const {\n  return 0xff != autoRepeaterOffsetFrequencyIndexVHF();\n}\nunsigned\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterOffsetFrequencyIndexVHF() const {\n  return getUInt8(Offset::autoRepOffsetVHF());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx) {\n  setUInt8(Offset::autoRepOffsetVHF(), idx);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::clearAutoRepeaterOffsetFrequencyIndexVHF() {\n  setAutoRepeaterOffsetFrequenyIndexVHF(0xff);\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::callToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::callToneTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::callToneDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setCallToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::callToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::callToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::idleToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double frequency = getUInt16_le(Offset::idleToneTones()+2*i);\n    unsigned int duration  = getUInt16_le(Offset::idleToneDurations()+2*i);\n    if (duration) tones.append({frequency, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setIdleToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::idleToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::idleToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD868UVCodeplug::GeneralSettingsElement::resetToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double frequency = getUInt16_le(Offset::resetToneTones()+2*i);\n    unsigned int duration  = getUInt16_le(Offset::resetToneDurations()+2*i);\n    if (duration) tones.append({frequency, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setResetToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::resetToneTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::resetToneDurations()+2*i, tones.at(i).second);\n  }\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::recordingDelay() const {\n  return ((unsigned)getUInt8(Offset::recordingDelay()))*200;\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setRecodringDelay(unsigned ms) {\n  setUInt8(Offset::recordingDelay(), ms/200);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::displayCall() const {\n  return getUInt8(Offset::callDisplayMode());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableDisplayCall(bool enable) {\n  setUInt8(Offset::callDisplayMode(), (enable ? 0x01 : 0x00));\n}\n\n\nAnytoneDisplaySettingsExtension::Color\nD868UVCodeplug::GeneralSettingsElement::callDisplayColor() const {\n  return Color::decode(getUInt8(Offset::callColor()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::callColor(), Color::encode(color));\n}\n\nInterval\nD868UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const {\n  return Interval::fromSeconds(getUInt8(Offset::gpsPeriod()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval sec) {\n  setUInt8(Offset::gpsPeriod(), sec.seconds());\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::showCurrentContact() const {\n  return getUInt8(Offset::showCurrentContact());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) {\n  setUInt8(Offset::showCurrentContact(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::keyToneLevelAdjustable() const {\n  return keyToneLevel().isNull();\n}\nLevel\nD868UVCodeplug::GeneralSettingsElement::keyToneLevel() const {\n  return Level::fromValue(getUInt8(Offset::keyToneLevel()), Limit::keyTone());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setKeyToneLevel(Level level) {\n  setUInt8(Offset::keyToneLevel(), level.mapTo(Limit::keyTone()));\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setKeyToneLevelAdjustable() {\n  setUInt8(Offset::keyToneLevel(), 0);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const {\n  return getUInt8(Offset::gpsUnits());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) {\n  setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::knobLock() const {\n  return getBit(Offset::knobLock(), 0);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {\n  setBit(Offset::knobLock(), 0, enable);\n}\nbool\nD868UVCodeplug::GeneralSettingsElement::keypadLock() const {\n  return getBit(Offset::keypadLock(), 1);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) {\n  setBit(Offset::keypadLock(), 1, enable);\n}\nbool\nD868UVCodeplug::GeneralSettingsElement::sidekeysLock() const {\n  return getBit(Offset::sideKeyLock(), 3);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) {\n  setBit(Offset::sideKeyLock(), 3, enable);\n}\nbool\nD868UVCodeplug::GeneralSettingsElement::keyLockForced() const {\n  return getBit(Offset::forceKeyLock(), 4);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) {\n  setBit(Offset::forceKeyLock(), 4, enable);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::showLastHeard() const {\n  return getUInt8(Offset::showLastHeard());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) {\n  setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10);\n}\nFrequency\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10);\n}\n\nFrequency\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10);\n}\nFrequency\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10);\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nD868UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirB(), (unsigned)dir);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::defaultChannel() const {\n  return getUInt8(Offset::defaultChannels());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) {\n  setUInt8(Offset::defaultChannels(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const {\n  return getUInt8(Offset::defaultZoneA());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned idx) {\n  setUInt8(Offset::defaultZoneA(), idx);\n}\n\nunsigned\nD868UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const {\n  return getUInt8(Offset::defaultZoneB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned idx) {\n  setUInt8(Offset::defaultZoneB(), idx);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const {\n  return 0xff == defaultChannelAIndex();\n}\nunsigned\nD868UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const {\n  return getUInt8(Offset::defaultChannelA());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelA(), idx);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() {\n  setDefaultChannelAIndex(0xff);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const {\n  return 0xff == defaultChannelBIndex();\n}\nunsigned\nD868UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const {\n  return getUInt8(Offset::defaultChannelB());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelB(), idx);\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() {\n  setDefaultChannelBIndex(0xff);\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::keepLastCaller() const {\n  return getUInt8(Offset::keepLastCaller());\n}\nvoid\nD868UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) {\n  setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  setGPSUpdatePeriod(Interval::fromSeconds(5));\n  // Set measurement system based on system locale (0x00==Metric)\n  enableGPSUnitsImperial(QLocale::ImperialSystem == QLocale::system().measurementSystem());\n  // Set default VOX sensitivity\n  setVOXLevel(ctx.config()->settings()->audio()->vox());\n  setVOXDelay(ctx.config()->settings()->audio()->voxDelay());\n  setMaxHeadphoneVolume(ctx.config()->settings()->audio()->maxHeadphoneVolume());\n\n  // Encode tone settings\n  enableIdleChannelTone(ctx.config()->settings()->tone()->channelIdle().testAnyFlags(\n    Channel::Type::FM | Channel::Type::DMR));\n  setKeyToneLevel(ctx.config()->settings()->tone()->keyToneVolume());\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Power save settings\n  setPowerSave(ext->powerSaveSettings()->powerSave());\n\n  // Encode key settings\n  enableKnobLock(ext->keySettings()->knobLockEnabled());\n  enableKeypadLock(ext->keySettings()->keypadLockEnabled());\n  enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled());\n  enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled());\n\n  // Encode audio settings\n  setVOXSource(ext->audioSettings()->voxSource());\n\n  // Encode display settings\n  setBacklightDuration(ext->displaySettings()->backlightDuration());\n  enableShowCurrentContact(ext->displaySettings()->showContact());\n\n  // Check encryption type\n  if (AnytoneDMRSettingsExtension::EncryptionType::AES == ext->dmrSettings()->encryption()) {\n    logInfo() << \"D868UVE does not support AES/ARC4 encryption.\";\n  }\n\n  return true;\n}\n\nbool\nD868UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  ctx.config()->settings()->audio()->setVox(voxLevel());\n  ctx.config()->settings()->audio()->setVOXDelay(voxDelay());\n  ctx.config()->settings()->audio()->setMaxHeadphoneVolume(this->maxHeadphoneVolume());\n\n  // Decode tone settings\n  ctx.config()->settings()->tone()->setChannelIdle(\n    idleChannelTone() ? (Channel::Type::DMR | Channel::Type::FM) : Channel::Type::None\n  );\n  ctx.config()->settings()->tone()->setKeyToneVolume(keyToneLevel());\n\n  // Get or add settings extension\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension()) {\n    ext = ctx.config()->settings()->anytoneExtension();\n  } else {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Decode power save settings:\n  ext->powerSaveSettings()->setPowerSave(powerSave());\n\n  // Decode key settings\n  ext->keySettings()->enableKnobLock(this->knobLock());\n  ext->keySettings()->enableKeypadLock(this->keypadLock());\n  ext->keySettings()->enableSideKeysLock(this->sidekeysLock());\n  ext->keySettings()->enableForcedKeyLock(this->keyLockForced());\n\n  // Decode audio settings\n  ext->audioSettings()->setVOXSource(voxSource());\n\n  // Decode display settings\n  ext->displaySettings()->enableShowContact(this->showCurrentContact());\n  ext->displaySettings()->setBacklightDuration(backlightDuration());\n\n  // Set encryption type\n  ext->dmrSettings()->setEncryption(AnytoneDMRSettingsExtension::EncryptionType::DMR);\n\n  return true;\n}\n\n\nbool\nD868UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err))\n    return false;\n\n  // Get or add settings extension\n  AnytoneSettingsExtension *ext = settings->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    settings->setAnytoneExtension(ext);\n  }\n\n  // Nothing to link\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D868UVCodeplug\n * ******************************************************************************************** */\nD868UVCodeplug::D868UVCodeplug(const QString &label, QObject *parent)\n  : AnytoneCodeplug(label, parent)\n{\n  // pass...\n}\n\nD868UVCodeplug::D868UVCodeplug(QObject *parent)\n  : AnytoneCodeplug(\"AnyTone AT-D868UV Codeplug\", parent)\n{\n  // pass...\n}\n\nvoid\nD868UVCodeplug::allocateUpdated() {\n  this->allocateVFOSettings();\n\n  // General config\n  this->allocateGeneralSettings();\n  this->allocateZoneChannelList();\n  this->allocateDTMFNumbers();\n  this->allocateBootSettings();\n  this->allocateRepeaterOffsetFrequencies();\n\n  this->allocateGPSSystems();\n\n  this->allocatEnhancedEncryptionKeys();\n\n  this->allocateHotKeySettings();\n  this->allocateAlarmSettings();\n  this->allocateFMBroadcastSettings();\n\n  this->allocate5ToneIDs();\n  this->allocate5ToneFunctions();\n  this->allocate5ToneSettings();\n\n  this->allocate2ToneIDs();\n  this->allocate2ToneFunctions();\n  this->allocate2ToneSettings();\n\n  this->allocateDTMFSettings();\n}\n\nvoid\nD868UVCodeplug::allocateForEncoding() {\n  this->allocateChannels();\n  this->allocateZones();\n  this->allocateContacts();\n  this->allocateAnalogContacts();\n  this->allocateRXGroupLists();\n  this->allocateScanLists();\n  this->allocateRadioIDs();\n  this->allocateSMSMessages();\n\n  // Encryption\n  this->allocatDMREncryptionKeys();\n}\n\nvoid\nD868UVCodeplug::allocateForDecoding() {\n  this->allocateRadioIDs();\n  this->allocateChannels();\n  this->allocateZones();\n  this->allocateContacts();\n  this->allocateAnalogContacts();\n  this->allocateRXGroupLists();\n  this->allocateScanLists();\n\n  // General config\n  this->allocateGeneralSettings();\n  this->allocateZoneChannelList();\n  this->allocateBootSettings();\n  this->allocateRepeaterOffsetFrequencies();\n  this->allocateSMSMessages();\n\n  // Encryption\n  this->allocatDMREncryptionKeys();\n\n  // GPS settings\n  this->allocateGPSSystems();\n}\n\n\nbool\nD868UVCodeplug::allocateBitmaps() {\n  // Channel bitmap\n  image(0).addElement(Offset::channelBitmap(), ChannelBitmapElement::size());\n  // Zone bitmap\n  image(0).addElement(Offset::zoneBitmap(), ZoneBitmapElement::size());\n  // Contacts bitmap\n  image(0).addElement(Offset::contactBitmap(), ContactBitmapElement::size());\n  // Analog contacts bytemap\n  image(0).addElement(Offset::dtmfContactBytemap(), DTMFContactBytemapElement::size());\n  // RX group list bitmaps\n  image(0).addElement(Offset::groupListBitmap(), GroupListBitmapElement::size());\n  // Scan list bitmaps\n  image(0).addElement(Offset::scanListBitmap(), ScanListBitmapElement::size());\n  // Radio IDs bitmaps\n  image(0).addElement(Offset::radioIDBitmap(), RadioIDBitmapElement::size());\n  // Message bitmaps\n  image(0).addElement(Offset::messageBytemap(), MessageBytemapElement::size());\n  // Status messages\n  image(0).addElement(Offset::statusMessageBitmap(), StatusMessageBitmapElement::size());\n  // FM Broadcast bitmaps\n  image(0).addElement(Offset::wfmChannelBitmap(), WFMChannelBitmapElement::size());\n  // 5-Tone function bitmaps\n  image(0).addElement(Offset::fiveToneIdBitmap(), FiveToneIDBitmapElement::size());\n  // 2-Tone function bitmaps\n  image(0).addElement(Offset::twoToneIdBitmap(), TwoToneIDBitmapElement::size());\n  image(0).addElement(Offset::twoToneFunctionBitmap(), TwoToneFunctionBitmapElement::size());\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::setBitmaps(Context& ctx)\n{\n  // Mark first radio ID as valid\n  RadioIDBitmapElement radioid_bitmap(data(Offset::radioIDBitmap()));\n  unsigned int num_radio_ids = std::min(Limit::numRadioIDs(), ctx.count<DMRRadioID>());\n  radioid_bitmap.clear(); radioid_bitmap.enableFirst(num_radio_ids);\n\n  // Mark valid channels (set bit)\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  unsigned int num_channels = std::min(Limit::numChannels(), ctx.count<Channel>());\n  channel_bitmap.clear(); channel_bitmap.enableFirst(num_channels);\n\n  // Mark valid contacts (clear bit)\n  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));\n  unsigned int num_contacts = std::min(Limit::numContacts(), ctx.count<DigitalContact>());\n  contact_bitmap.clear(); contact_bitmap.enableFirst(num_contacts);\n\n  // Mark valid analog contacts (clear bytes)\n  DTMFContactBytemapElement analog_contact_bitmap(data(Offset::dtmfContactBytemap()));\n  unsigned int num_dtmf_contacts = std::min(Limit::numDTMFContacts(), ctx.count<DTMFContact>());\n  analog_contact_bitmap.clear(); analog_contact_bitmap.enableFirst(num_dtmf_contacts);\n\n  // Mark valid zones (set bits)\n  ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap()));\n  unsigned int num_zones = std::min(Limit::numZones(), ctx.count<Zone>());\n  zone_bitmap.clear(); zone_bitmap.enableFirst(num_zones);\n\n  // Mark group lists\n  GroupListBitmapElement group_bitmap(data(Offset::groupListBitmap()));\n  unsigned int num_group_lists = std::min(Limit::numGroupLists(), ctx.count<RXGroupList>());\n  group_bitmap.clear(); group_bitmap.enableFirst(num_group_lists);\n\n  // Mark scan lists\n  ScanListBitmapElement scan_bitmap(data(Offset::scanListBitmap()));\n  unsigned int num_scan_lists = std::min(Limit::numScanLists(), ctx.count<ScanList>());\n  scan_bitmap.clear(); scan_bitmap.enableFirst(num_scan_lists);\n\n  // Mark SMS messages\n  MessageBytemapElement sms_bytemap(data(Offset::messageBytemap()));\n  unsigned int num_messages = std::min(Limit::numMessages(), ctx.count<SMSTemplate>());\n  sms_bytemap.clear(); sms_bytemap.enableFirst(num_messages);\n}\n\n\nConfig *\nD868UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  // Apply base preprocessing\n  auto intermediate = AnytoneCodeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot apply preprocessing for D868UVE.\";\n    return nullptr;\n  }\n\n  // Remove all but 16bit DMR encryption keys.\n  EncryptionKeyFilterVisitor filter(\n        { EncryptionKeyFilterVisitor::Filter(BasicEncryptionKey::staticMetaObject, 16, 16) });\n  if (! filter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot remove unsupported exncryption.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  // Remove DMR encryption keys, that are not 16bit wide.\n  return intermediate;\n}\n\n\nbool\nD868UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  if (! this->encodeRadioID(flags, ctx, err))\n    return false;\n\n  if (! this->encodeGeneralSettings(flags, ctx, err))\n    return false;\n\n  if (! this->encodeSMSMessages(flags, ctx, err))\n    return false;\n\n  if (! this->encodeRepeaterOffsetFrequencies(flags, ctx, err))\n    return false;\n\n  if (! this->encodeBootSettings(flags, ctx, err))\n    return false;\n\n  if (! this->encodeChannels(flags, ctx, err))\n    return false;\n\n  if (! this->encodeContacts(flags, ctx, err))\n    return false;\n\n  if (! this->encodeAnalogContacts(flags, ctx, err))\n    return false;\n\n  if (! this->encodeRXGroupLists(flags, ctx, err))\n    return false;\n\n  if (! this->encodeZones(flags, ctx, err))\n    return false;\n\n  if (! this->encodeScanLists(flags, ctx, err))\n    return false;\n\n  if (! this->encodeGPSSystems(flags, ctx, err))\n    return false;\n\n  if (! this->encodeDMREncryptionKeys(flags, ctx, err))\n    return false;\n\n  return true;\n}\n\nbool\nD868UVCodeplug::createElements(Context &ctx, const ErrorStack &err)\n{\n  if (! this->setRadioID(ctx, err))\n    return false;\n\n  if (! this->decodeGeneralSettings(ctx, err))\n    return false;\n\n  if (! this->createSMSMessages(ctx, err))\n    return false;\n\n  if (! this->decodeRepeaterOffsetFrequencies(ctx, err))\n    return false;\n\n  if (! this->decodeBootSettings(ctx, err))\n    return false;\n\n  if (! this->decodeDMREncryptionKeys(ctx, err))\n    return false;\n\n  if (! this->createChannels(ctx, err))\n    return false;\n\n  if (! this->createContacts(ctx, err))\n    return false;\n\n  if (! this->createAnalogContacts(ctx, err))\n    return false;\n\n  if (! this->createRXGroupLists(ctx, err))\n    return false;\n\n  if (! this->createZones(ctx, err))\n    return false;\n\n  if (! this->createScanLists(ctx, err))\n    return false;\n\n  if (! this->createGPSSystems(ctx, err))\n    return false;\n\n  return true;\n}\n\n\nbool\nD868UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) {\n  if (! this->linkRXGroupLists(ctx, err))\n    return false;\n\n  if (! this->linkZones(ctx, err))\n    return false;\n\n  if (! this->linkScanLists(ctx, err))\n    return false;\n\n  if (! this->linkChannels(ctx, err))\n    return false;\n\n  if (! this->linkGPSSystems(ctx, err))\n    return false;\n\n  if (! this->linkGeneralSettings(ctx, err)) {\n    return false;\n  }\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateChannels() {\n  /* Allocate channels */\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // if disabled -> skip\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    // compute address for channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() +\n        + bank * Offset::betweenChannelBanks()\n        + idx * ChannelElement::size();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, ChannelElement::size());\n    }\n  }\n}\n\nbool\nD868UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  // Encode channels\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    // enable channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    ChannelElement ch(data(Offset::channelBanks() + bank * Offset::betweenChannelBanks()\n                           + idx * ChannelElement::size()));\n    if (! ch.fromChannelObj(ctx.get<Channel>(i), ctx))\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n\n  // Create channels\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (Channel *obj = ch.toChannelObj(ctx)) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i);\n    }\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  // Link channel objects\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (ctx.has<Channel>(i))\n      ch.linkChannelObj(ctx.get<Channel>(i), ctx);\n  }\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateVFOSettings() {\n  // Allocate VFO channels\n  image(0).addElement(Offset::vfoA(), ChannelElement::size());\n  image(0).addElement(Offset::vfoA()+0x2000, ChannelElement::size());\n  image(0).addElement(Offset::vfoB(), ChannelElement::size());\n  image(0).addElement(Offset::vfoB()+0x2000, ChannelElement::size());\n}\n\nvoid\nD868UVCodeplug::allocateContacts() {\n  /* Allocate contacts */\n  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));\n  unsigned contactCount=0;\n  for (uint16_t i=0; i<Limit::numContacts(); i++) {\n    // enabled if false (ass hole)\n    if (! contact_bitmap.isEncoded(i))\n      continue;\n    contactCount++;\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + ((i%Limit::contactsPerBank())/Limit::contactsPerBlock())*Offset::betweenContactBlocks();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, Offset::betweenContactBlocks());\n      memset(data(addr), 0x00, Offset::betweenContactBlocks());\n    }\n  }\n\n  if (contactCount) {\n    image(0).addElement(Offset::contactIndex(), align_size(4*contactCount, 16));\n    memset(data(Offset::contactIndex()), 0xff, align_size(4*contactCount, 16));\n    image(0).addElement(Offset::contactIdTable(), align_size(ContactMapElement::size()*(1+contactCount), 16));\n    memset(data(Offset::contactIdTable()), 0xff, align_size(ContactMapElement::size()*(1+contactCount), 16));\n  }\n}\n\nbool\nD868UVCodeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  QVector<DMRContact*> contacts;\n  // Encode contacts and also collect id<->index map\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size();\n    ContactElement con(data(addr));\n    DMRContact *contact = ctx.get<DMRContact>(i);\n    if(! con.fromContactObj(contact, ctx))\n      return false;\n    ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i);\n    contacts.append(contact);\n  }\n  // encode index map for contacts\n  std::sort(contacts.begin(), contacts.end(),\n            [](DMRContact *a, DMRContact *b) {\n    return a->number() < b->number();\n  });\n  for (int i=0; i<contacts.size(); i++) {\n    ContactMapElement el(data(Offset::contactIdTable() + i*ContactMapElement::size()));\n    el.setID(contacts[i]->number(), (DMRContact::GroupCall==contacts[i]->type()));\n    el.setIndex(ctx.index(contacts[i]));\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create digital contacts\n  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));\n  for (uint16_t i=0; i<Limit::numContacts(); i++) {\n    // Check if contact is enabled:\n    if (! contact_bitmap.isEncoded(i))\n      continue;\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size();\n    ContactElement con(data(addr));\n    if (DMRContact *obj = con.toContactObj(ctx)) {\n      ctx.config()->contacts()->add(obj); ctx.add(obj, i);\n    }\n  }\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateAnalogContacts() {\n  /* Allocate analog contacts */\n  DTMFContactBytemapElement analog_contact_bytemap(data(Offset::dtmfContactBytemap()));\n  for (uint8_t i=0; i<Limit::numDTMFContacts(); i++) {\n    // if disabled -> skip\n    if (! analog_contact_bytemap.isEncoded(i))\n      continue;\n    uint32_t bank_addr = Offset::dtmfContacts() + (i/2)*(2*DTMFContactElement::size());\n    if (! isAllocated(bank_addr, 0)) {\n      image(0).addElement(bank_addr, 2*DTMFContactElement::size());\n    }\n  }\n  image(0).addElement(Offset::dtmfIndex(), 1*Limit::numDTMFContacts());\n}\n\nbool\nD868UVCodeplug::encodeAnalogContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  uint8_t *idxlst = data(Offset::dtmfIndex());\n  memset(idxlst, 0xff, 1*Limit::numDTMFContacts());\n  for (unsigned int i=0; i<ctx.count<DTMFContact>(); i++) {\n    DTMFContactElement cont(data(Offset::dtmfContacts() + i*DTMFContactElement::size()));\n    cont.fromContact(ctx.get<DTMFContact>(i));\n    idxlst[i] = i;\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::createAnalogContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  DTMFContactBytemapElement analog_contact_bytemap(data(Offset::dtmfContactBytemap()));\n  for (unsigned int i=0; i<Limit::numDTMFContacts(); i++) {\n    // if disabled -> skip\n    if (! analog_contact_bytemap.isEncoded(i))\n      continue;\n    DTMFContactElement cont(data(Offset::dtmfContacts() + i*DTMFContactElement::size()));\n    if (DTMFContact *dtmf = cont.toContact()) {\n      ctx.config()->contacts()->add(dtmf);\n      ctx.add(dtmf, i);\n    }\n  }\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateRadioIDs() {\n  /* Allocate radio IDs */\n  RadioIDBitmapElement radioid_bitmap(data(Offset::radioIDBitmap()));\n  for (uint8_t i=0; i<Limit::numRadioIDs(); i++) {\n    // if disabled -> skip\n    if (! radioid_bitmap.isEncoded(i))\n      continue;\n    // Allocate radio IDs individually\n    uint32_t addr = Offset::radioIDs() + i*RadioIDElement::size();\n    if (! isAllocated(addr, 0)) {\n      image(0).addElement(addr, RadioIDElement::size());\n    }\n  }\n}\n\nbool\nD868UVCodeplug::encodeRadioID(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  // Encode radio IDs\n  for (unsigned int i=0; i<ctx.count<DMRRadioID>(); i++) {\n    RadioIDElement(data(Offset::radioIDs() + i*RadioIDElement::size()))\n      .fromRadioID(ctx.get<DMRRadioID>(i));\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::setRadioID(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Find a valid RadioID\n  RadioIDBitmapElement radio_id_bitmap(data(Offset::radioIDBitmap()));\n  for (uint16_t i=0; i<Limit::numRadioIDs(); i++) {\n    if (! radio_id_bitmap.isEncoded(i))\n      continue;\n    RadioIDElement id(data(Offset::radioIDs() + i*RadioIDElement::size()));\n    if (DMRRadioID *rid = id.toRadioID()) {\n      ctx.config()->radioIDs()->add(rid);  ctx.add(rid, i);\n    }\n  }\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateRXGroupLists() {\n  /*\n   * Allocate group lists\n   */\n  GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap()));\n  for (uint16_t i=0; i<Limit::numGroupLists(); i++) {\n    // if disabled -> skip\n    if (! grouplist_bitmap.isEncoded(i))\n      continue;\n    // Allocate RX group lists indivitually\n    uint32_t addr = Offset::groupLists() + i*Offset::betweenGroupLists();\n    if (! isAllocated(addr, 0)) {\n      image(0).addElement(addr, GroupListElement::size());\n      GroupListElement(data(addr)).clear();\n    }\n  }\n\n}\n\nbool\nD868UVCodeplug::encodeRXGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  // Encode RX group-lists\n  for (int i=0; i<ctx.config()->rxGroupLists()->count(); i++) {\n    GroupListElement grp(data(Offset::groupLists() + i*Offset::betweenGroupLists()));\n    grp.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx);\n  }\n\n  return true;\n}\n\nbool\nD868UVCodeplug::createRXGroupLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create RX group lists\n  GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap()));\n  for (uint16_t i=0; i<Limit::numGroupLists(); i++) {\n    // check if group list is enabled\n    if (! grouplist_bitmap.isEncoded(i))\n      continue;\n    // construct RXGroupList from definition\n    GroupListElement grp(data(Offset::groupLists() + i*Offset::betweenGroupLists()));\n    if (RXGroupList *obj = grp.toGroupListObj()) {\n      ctx.config()->rxGroupLists()->add(obj); ctx.add(obj, i);\n    }\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::linkRXGroupLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GroupListBitmapElement grouplist_bitmap(data(Offset::groupListBitmap()));\n  for (uint16_t i=0; i<Limit::numGroupLists(); i++) {\n    // check if group list is enabled\n    if (! grouplist_bitmap.isEncoded(i))\n      continue;\n\n    // link group list\n    GroupListElement grp(data(Offset::groupLists() + i*Offset::betweenGroupLists()));\n    if (! grp.linkGroupList(ctx.get<RXGroupList>(i), ctx)) {\n      logError() << \"Cannot link RX group list idx=\" << i;\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateZones() {\n  ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap()));\n  for (uint16_t i=0; i<Limit::numZones(); i++) {\n    // if invalid -> skip\n    if (! zone_bitmap.isEncoded(i))\n      continue;\n    // Allocate zone itself\n    image(0).addElement(Offset::zoneChannels()+i*Offset::betweenZoneChannels(), Size::zoneChannels());\n    image(0).addElement(Offset::zoneNames()+i*Offset::betweenZoneNames(), Size::zoneName());\n  }\n}\n\nbool\nD868UVCodeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  // Encode zones\n  for (unsigned int i=0; i<ctx.count<Zone>(); i++) {\n    Zone *zone = ctx.get<Zone>(i);\n    // Clear name and channel list\n    uint8_t  *name     = (uint8_t *)data(Offset::zoneNames() + i*Offset::betweenZoneNames());\n    uint16_t *channels = (uint16_t *)data(Offset::zoneChannels() + i*Offset::betweenZoneChannels());\n    encode_ascii(name, zone->name(), Limit::zoneNameLength(), 0);\n    memset(channels, 0xff, Size::zoneChannels());\n\n    // Handle list A\n    for (int j=0; j<zone->A()->count(); j++) {\n      channels[j] = qToLittleEndian(ctx.index(zone->A()->get(j)->as<Channel>()));\n    }\n\n    if (! encodeZone(i, zone, flags, ctx, err))\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  return true;\n}\n\nbool\nD868UVCodeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create zones\n  ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap()));\n  for (uint16_t i=0; i<Limit::numZones(); i++) {\n    // Check if zone is enabled:\n    if (! zone_bitmap.isEncoded(i))\n      continue;\n    // Determine whether this zone should be combined with the previous one\n    QString zonename = decode_ascii(\n          data(Offset::zoneNames()+i*Offset::betweenZoneNames()),\n          Limit::zoneNameLength(), 0);\n    Zone *zone = new Zone(zonename);\n    if (! decodeZone(i, zone, ctx, err)) {\n      zone->deleteLater();\n      return false;\n    }\n    // add to config\n    ctx.config()->zones()->add(zone); ctx.add(zone, i);\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(ctx); Q_UNUSED(err)\n  return true;\n}\n\nbool\nD868UVCodeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create zones\n  ZoneBitmapElement zone_bitmap(data(Offset::zoneBitmap()));\n  for (uint16_t i=0; i<Limit::numZones(); i++) {\n    // Check if zone is enabled:\n    if (! zone_bitmap.isEncoded(i))\n      continue;\n    Zone *zone = ctx.get<Zone>(i);\n\n    // link zone\n    uint16_t *channels = (uint16_t *)data(Offset::zoneChannels() + i*Offset::betweenZoneChannels());\n    for (uint8_t j=0; j<Limit::numChannelsPerZone(); j++, channels++) {\n      // If not enabled -> continue\n      if (0xffff == *channels)\n        continue;\n      // Get channel index and check if defined\n      uint16_t cidx = qFromLittleEndian(*channels);\n      if (! ctx.has<Channel>(cidx))\n        continue;\n      zone->A()->add(ctx.get<Channel>(cidx));\n    }\n\n    if (! linkZone(i, zone, false, ctx, err))\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::linkZone(int i, Zone *zone, bool isB, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(i); Q_UNUSED(zone); Q_UNUSED(isB); Q_UNUSED(ctx); Q_UNUSED(err)\n  return true;\n}\n\nvoid\nD868UVCodeplug::allocateScanLists() {\n  ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap()));\n  for (uint8_t i=0; i<Limit::numScanLists(); i++) {\n    // if disabled -> skip\n    if (! scanlist_bitmap.isEncoded(i))\n      continue;\n    // Allocate scan lists indivitually\n    uint8_t bank = (i/Limit::numScanListsPerBank()), bank_idx = (i%Limit::numScanListsPerBank());\n    uint32_t addr = Offset::scanListBanks() + bank*Offset::betweenScanListBanks()\n        + bank_idx*Offset::betweenScanLists();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, ScanListElement::size());\n      ScanListElement(data(addr)).clear();\n    }\n  }\n}\n\nbool\nD868UVCodeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  // Encode scan lists\n  unsigned int num_scan_lists = std::min(Limit::numScanLists(), ctx.count<ScanList>());\n  for (unsigned int i=0; i<num_scan_lists; i++) {\n    uint8_t bank = i/Limit::numScanListsPerBank(), idx = i%Limit::numScanListsPerBank();\n    ScanListElement scan(data(Offset::scanListBanks() + bank*Offset::betweenScanListBanks()\n                              + idx*Offset::betweenScanLists()));\n    scan.fromScanListObj(ctx.config()->scanlists()->scanlist(i), ctx);\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create scan lists\n  ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap()));\n  for (unsigned int i=0; i<Limit::numScanLists(); i++) {\n    if (! scanlist_bitmap.isEncoded(i))\n      continue;\n    uint8_t bank = i/Limit::numScanListsPerBank(), bank_idx = i%Limit::numScanListsPerBank();\n    uint32_t addr = Offset::scanListBanks() + bank*Offset::betweenScanListBanks()\n        + bank_idx*Offset::betweenScanLists();\n    ScanListElement scanl(data(addr));\n    // Create scanlist\n    ScanList *obj = scanl.toScanListObj();\n    ctx.config()->scanlists()->add(obj); ctx.add(obj, i);\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ScanListBitmapElement scanlist_bitmap(data(Offset::scanListBitmap()));\n  for (unsigned i=0; i<Limit::numScanLists(); i++) {\n    if (! scanlist_bitmap.isEncoded(i))\n      continue;\n    uint8_t bank = i/Limit::numScanListsPerBank(), bank_idx = i%Limit::numScanListsPerBank();\n    uint32_t addr = Offset::scanListBanks() + bank*Offset::betweenScanListBanks()\n        + bank_idx*Offset::betweenScanLists();\n    ScanListElement scanl(data(addr));\n    // Create scanlist\n    ScanList *obj = ctx.get<ScanList>(i);\n    // Link scanlists immediately, all channels are defined already\n    ctx.config()->scanlists()->add(obj); scanl.linkScanListObj(obj, ctx);\n  }\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateGeneralSettings() {\n  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());\n}\n\nbool\nD868UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err);\n}\n\nbool\nD868UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n}\n\nbool\nD868UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err);\n}\n\nvoid\nD868UVCodeplug::allocateZoneChannelList() {\n  image(0).addElement(Offset::zoneChannelList(), ZoneChannelListElement::size());\n}\n\n\nvoid\nD868UVCodeplug::allocateDTMFNumbers() {\n  image(0).addElement(Offset::dtmfIDList(), DTMFIDListElement::size());\n}\n\n\nvoid\nD868UVCodeplug::allocateBootSettings() {\n  image(0).addElement(Offset::bootSettings(), BootSettingsElement::size());\n}\n\nbool\nD868UVCodeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  return BootSettingsElement(data(Offset::bootSettings())).fromConfig(flags, ctx);\n}\n\nbool\nD868UVCodeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  return BootSettingsElement(data(Offset::bootSettings())).updateConfig(ctx);\n}\n\n\nvoid\nD868UVCodeplug::allocateGPSSystems() {\n  image(0).addElement(Offset::aprsSettings(), DMRAPRSSettingsElement::size());\n  image(0).addElement(Offset::dmrAPRSMessage(), DMRAPRSMessageElement::size());\n}\n\nbool\nD868UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  DMRAPRSSettingsElement gps(data(Offset::aprsSettings()));\n  return gps.fromConfig(flags, ctx);\n}\n\nbool\nD868UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  QSet<uint8_t> systems;\n  // First find all GPS systems linked, that is referenced by any channel\n  // Create channels\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t  bank = i/128, idx = i%128;\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    if (ctx.get<Channel>(i)->is<FMChannel>())\n      continue;\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (ChannelElement::APRSType::Off != ch.txAPRSType())\n      systems.insert(ch.digitalAPRSSystemIndex());\n  }\n  // Then create all referenced GPS systems\n  DMRAPRSSettingsElement gps(data(Offset::aprsSettings()));\n  if (! gps.updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot update config from DMR-APRS settings.\";\n    return false;\n  }\n  for (QSet<uint8_t>::iterator idx=systems.begin(); idx!=systems.end(); idx++)\n    gps.createGPSSystem(*idx, ctx);\n  return true;\n}\n\nbool\nD868UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  DMRAPRSSettingsElement gps(data(Offset::aprsSettings()));\n  // Then link all referenced GPS systems\n  for (uint8_t i=0; i<Limit::dmrAPRSSystems(); i++) {\n    if (! ctx.has<DMRAPRSSystem>(i))\n      continue;\n    gps.linkGPSSystem(i, ctx);\n  }\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateSMSMessages() {\n  // Prefab. SMS messages\n  MessageBytemapElement messages_bytemap(data(Offset::messageBytemap()));\n  unsigned message_count = 0;\n  for (uint8_t i=0; i<Limit::numMessages(); i++) {\n    if (! messages_bytemap.isEncoded(i))\n      continue;\n    message_count++;\n    uint32_t addr = Offset::messageBanks() + (i/Limit::numMessagePerBank())*Offset::betweenMessageBanks();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, Size::messageBank());\n    }\n  }\n  if (message_count) {\n    image(0).addElement(Offset::messageIndex(), Size::messageIndex()*message_count);\n  }\n}\n\nbool\nD868UVCodeplug::encodeSMSMessages(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  unsigned int num_sms_messages = std::min(Limit::numMessages(), ctx.count<SMSTemplate>());\n  for (unsigned int i=0; i<num_sms_messages; i++) {\n    unsigned int bank = i/Limit::numMessagePerBank(), msg_idx = i % Limit::numMessagePerBank();\n    unsigned int addr = Offset::messageBanks() + bank*Offset::betweenMessageBanks() + msg_idx*MessageElement::size();\n    MessageElement message(data(addr));\n    message.setMessage(ctx.get<SMSTemplate>(i)->message());\n    MessageListElement listElement(data(Offset::messageIndex() + i*Size::messageIndex()));\n    listElement.setIndex(i);\n    if (i > 0) {\n      MessageListElement prevElement(data(Offset::messageIndex() + (i-1)*Size::messageIndex()));\n      prevElement.setIndex(i);\n    }\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::createSMSMessages(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  MessageBytemapElement messages_bytemap(data(Offset::messageBytemap()));\n  for (unsigned int i=0; i<Limit::numMessages(); i++) {\n    if (! messages_bytemap.isEncoded(i))\n      continue;\n    unsigned int bank = i/Limit::numMessagePerBank(), msg_idx = i % Limit::numMessagePerBank();\n    unsigned int addr = Offset::messageBanks() + bank*Offset::betweenMessageBanks() + msg_idx*MessageElement::size();\n    MessageElement message(data(addr));\n    SMSTemplate *temp = new SMSTemplate();\n    temp->setName(QString(\"SMS %1\").arg(i+1));\n    temp->setMessage(message.message());\n    ctx.config()->smsExtension()->smsTemplates()->add(temp);\n    ctx.add(temp, i);\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::linkSMSMessages(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  // nothing to do\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocateHotKeySettings() {\n  // Allocate Hot Keys\n  image(0).addElement(Offset::analogQuickCall(), AnalogQuickCallsElement::size());\n  image(0).addElement(Offset::statusMessages(), StatusMessagesElement::size());\n  image(0).addElement(Offset::hotKeySettings(), HotKeySettingsElement::size());\n}\n\nvoid\nD868UVCodeplug::allocateRepeaterOffsetFrequencies() {\n  // Offset frequencies\n  image(0).addElement(Offset::offsetFrequencies(), RepeaterOffsetListElement::size());\n}\n\nbool\nD868UVCodeplug::encodeRepeaterOffsetFrequencies(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  // If no AnyTone extension is present -> leave untouched.\n  if (! ctx.config()->settings()->anytoneExtension())\n    return true;\n\n  RepeaterOffsetListElement offsets(data(Offset::offsetFrequencies()));\n  offsets.clear();\n  for (unsigned int i=0; i<ctx.count<AnytoneAutoRepeaterOffset>(); i++) {\n    offsets.setOffset(i, ctx.get<AnytoneAutoRepeaterOffset>(i)->offset());\n  }\n  return true;\n}\n\nbool\nD868UVCodeplug::decodeRepeaterOffsetFrequencies(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Allocate extension, if not present.\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension()) {\n    ext = ctx.config()->settings()->anytoneExtension();\n  } else {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Decode offsets.\n  RepeaterOffsetListElement offsets(data(Offset::offsetFrequencies()));\n  for (unsigned int i=0; i<RepeaterOffsetListElement::Limit::numEntries(); i++) {\n    if (offsets.isSet(i)) {\n      AnytoneAutoRepeaterOffset *offset = new AnytoneAutoRepeaterOffset();\n      offset->setOffset(offsets.offset(i));\n      offset->setName(QString(\"%1 offset\").arg(offsets.offset(i).format()));\n      ext->autoRepeaterSettings()->offsets()->add(offset);\n      ctx.add(offset, i);\n    }\n  }\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocatDMREncryptionKeys() {\n  image(0).addElement(Offset::dmrEncryptionKeys(), DMREncryptionKeyListElement::size());\n}\n\nbool\nD868UVCodeplug::encodeDMREncryptionKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  DMREncryptionKeyListElement keys(data(Offset::dmrEncryptionKeys()));\n  keys.clear();\n\n  for (unsigned int i=0; i<DMREncryptionKeyListElement::Limit::numEntries(); i++) {\n    keys.clearKey(i);\n    if (! ctx.has<BasicEncryptionKey>(i))\n      continue;\n    keys.setKey(i, *ctx.get<BasicEncryptionKey>(i));\n  }\n\n  return true;\n}\n\n\nbool\nD868UVCodeplug::decodeDMREncryptionKeys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  DMREncryptionKeyListElement keys(data(Offset::dmrEncryptionKeys()));\n  for (unsigned int i=0; i<DMREncryptionKeyListElement::Limit::numEntries(); i++) {\n    if (! keys.hasKey(i))\n      continue;\n    auto key = new BasicEncryptionKey();\n    key->setName(QString(\"Basic Key %1\").arg(i));\n    key->setKey(keys.key(i));\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n\n\nvoid\nD868UVCodeplug::allocatEnhancedEncryptionKeys() {\n  image(0).addElement(Offset::enhancedEncryptionKeys(), EnhancedEncryptionKeyListElement::size());\n}\n\n\nvoid\nD868UVCodeplug::allocateAlarmSettings() {\n  // Alarm settings\n  image(0).addElement(Offset::alarmSettings(), AlarmSettingElement::size());\n  image(0).addElement(Offset::alarmSettingsExtension(), DigitalAlarmExtensionElement::size());\n}\n\nvoid\nD868UVCodeplug::allocateFMBroadcastSettings() {\n  // FM broad-cast settings\n  image(0).addElement(Offset::wfmChannels(), WFMChannelListElement::size());\n  image(0).addElement(Offset::wfmVFO(), WFMVFOElement::size());\n}\n\nvoid\nD868UVCodeplug::allocate5ToneIDs() {\n  // Allocate 5-tone functions\n  FiveToneIDBitmapElement bitmap(data(Offset::fiveToneIdBitmap()));\n  for (uint8_t i=0; i<FiveToneIDListElement::Limit::numEntries(); i++) {\n    if (! bitmap.isEncoded(i))\n      continue;\n    image(0).addElement(Offset::fiveToneIdList() + i*FiveToneIDElement::size(), FiveToneIDElement::size());\n  }\n}\n\nvoid\nD868UVCodeplug::allocate5ToneFunctions() {\n  image(0).addElement(Offset::fiveToneFunctions(), FiveToneFunctionListElement::size());\n}\n\nvoid\nD868UVCodeplug::allocate5ToneSettings() {\n  image(0).addElement(Offset::fiveToneSettings(), FiveToneSettingsElement::size());\n}\n\nvoid\nD868UVCodeplug::allocate2ToneIDs() {\n  // Allocate 2-tone encoding\n  TwoToneIDBitmapElement enc_bitmap(data(Offset::twoToneIdBitmap()));\n  for (uint8_t i=0; i<Limit::numTwoToneIDs(); i++) {\n    if (! enc_bitmap.isEncoded(i))\n      continue;\n    image(0).addElement(Offset::twoToneIdList() + i*TwoToneIDElement::size(), TwoToneIDElement::size());\n  }\n}\n\n\nvoid\nD868UVCodeplug::allocate2ToneFunctions() {\n  // Allocate 2-tone decoding\n  TwoToneFunctionBitmapElement dec_bitmap(data(Offset::twoToneFunctionBitmap()));\n  for (uint8_t i=0; i<Limit::numTwoToneFunctions(); i++) {\n    if (! dec_bitmap.isEncoded(i))\n      continue;\n    image(0).addElement(Offset::twoToneFunctionList() + i*TwoToneFunctionElement::size(),\n                        TwoToneFunctionElement::size());\n  }\n}\n\nvoid\nD868UVCodeplug::allocate2ToneSettings() {\n  image(0).addElement(Offset::twoToneSettings(), TwoToneSettingsElement::size());\n}\n\n\nvoid\nD868UVCodeplug::allocateDTMFSettings() {\n  image(0).addElement(Offset::dtmfSettings(), DTMFSettingsElement::size());\n}\n"
  },
  {
    "path": "lib/d868uv_codeplug.hh",
    "content": "#ifndef D868UV_CODEPLUG_HH\n#define D868UV_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"anytone_codeplug.hh\"\n\nclass Channel;\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\n\n\n/** Represents the device specific binary codeplug for Anytone AT-D868UV radios.\n *\n * In contrast to many other code-plugs, the code-plug for Anytone radios are spread over a large\n * memory area. The amount of fragmentation of the codeplug is overwhelming.\n * For example, while channels are organized more or less nicely in continuous banks, zones are\n * distributed throughout the entire code-plug. That is, the names of zones are located in a\n * different memory section that the channel lists. Some lists are defined though bit-masks others\n * by byte-masks. All bit-masks are positive, that is 1 indicates an enabled item while the\n * bit-mask for contacts is inverted.\n *\n * In general the code-plug is huge and complex. Moreover, the radio provides a huge amount of\n * options and features. To this end, reverse-engineering this code-plug was a nightmare.\n *\n * More over, the binary code-plug file generate by the windows CPS does not directly relate to\n * the data being written to the radio. To this end the code-plug has been reverse-engineered\n * using wireshark to monitor the USB communication between the windows CPS (running in a virtual\n * box) and the device. The latter makes the reverse-engineering particularly cumbersome.\n *\n *\n * @ingroup d868uv */\nclass D868UVCodeplug : public AnytoneCodeplug\n{\n  Q_OBJECT\n\nprotected:\n  /** Colors supported by the D868UVE. */\n  struct Color {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      White = 0, Red=1\n    } CodedColor;\n  };\n\n\npublic:\n  /** Represents the channel element for AnyTone D868UV devices.\n   *  This class derives from @c AnytoneCodeplug::ChannelElement and implements the device-specific\n   *  encoding of channels for the AnyTone D868UV.\n   *\n   *  Memory layout of the encoded channel element (size 0x0040 bytes):\n   *  @verbinclude d868uv_channel.txt */\n  class ChannelElement: public AnytoneCodeplug::ChannelElement\n  {\n  public:\n    /** Possible encryption types. */\n    enum class DMREncryptionType {\n      Basic = 0,\n      Enhanced = 1\n    };\n\n    /** Possible APRS modes. */\n    enum class APRSType {\n      Off = 0, DMR = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns @c true if ranging is enabled. */\n    virtual bool ranging() const;\n    /** Enables/disables ranging. */\n    virtual void enableRanging(bool enable);\n    /** Returns @c true if through mode is enabled. */\n    virtual bool throughMode() const;\n    /** Enables/disables though mode. */\n    virtual void enableThroughMode(bool enable);\n    /** Returns @c true if data ACK is enabled. */\n    virtual bool dataACK() const;\n    /** Enables/disables data ACK. */\n    virtual void enableDataACK(bool enable);\n\n    /** Returns APRS type for reporting the position. */\n    APRSType txAPRSType() const;\n    /** Sets APRS type for reporting the position. */\n    void setTXAPRSType(APRSType aprsType);\n    /** Returns the DMR APRS system index. */\n    virtual unsigned digitalAPRSSystemIndex() const;\n    /** Sets the DMR APRS system index. */\n    virtual void setDigitalAPRSSystemIndex(unsigned idx);\n\n    /** Returns the encryption type. */\n    virtual DMREncryptionType dmrEncryptionType() const;\n    /** Sets the encryption type. */\n    virtual void setDMREncryptionType(DMREncryptionType type);\n    /** Returns @c true if a DMR encryption key is set. */\n    virtual bool hasDMREncryptionKeyIndex() const;\n    /** Returns the DMR encryption key index (+1), 0=Off. */\n    virtual unsigned dmrEncryptionKeyIndex() const;\n    /** Sets the DMR encryption key index (+1), 0=Off. */\n    virtual void setDMREncryptionKeyIndex(unsigned idx);\n    /** Clears the DMR encryption key index. */\n    virtual void clearDMREncryptionKeyIndex();\n\n    /** Returns @c true if multiple key encryption is enabled. */\n    virtual bool multipleKeyEncryption() const;\n    /** Enables/disables multiple key encryption. */\n    virtual void enableMultipleKeyEncryption(bool enable);\n    /** Returns @c true if random key is enabled. */\n    virtual bool randomKey() const;\n    /** Enables/disables random key. */\n    virtual void enableRandomKey(bool enable);\n    /** Returns @c true if SMS is enabled. */\n    virtual bool sms() const;\n    /** Enables/disables SMS. */\n    virtual void enableSMS(bool enable);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(Context &ctx) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx);\n\n  protected:\n    /** Internal used offsets within the channel element. */\n    struct Offset: public AnytoneCodeplug::ChannelElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static Bit dmrEncryptionType()               { return {0x0021, 6}; }\n      static unsigned int dmrEncryptionKey()       { return 0x0022; }\n      static Bit ranging()                         { return {0x0034, 0}; }\n      static Bit throughMode()                     { return {0x0034, 1}; }\n      static Bit dataACK()                         { return {0x0034, 2}; }\n      static unsigned int txAPRSType()             { return 0x0035; }\n      static unsigned int digitalAPRSSystemIndex() { return 0x0036; }\n      static Bit multipleKeyEncryption()           { return {0x003b, 0}; }\n      static Bit randomKey()                       { return {0x003b, 1}; }\n      static Bit sms()                             { return {0x003b, 2}; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the general config of the radio within the D868UV binary codeplug.\n   *\n   * This class only implements the differences to the generic\n   * @c AnytoneCodeplug::GeneralSettingsElement.\n   *\n   * Memory layout of encoded general settings (size 0x00d0 bytes):\n   * @verbinclude d868uv_generalsettings.txt\n   */\n  class GeneralSettingsElement: public AnytoneCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific key functions. */\n    struct KeyFunction {\n    public:\n      /** Encodes key function. */\n      static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone);\n      /** Decodes key function. */\n      static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code);\n\n    protected:\n      /** Encoded key functions. */\n      typedef enum {\n        Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04,\n        Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09,\n        Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f,\n        Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14,\n        HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19,\n        WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d,\n        PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21,\n        ChannelType = 0x22, Ranging = 0x23, ChannelRanging = 0x24, MaxVolume = 0x25, Slot = 0x26\n      } KeyFunctionCode;\n    };\n\n    /** Possible backlight duration values. */\n    enum class BacklightDuration {\n      Infinite = 0, _5s = 1, _10s = 2, _15s = 3, _20s = 4, _25s = 5, _30s = 6, _1min=7, _2min=8,\n      _3min = 9, _4min = 10, _5min = 11\n    };\n\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00d0; }\n\n    /** Resets the general settings. */\n    void clear();\n\n    /** Returns the power-save mode. */\n    virtual AnytonePowerSaveSettingsExtension::PowerSave powerSave() const;\n    /** Sets the power-save mode. */\n    virtual void setPowerSave(AnytonePowerSaveSettingsExtension::PowerSave mode);\n\n    /** Returns the VOX level. */\n    virtual Level voxLevel() const;\n    /** Sets the VOX level. */\n    virtual void setVOXLevel(Level level);\n    /** Returns the VOX delay in ms. */\n    virtual Interval voxDelay() const;\n    /** Sets the VOX delay in ms. */\n    virtual void setVOXDelay(Interval ms);\n    /** Returns the VOX source. */\n    virtual AnytoneAudioSettingsExtension::VoxSource voxSource() const;\n    /** Sets the VOX source. */\n    virtual void setVOXSource(AnytoneAudioSettingsExtension::VoxSource source);\n    Level dmrMicGain() const;\n    void setDMRMicGain(Level gain);\n    Level maxSpeakerVolume() const;\n    void setMaxSpeakerVolume(Level level);\n    /** Returns the maximum headphone volume. */\n    virtual Level maxHeadphoneVolume() const;\n    /** Sets the maximum headphone volume. */\n    virtual void setMaxHeadphoneVolume(Level max);\n    bool enhanceAudio() const;\n    void enableEnhancedAudio(bool enable);\n    bool recording() const;\n    void enableRecording(bool enable);\n    /** Returns the recording delay in ms. */\n    virtual unsigned recordingDelay() const;\n    /** Sets the recording delay in ms. */\n    virtual void setRecodringDelay(unsigned ms);\n\n\n    AnytoneSettingsExtension::VFOScanType vfoScanType() const;\n    void setVFOScanType(AnytoneSettingsExtension::VFOScanType type);\n    Frequency minVFOScanFrequencyUHF() const;\n    void setMinVFOScanFrequencyUHF(Frequency hz);\n    Frequency maxVFOScanFrequencyUHF() const;\n    void setMaxVFOScanFrequencyUHF(Frequency hz);\n    Frequency minVFOScanFrequencyVHF() const;\n    void setMinVFOScanFrequencyVHF(Frequency hz);\n    Frequency maxVFOScanFrequencyVHF() const;\n    void setMaxVFOScanFrequencyVHF(Frequency hz);\n\n    AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const;\n    void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const;\n    void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const;\n    void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const;\n    void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const;\n    void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const;\n    void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const;\n    void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const;\n    void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const;\n    void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const;\n    void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func);\n    Interval longPressDuration() const;\n    void setLongPressDuration(Interval ms);\n\n    bool vfoModeA() const;\n    void enableVFOModeA(bool enable);\n    bool vfoModeB() const;\n    void enableVFOModeB(bool enable);\n\n    unsigned memoryZoneA() const;\n    void setMemoryZoneA(unsigned zone);\n    unsigned memoryZoneB() const;\n    void setMemoryZoneB(unsigned zone);\n\n    bool gps() const;\n    void enableGPS(bool enable);\n    QTimeZone gpsTimeZone() const;\n    void setGPSTimeZone(const QTimeZone &zone);\n    bool getGPSPosition() const;\n    void enableGetGPSPosition(bool enable);\n    bool gpsUnitsImperial() const ;\n    void enableGPSUnitsImperial(bool enable);\n    /** Returns the GPS update period in seconds. */\n    virtual Interval gpsUpdatePeriod() const;\n    /** Sets the GPS update period in seconds. */\n    virtual void setGPSUpdatePeriod(Interval sec);\n\n    bool keyToneEnabled() const;\n    void enableKeyTone(bool enable);\n    bool smsAlert() const;\n    void enableSMSAlert(bool enable);\n    bool callAlert() const;\n    void enableCallAlert(bool enable);\n    bool dmrTalkPermit() const;\n    void enableDMRTalkPermit(bool enable);\n    bool fmTalkPermit() const;\n    void enableFMTalkPermit(bool enable);\n    bool dmrResetTone() const;\n    void enableDMRResetTone(bool enable);\n    bool idleChannelTone() const;\n    void enableIdleChannelTone(bool enable);\n    bool startupTone() const;\n    void enableStartupTone(bool enable);\n    void callToneMelody(Melody &melody) const;\n    void setCallToneMelody(const Melody &melody);\n    void idleToneMelody(Melody &melody) const;\n    void setIdleToneMelody(const Melody &melody);\n    void resetToneMelody(Melody &melody) const;\n    void setResetToneMelody(const Melody &melody);\n\n    bool activeChannelB() const;\n    void enableActiveChannelB(bool enable);\n\n    bool subChannel() const;\n    void enableSubChannel(bool enable);\n\n    Interval menuExitTime() const;\n    void setMenuExitTime(Interval intv);\n\n    bool callEndPrompt() const;\n    void enableCallEndPrompt(bool enable);\n    bool volumeChangePrompt() const;\n    void enableVolumeChangePrompt(bool enable);\n    AnytoneDisplaySettingsExtension::LastCallerDisplayMode lastCallerDisplayMode() const;\n    void setLastCallerDisplayMode(AnytoneDisplaySettingsExtension::LastCallerDisplayMode mode);\n    bool displayClock() const;\n    void enableDisplayClock(bool enable);\n    bool displayCall() const;\n    void enableDisplayCall(bool enable);\n    AnytoneDisplaySettingsExtension::Color callDisplayColor() const;\n    void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color);\n    bool showLastHeard() const;\n    void enableShowLastHeard(bool enable);\n    unsigned brightness() const;\n    void setBrightness(unsigned level);\n    /** Returns @c true if the backlight is always on. */\n    virtual bool backlightPermanent() const;\n    /** Returns the backlight duration in seconds. */\n    virtual Interval backlightDuration() const;\n    /** Sets the backlight duration in seconds. */\n    virtual void setBacklightDuration(Interval sec);\n    /** Sets the backlight to permanent (always on). */\n    virtual void enableBacklightPermanent();\n\n    AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionA() const;\n    void setAutoRepeaterDirectionA(AnytoneAutoRepeaterSettingsExtension::Direction dir);\n    AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const;\n    void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir);\n    bool hasAutoRepeaterOffsetFrequencyIndexUHF() const;\n    unsigned autoRepeaterOffsetFrequencyIndexUHF() const;\n    void setAutoRepeaterOffsetFrequenyIndexUHF(unsigned idx);\n    void clearAutoRepeaterOffsetFrequencyIndexUHF();\n    bool hasAutoRepeaterOffsetFrequencyIndexVHF() const;\n    unsigned autoRepeaterOffsetFrequencyIndexVHF() const;\n    void setAutoRepeaterOffsetFrequenyIndexVHF(unsigned idx);\n    void clearAutoRepeaterOffsetFrequencyIndexVHF();\n    Frequency autoRepeaterMinFrequencyVHF() const;\n    void setAutoRepeaterMinFrequencyVHF(Frequency Hz);\n    Frequency autoRepeaterMaxFrequencyVHF() const;\n    void setAutoRepeaterMaxFrequencyVHF(Frequency Hz);\n    Frequency autoRepeaterMinFrequencyUHF() const;\n    void setAutoRepeaterMinFrequencyUHF(Frequency Hz);\n    Frequency autoRepeaterMaxFrequencyUHF() const;\n    void setAutoRepeaterMaxFrequencyUHF(Frequency Hz);\n\n    bool showCurrentContact() const;\n    void enableShowCurrentContact(bool enable);\n\n    /** Returns @c true if the key-tone level is adjustable. */\n    virtual bool keyToneLevelAdjustable() const;\n    /** Returns the key-tone level (0=adjustable). */\n    virtual Level keyToneLevel() const;\n    /** Sets the key-tone level. */\n    virtual void setKeyToneLevel(Level level);\n    /** Sets the key-tone level adjustable. */\n    virtual void setKeyToneLevelAdjustable();\n\n    bool knobLock() const;\n    void enableKnobLock(bool enable);\n    bool keypadLock() const;\n    void enableKeypadLock(bool enable);\n    bool sidekeysLock() const;\n    void enableSidekeysLock(bool enable);\n    bool keyLockForced() const;\n    void enableKeyLockForced(bool enable);\n\n    bool defaultChannel() const;\n    void enableDefaultChannel(bool enable);\n    unsigned defaultZoneIndexA() const;\n    void setDefaultZoneIndexA(unsigned idx);\n    unsigned defaultZoneIndexB() const;\n    void setDefaultZoneIndexB(unsigned idx);\n    bool defaultChannelAIsVFO() const;\n    unsigned defaultChannelAIndex() const;\n    void setDefaultChannelAIndex(unsigned idx);\n    void setDefaultChannelAToVFO();\n    bool defaultChannelBIsVFO() const;\n    unsigned defaultChannelBIndex() const;\n    void setDefaultChannelBIndex(unsigned idx);\n    void setDefaultChannelBToVFO();\n\n    bool keepLastCaller() const;\n    void enableKeepLastCaller(bool enable);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err);\n    bool updateConfig(Context &ctx, const ErrorStack &err);\n    bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: Element::Limit {\n      /** Valid VOX sensitivity levels. */\n      static constexpr Range<unsigned int> vox() { return {1,3}; }\n      /** Valid mic gain settings. */\n      static constexpr Range<unsigned int> micGain() { return {0,4}; }\n      /** Valid settings for the speaker and headphone volume settings. */\n      static constexpr Range<unsigned int> volume() { return {0, 8}; }\n      /** Valid range for key tone volume. */\n      static constexpr Range<unsigned int> keyTone() { return {0,15}; }\n    };\n\n  protected:\n    /** Some internal used offsets within the element. */\n    struct Offset: public AnytoneCodeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int enableKeyTone()     { return 0x0000; }\n      static constexpr unsigned int powerSaveMode()     { return 0x000b; }\n      static constexpr unsigned int voxLevel()          { return 0x000c; }\n      static constexpr unsigned int voxDelay()          { return 0x000d; }\n      static constexpr unsigned int vfoScanType()       { return 0x000e; }\n      static constexpr unsigned int dmrMicGain()        { return 0x000f; }\n      static constexpr unsigned int progFuncKeyAShort() { return 0x0010; }\n      static constexpr unsigned int progFuncKeyBShort() { return 0x0011; }\n      static constexpr unsigned int progFuncKeyCShort() { return 0x0012; }\n      static constexpr unsigned int progFuncKey1Short() { return 0x0013; }\n      static constexpr unsigned int progFuncKey2Short() { return 0x0014; }\n      static constexpr unsigned int vfoModeA()          { return 0x0015; }\n      static constexpr unsigned int vfoModeB()          { return 0x0016; }\n      static constexpr unsigned int memZoneA()          { return 0x001f; }\n      static constexpr unsigned int memZoneB()          { return 0x0020; }\n      static constexpr unsigned int enableRecoding()    { return 0x0022; }\n      static constexpr unsigned int displayBrightness() { return 0x0026; }\n      static constexpr unsigned int backlightDuration() { return 0x0027; }\n      static constexpr unsigned int gpsEnable()         { return 0x0028; }\n      static constexpr unsigned int smsAlert()          { return 0x0029; }\n      static constexpr unsigned int activeChannelB()    { return 0x002c; }\n      static constexpr unsigned int subChannel()        { return 0x002d; }\n      static constexpr unsigned int callAlert()         { return 0x002f; }\n      static constexpr unsigned int gpsTimeZone()       { return 0x0030; }\n      static constexpr unsigned int talkPermit()        { return 0x0031; }\n      static constexpr unsigned int dmrResetTone()      { return 0x0032; }\n      static constexpr unsigned int voxSource()         { return 0x0033; }\n      static constexpr unsigned int idleChannelTone()   { return 0x0036; }\n      static constexpr unsigned int menuExitTime()      { return 0x0037; }\n      static constexpr unsigned int startupTone()       { return 0x0039; }\n      static constexpr unsigned int callEndPrompt()     { return 0x003a; }\n      static constexpr unsigned int maxSpeakerVolume()  { return 0x003b; }\n      static constexpr unsigned int getGPSPosition()    { return 0x003f; }\n      static constexpr unsigned int progFuncKeyALong()  { return 0x0041; }\n      static constexpr unsigned int progFuncKeyBLong()  { return 0x0042; }\n      static constexpr unsigned int progFuncKeyCLong()  { return 0x0043; }\n      static constexpr unsigned int progFuncKey1Long()  { return 0x0044; }\n      static constexpr unsigned int progFuncKey2Long()  { return 0x0045; }\n      static constexpr unsigned int longPressDuration() { return 0x0046; }\n      static constexpr unsigned int volumeChangePrompt(){ return 0x0047; }\n      static constexpr unsigned int autoRepeaterDirA()  { return 0x0048; }\n      static constexpr unsigned int lastCallerDisplay() { return 0x004d; }\n      static constexpr unsigned int showClock()         { return 0x0051; }\n      static constexpr unsigned int maxHeadPhoneVolume(){ return 0x0052; }\n      static constexpr unsigned int enhanceAudio()      { return 0x0057; }\n      static constexpr unsigned int minVFOScanUHF()     { return 0x0058; }\n      static constexpr unsigned int maxVFOScanUHF()     { return 0x005c; }\n      static constexpr unsigned int minVFOScanVHF()     { return 0x0060; }\n      static constexpr unsigned int maxVFOScanVHF()     { return 0x0064; }\n      static constexpr unsigned int autoRepOffsetUHF()  { return 0x0068; }\n      static constexpr unsigned int autoRepOffsetVHF()  { return 0x0069; }\n      static constexpr unsigned int callToneTones()     { return 0x0072; }\n      static constexpr unsigned int callToneDurations() { return 0x007c; }\n      static constexpr unsigned int idleToneTones()     { return 0x0086; }\n      static constexpr unsigned int idleToneDurations() { return 0x0090; }\n      static constexpr unsigned int resetToneTones()    { return 0x009a; }\n      static constexpr unsigned int resetToneDurations(){ return 0x00a4; }\n      static constexpr unsigned int recordingDelay()    { return 0x00ae; }\n      static constexpr unsigned int callDisplayMode()   { return 0x00af; }\n      static constexpr unsigned int callColor()         { return 0x00b0; }\n      static constexpr unsigned int gpsPeriod()         { return 0x00b1; }\n      static constexpr unsigned int showCurrentContact(){ return 0x00b2; }\n      static constexpr unsigned int keyToneLevel()      { return 0x00b3; }\n      static constexpr unsigned int gpsUnits()          { return 0x00b4; }\n      static constexpr unsigned int knobLock()          { return 0x00b5; }\n      static constexpr unsigned int keypadLock()        { return 0x00b5; }\n      static constexpr unsigned int sideKeyLock()       { return 0x00b5; }\n      static constexpr unsigned int forceKeyLock()      { return 0x00b5; }\n      static constexpr unsigned int showLastHeard()     { return 0x00b6; }\n      static constexpr unsigned int autoRepMinVHF()     { return 0x00b8; }\n      static constexpr unsigned int autoRepMaxVHF()     { return 0x00bc; }\n      static constexpr unsigned int autoRepMinUHF()     { return 0x00c0; }\n      static constexpr unsigned int autoRepMaxUHF()     { return 0x00c4; }\n      static constexpr unsigned int autoRepeaterDirB()  { return 0x00c8; }\n      static constexpr unsigned int defaultChannels()   { return 0x00ca; }\n      static constexpr unsigned int defaultZoneA()      { return 0x00cb; }\n      static constexpr unsigned int defaultZoneB()      { return 0x00cc; }\n      static constexpr unsigned int defaultChannelA()   { return 0x00cd; }\n      static constexpr unsigned int defaultChannelB()   { return 0x00ce; }\n      static constexpr unsigned int keepLastCaller()    { return 0x00cf; }\n      /// @endcond\n    };\n  };\n\nprotected:\n  /** Hidden constructor constructor. */\n  explicit D868UVCodeplug(const QString &label, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit D868UVCodeplug(QObject *parent = nullptr);\n\n  Config* preprocess(Config *config, const ErrorStack &err) const;\n\nprotected:\n  bool allocateBitmaps();\n  virtual void setBitmaps(Context &ctx);\n  virtual void allocateUpdated();\n  virtual void allocateForDecoding();\n  virtual void allocateForEncoding();\n\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  virtual bool createElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  virtual bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate channels from bitmap. */\n  virtual void allocateChannels();\n  /** Encode channels into codeplug. */\n  virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create channels from codeplug. */\n  virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link channels. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate VFO settings. */\n  virtual void allocateVFOSettings();\n\n  /** Allocate contacts from bitmaps. */\n  virtual void allocateContacts();\n  /** Encode contacts into codeplug. */\n  virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create contacts from codeplug. */\n  virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate analog contacts from bitmaps. */\n  virtual void allocateAnalogContacts();\n  /** Encode analog contacts into codeplug. */\n  virtual bool encodeAnalogContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create analog contacts from codeplug. */\n  virtual bool createAnalogContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate radio IDs from bitmaps. */\n  virtual void allocateRadioIDs();\n  /** Encode radio ID into codeplug. */\n  virtual bool encodeRadioID(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Set radio ID from codeplug. */\n  virtual bool setRadioID(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates RX group lists from bitmaps. */\n  virtual void allocateRXGroupLists();\n  /** Encode RX group lists into codeplug. */\n  virtual bool encodeRXGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create RX group lists from codeplug. */\n  virtual bool createRXGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link RX group lists. */\n  virtual bool linkRXGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate zones from bitmaps. */\n  virtual void allocateZones();\n  /** Encode zones into codeplug. */\n  virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Function to encode a single zone. */\n  virtual bool encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create zones from codeplug. */\n  virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Function to decode a single zone. */\n  virtual bool decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link zones. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Function to link a single zone. */\n  virtual bool linkZone(int i, Zone *zone, bool isB, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate scanlists from bitmaps. */\n  virtual void allocateScanLists();\n  /** Encode scan lists into codeplug. */\n  virtual bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create scan lists from codeplug. */\n  virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link scan lists. */\n  virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates general settings memory section. */\n  virtual void allocateGeneralSettings();\n  /** Encodes the general settings section. */\n  virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decodes the general settings section. */\n  virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link the general settings. */\n  virtual bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates zone channel list memory section. */\n  virtual void allocateZoneChannelList();\n\n  /** Allocates DTMF number list memory section. */\n  virtual void allocateDTMFNumbers();\n\n  /** Allocates boot settings memory section. */\n  virtual void allocateBootSettings();\n  /** Encodes the boot settings section. */\n  virtual bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decodes the boot settings section. */\n  virtual bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates GPS settings memory section. */\n  virtual void allocateGPSSystems();\n  /** Encodes the GPS settings section. */\n  virtual bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create GPS systems from codeplug. */\n  virtual bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link GPS systems. */\n  virtual bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocate prefab SMS messages. */\n  virtual void allocateSMSMessages();\n  /** Encodes prefab SMS messages. */\n  virtual bool encodeSMSMessages(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Create SMS messages from codeplug. */\n  virtual bool createSMSMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link SMS messages. */\n  virtual bool linkSMSMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n\n\n  /** Allocates hot key settings memory section. */\n  virtual void allocateHotKeySettings();\n\n  /** Allocates repeater offset settings memory section. */\n  virtual void allocateRepeaterOffsetFrequencies();\n  /** Encodes auto-repeater offset frequencies. */\n  virtual bool encodeRepeaterOffsetFrequencies(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decodes auto-repeater offset frequencies. */\n  virtual bool decodeRepeaterOffsetFrequencies(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates DMR encryption keys. */\n  virtual void allocatDMREncryptionKeys();\n  /** Encodes DMR encryption keys. */\n  virtual bool encodeDMREncryptionKeys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decodes DMR encryption keys */\n  virtual bool decodeDMREncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates 'enhanced' encryption keys. */\n  virtual void allocatEnhancedEncryptionKeys();\n\n  /** Allocates alarm settings memory section. */\n  virtual void allocateAlarmSettings();\n  /** Allocates FM broadcast settings memory section. */\n  virtual void allocateFMBroadcastSettings();\n\n  /** Allocates all 5-Tone IDs used. */\n  virtual void allocate5ToneIDs();\n  /** Allocates 5-Tone functions. */\n  virtual void allocate5ToneFunctions();\n  /** Allocates 5-Tone settings. */\n  virtual void allocate5ToneSettings();\n\n  /** Allocates all 2-Tone IDs used. */\n  virtual void allocate2ToneIDs();\n  /** Allocates 2-Tone functions. */\n  virtual void allocate2ToneFunctions();\n  /** Allocates 2-Tone settings. */\n  virtual void allocate2ToneSettings();\n\n  /** Allocates DTMF settings. */\n  virtual void allocateDTMFSettings();\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit {\n    static constexpr unsigned int channelsPerBank()     { return 128; }   ///< Max number of channels per bank.\n    static constexpr unsigned int numChannels()         { return 4000; }  ///< Max number of channels.\n    static constexpr unsigned int contactsPerBank()     { return 1000; }  ///< Max number of contacts per bank.\n    static constexpr unsigned int contactsPerBlock()    { return 4; }     ///< Max number of contacts per block.\n    static constexpr unsigned int numContacts()         { return 10000; } ///< Max number of contacts.\n    static constexpr unsigned int numDTMFContacts()     { return 128; }   ///< Max number of DTMF contacts.\n    static constexpr unsigned int numGroupLists()       { return 250; }   ///< Max number of group lists.\n    static constexpr unsigned int numScanLists()        { return 250; }   ///< Max number of scan lists.\n    static constexpr unsigned int numScanListsPerBank() { return 16; }    ///< Max number of scan lists per bank.\n    static constexpr unsigned int numRadioIDs()         { return 250; }   ///< Max number of radio IDs.\n    // There is no zone element -> hence all attributes must be defied within the codeplug.\n    static constexpr unsigned int numZones()            { return 250; }   ///< Max number of zones.\n    static constexpr unsigned int numChannelsPerZone()  { return 250; }   ///< Max number of channels per zone.\n    static constexpr unsigned int zoneNameLength()      { return 16; }    ///< Max zone name length.\n    static constexpr unsigned int dmrAPRSSystems()      { return 8; }     ///< Max number of DMR APRS systems.\n    static constexpr unsigned int numMessages()         { return 100; }   ///< Max number of preset SMS.\n    static constexpr unsigned int numMessagePerBank()   { return 8; }     ///< Max number of SMS per bank.\n    static constexpr unsigned int numTwoToneIDs()       { return 24; }    ///< Max number of two-tone IDs.\n    static constexpr unsigned int numTwoToneFunctions() { return 16; }    ///< Max number of two-tone functions.\n  };\n\nprotected:\n  /** Some internal used offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int channelBitmap()        { return 0x024c1500; }\n    static constexpr unsigned int channelBanks()         { return 0x00800000; }\n    static constexpr unsigned int betweenChannelBanks()  { return 0x00040000; }\n    static constexpr unsigned int vfoA()                 { return 0x00fc0800; }\n    static constexpr unsigned int vfoB()                 { return 0x00fc0840; }\n\n    static constexpr unsigned int contactBitmap()        { return 0x02640000; }\n    static constexpr unsigned int contactBanks()         { return 0x02680000; }\n    static constexpr unsigned int betweenContactBanks()  { return 0x00040000; }\n    static constexpr unsigned int betweenContactBlocks() { return 0x00000190; }\n    static constexpr unsigned int contactIndex()         { return 0x02600000; }\n    static constexpr unsigned int contactIdTable()       { return 0x04340000; }\n\n    static constexpr unsigned int dtmfContactBytemap()   { return 0x02900100; }\n    static constexpr unsigned int dtmfContacts()         { return 0x02940000; }\n    static constexpr unsigned int dtmfIndex()            { return 0x02900000; }\n    static constexpr unsigned int dtmfSettings()         { return 0x024C1080; }\n    static constexpr unsigned int dtmfIDList()           { return 0x02500500; }\n\n    static constexpr unsigned int groupListBitmap()      { return 0x025C0B10; }\n    static constexpr unsigned int groupLists()           { return 0x02980000; }\n    static constexpr unsigned int betweenGroupLists()    { return 0x00000200; }\n\n    static constexpr unsigned int scanListBitmap()       { return 0x024c1340; }\n    static constexpr unsigned int scanListBanks()        { return 0x01080000; }\n    static constexpr unsigned int betweenScanLists()     { return 0x00000200; }\n    static constexpr unsigned int betweenScanListBanks() { return 0x00040000; }\n\n    static constexpr unsigned int radioIDBitmap()        { return 0x024c1320; }\n    static constexpr unsigned int radioIDs()             { return 0x02580000; }\n\n    static constexpr unsigned int settings()             { return 0x02500000; }\n    static constexpr unsigned int zoneChannelList()      { return 0x02500100; }\n    static constexpr unsigned int bootSettings()         { return 0x02500600; }\n    static constexpr unsigned int aprsSettings()         { return 0x02501000; }\n    static constexpr unsigned int dmrAPRSMessage()       { return 0x02501100; }\n    static constexpr unsigned int offsetFrequencies()    { return 0x024C2000; }\n\n    static constexpr unsigned int zoneBitmap()           { return 0x024c1300; }\n    static constexpr unsigned int zoneChannels()         { return 0x01000000; }\n    static constexpr unsigned int betweenZoneChannels()  { return 0x00000200; }\n    static constexpr unsigned int zoneNames()            { return 0x02540000; }\n    static constexpr unsigned int betweenZoneNames()     { return 0x00000020; }\n\n    static constexpr unsigned int messageBytemap()       { return 0x01640800; }\n    static constexpr unsigned int messageBanks()         { return 0x02140000; }\n    static constexpr unsigned int betweenMessageBanks()  { return 0x00040000; }\n    static constexpr unsigned int messageIndex()         { return 0x01640000; }\n\n    static constexpr unsigned int analogQuickCall()      { return 0x025C0000; }\n    static constexpr unsigned int statusMessageBitmap()  { return 0x025C0B00; }\n    static constexpr unsigned int statusMessages()       { return 0x025C0100; }\n    static constexpr unsigned int hotKeySettings()       { return 0x025C0500; }\n\n    static constexpr unsigned int alarmSettings()        { return 0x024C1400; }\n    static constexpr unsigned int alarmSettingsExtension() { return 0x024c1440; }\n\n    static constexpr unsigned int fiveToneIdBitmap()     { return 0x024C0C80; }\n    static constexpr unsigned int fiveToneIdList()       { return 0x024C0000; }\n    static constexpr unsigned int fiveToneFunctions()    { return 0x024C0D00; }\n    static constexpr unsigned int fiveToneSettings()     { return 0x024C1000; }\n\n    static constexpr unsigned int twoToneIdBitmap()      { return 0x024C1280; }\n    static constexpr unsigned int twoToneIdList()        { return 0x024C1100; }\n    static constexpr unsigned int twoToneFunctionBitmap(){ return 0x024c2600; }\n    static constexpr unsigned int twoToneFunctionList()  { return 0x024c2400; }\n    static constexpr unsigned int twoToneSettings()      { return 0x024C1290; }\n\n    static constexpr unsigned int wfmChannelBitmap()     { return 0x02480210; }\n    static constexpr unsigned int wfmChannels()          { return 0x02480000; }\n    static constexpr unsigned int wfmVFO()               { return 0x02480200; }\n\n    static constexpr unsigned int dmrEncryptionKeys()      { return 0x024C1700; }\n    static constexpr unsigned int enhancedEncryptionKeys() { return 0x024C1800; }\n    /// @endcond\n  };\n\n  /** Internal used sizes. Usually sizes are specified by static methods of the element, however,\n   * some stuff is not represented by Elements in the AnyTone codeplug, hence this table is needed. */\n  struct Size {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int zoneName()             { return 0x0020; }\n    static constexpr unsigned int zoneChannels()         { return 0x0200; }\n    static constexpr unsigned int messageBank()          { return 0x0800; }\n    static constexpr unsigned int messageIndex()         { return 0x0010; }\n    /// @endcond\n  };\n};\n\n#endif // D868UVCODEPLUG_HH\n"
  },
  {
    "path": "lib/d868uv_filereader.cc",
    "content": "#include \"d868uv_filereader.hh\"\n#include <QtEndian>\n\n#include \"logger.hh\"\n#include \"utils.hh\"\n\n\n#define HEADER_SIZE              0x000000e2\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader::ChannelElement\n * ********************************************************************************************* */\nD868UVFileReader::ChannelElement::ChannelElement(const uint8_t *ptr)\n  : Element(ptr), _nameLength(0)\n{\n  _nameLength = strnlen((char *)_data+0x30,16);\n}\n\nuint16_t\nD868UVFileReader::ChannelElement::index() const {\n  return qFromLittleEndian(*(uint16_t *)_data + 0x00);\n}\n\nD868UVCodeplug::channel_t::Mode\nD868UVFileReader::ChannelElement::mode() const {\n  return (D868UVCodeplug::channel_t::Mode) *(_data + 0x0b);\n}\n\ndouble\nD868UVFileReader::ChannelElement::rxFrequency() const {\n  return double(qFromLittleEndian(*(uint32_t *)(_data + 0x02)))/1e5;\n}\n\ndouble\nD868UVFileReader::ChannelElement::txFrequency() const {\n  double rx  = double(qFromLittleEndian(*(uint32_t *)(_data + 0x02)))/1e5;\n  double off = double(qFromLittleEndian(*(uint32_t *)(_data + 0x07)))/1e5;\n  D868UVCodeplug::channel_t::RepeaterMode repeater_type = (D868UVCodeplug::channel_t::RepeaterMode)*(_data+0x06);\n\n  switch(repeater_type) {\n  case D868UVCodeplug::channel_t::RM_TXPOS: return rx+off;\n  case D868UVCodeplug::channel_t::RM_TXNEG: return rx-off;\n  case D868UVCodeplug::channel_t::RM_SIMPLEX:\n  default:\n    break;\n  }\n\n  return rx;\n}\n\nChannel::Power\nD868UVFileReader::ChannelElement::power() const {\n  D868UVCodeplug::channel_t::Power pwr = (D868UVCodeplug::channel_t::Power) * (_data + 0x0c);\n  switch(pwr) {\n  case D868UVCodeplug::channel_t::POWER_LOW: return Channel::LowPower;\n  case D868UVCodeplug::channel_t::POWER_MIDDLE: return Channel::MidPower;\n  case D868UVCodeplug::channel_t::POWER_HIGH: return Channel::HighPower;\n  case D868UVCodeplug::channel_t::POWER_TURBO: return Channel::MaxPower;\n  default:\n    break;\n  }\n  return Channel::MinPower;\n}\n\nAnalogChannel::Bandwidth\nD868UVFileReader::ChannelElement::bandwidth() const {\n  D868UVCodeplug::channel_t::Bandwidth bw = (D868UVCodeplug::channel_t::Bandwidth) * (_data + 0x0d);\n  return (D868UVCodeplug::channel_t::BW_25_KHZ == bw) ? AnalogChannel::BWWide : AnalogChannel::BWNarrow;\n}\n\nbool\nD868UVFileReader::ChannelElement::rxOnly() const {\n  return *(_data + 0x0f);\n}\n\nSignaling::Code\nD868UVFileReader::ChannelElement::rxSignaling() const {\n  ///@todo Implement.\n  return Signaling::SIGNALING_NONE;\n}\n\nSignaling::Code\nD868UVFileReader::ChannelElement::txSignaling() const {\n  ///@todo Implement.\n  return Signaling::SIGNALING_NONE;\n}\n\nDigitalChannel::Admit\nD868UVFileReader::ChannelElement::admitDigital() const {\n  D868UVCodeplug::channel_t::Admit admit = (D868UVCodeplug::channel_t::Admit) * (_data + 0x20);\n  switch (admit) {\n  case D868UVCodeplug::channel_t::ADMIT_CH_FREE: return DigitalChannel::AdmitFree;\n  case D868UVCodeplug::channel_t::ADMIT_COLORCODE: return DigitalChannel::AdmitColorCode;\n  case D868UVCodeplug::channel_t::ADMIT_ALWAYS:\n  default:\n    break;\n  }\n  return DigitalChannel::AdmitNone;\n}\n\nAnalogChannel::Admit\nD868UVFileReader::ChannelElement::admitAnalog() const {\n  D868UVCodeplug::channel_t::Admit admit = (D868UVCodeplug::channel_t::Admit) * (_data + 0x20);\n  switch (admit) {\n  case D868UVCodeplug::channel_t::ADMIT_CH_FREE: return AnalogChannel::AdmitFree;\n  case D868UVCodeplug::channel_t::ADMIT_ALWAYS:\n  default:\n    break;\n  }\n  return AnalogChannel::AdmitNone;\n}\n\nuint8_t\nD868UVFileReader::ChannelElement::colorCode() const {\n  return *(_data + 0x28);\n}\n\nDigitalChannel::TimeSlot\nD868UVFileReader::ChannelElement::timeSlot() const {\n  return (0==(*(_data + 0x2a))) ? DigitalChannel::TimeSlot1 : DigitalChannel::TimeSlot2;\n}\n\nQString\nD868UVFileReader::ChannelElement::name() const {\n  return QString::fromLocal8Bit((char *)(_data + 0x30), _nameLength);\n}\n\nsize_t\nD868UVFileReader::ChannelElement::size() const {\n  return 0x30 + _nameLength+1 + 0x14;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader::RadioIDElement\n * ********************************************************************************************* */\nD868UVFileReader::RadioIDElement::RadioIDElement(const uint8_t *ptr)\n  : AnytoneFileReader::Element(ptr)\n{\n  _nameLength = strnlen((char *)(_data+0x04), 16);\n}\n\nuint8_t\nD868UVFileReader::RadioIDElement::index() const {\n  return *(_data + 0x00);\n}\n\nuint32_t\nD868UVFileReader::RadioIDElement::id() const {\n  uint32_t a=_data[1], b=_data[2], c=_data[3];\n  return ((c<<16) | (b<<8) | a);\n}\n\nQString\nD868UVFileReader::RadioIDElement::name() const {\n  return QString::fromLocal8Bit((char *)(_data+4), _nameLength);\n}\n\nsize_t\nD868UVFileReader::RadioIDElement::size() const {\n  return 4 + _nameLength+1;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader::ZoneElement\n * ********************************************************************************************* */\nD868UVFileReader::ZoneElement::ZoneElement(const uint8_t *ptr)\n  : AnytoneFileReader::Element(ptr)\n{\n  _numChannels = *(_data + 0x01);\n  _nameLength = strnlen((char *)(_data+0x02 + 2*_numChannels + 2*2), 16);\n}\n\nuint8_t\nD868UVFileReader::ZoneElement::index() const {\n  return *(_data+0x00);\n}\n\nuint8_t\nD868UVFileReader::ZoneElement::numChannels() const {\n  return _numChannels;\n}\n\nuint16_t\nD868UVFileReader::ZoneElement::channel(uint8_t index) const {\n  return qFromLittleEndian(*(uint16_t *)(_data + 0x02 + 2*index));\n}\n\nQString\nD868UVFileReader::ZoneElement::name() const {\n  return QString::fromLocal8Bit((char *)(_data+0x02+2*_numChannels+2*2), _nameLength);\n}\n\nsize_t\nD868UVFileReader::ZoneElement::size() const {\n  return 0x02 + 2*_numChannels + 2*2 + _nameLength+1;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader::ScanListElement\n * ********************************************************************************************* */\nD868UVFileReader::ScanListElement::ScanListElement(const uint8_t *ptr)\n  : AnytoneFileReader::Element(ptr)\n{\n  _nameLength = strnlen((char *)(_data+0x01), 16);\n  _numChannels = *(_data + 0x01 + _nameLength+1 + 0x0b);\n}\n\nuint8_t\nD868UVFileReader::ScanListElement::index() const {\n  return *(_data+0x00);\n}\n\nuint8_t\nD868UVFileReader::ScanListElement::numChannels() const {\n  return _numChannels;\n}\n\nD868UVCodeplug::scanlist_t::PriChannel\nD868UVFileReader::ScanListElement::prioChannelSelect() const {\n  return (D868UVCodeplug::scanlist_t::PriChannel)(* (_data + 0x01 + _nameLength+1 + 0x01));\n}\n\nuint16_t\nD868UVFileReader::ScanListElement::prioChannel1() const {\n  return qFromLittleEndian((uint16_t)(* (_data + 0x01 + _nameLength+1 + 0x02)));\n}\n\nuint16_t\nD868UVFileReader::ScanListElement::prioChannel2() const {\n  return qFromLittleEndian((uint16_t)(* (_data + 0x01 + _nameLength+1 + 0x04)));\n}\n\nuint16_t\nD868UVFileReader::ScanListElement::channel(uint8_t index) const {\n  return qFromLittleEndian(*(uint16_t *)(_data + 0x01 + _nameLength+1 + 0x0b + 0x03 + 2*index));\n}\n\nQString\nD868UVFileReader::ScanListElement::name() const {\n  return QString::fromLocal8Bit((char *)(_data+1), _nameLength);\n}\n\nsize_t\nD868UVFileReader::ScanListElement::size() const {\n  return 0x0f + 2*_numChannels + _nameLength+1;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader::AnalogContactElement\n * ********************************************************************************************* */\nD868UVFileReader::AnalogContactElement::AnalogContactElement(const uint8_t *ptr)\n  : AnytoneFileReader::Element(ptr)\n{\n  _numberLength = *(_data+1);\n  _nameLength = strnlen((char *)(_data+0x02+_numberLength), 16);\n}\n\nuint8_t\nD868UVFileReader::AnalogContactElement::index() const {\n  return *(_data + 0x00);\n}\n\nQString\nD868UVFileReader::AnalogContactElement::number() const {\n  return QString::fromLocal8Bit((char *)(_data+0x02), _numberLength);\n}\n\nQString\nD868UVFileReader::AnalogContactElement::name() const {\n  return QString::fromLocal8Bit((char *)(_data+0x02+_numberLength), _nameLength);\n}\n\nsize_t\nD868UVFileReader::AnalogContactElement::size() const {\n  return 0x02 + _numberLength + _nameLength+1;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of D868UVFileReader\n * ********************************************************************************************* */\nD868UVFileReader::D868UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message)\n  : AnytoneFileReader(config, data, size, message)\n{\n  // pass...\n}\n\nbool\nD868UVFileReader::readHeader() {\n  // Header content is ignored, only advance pointer to end of header\n  _data += HEADER_SIZE;\n  return true;\n}\n\nbool\nD868UVFileReader::linkHeader() {\n  // Header content is ignored, only advance pointer to end of header\n  _data += HEADER_SIZE;\n  return true;\n}\n\nbool\nD868UVFileReader::readChannels() {\n  // Read number of channels and advance pointer\n  uint16_t numChannels = qFromLittleEndian(*(uint16_t *)_data); _data += 0x02;\n  // Read each channel\n  for (uint16_t i=0; i<numChannels; i++) {\n    if (! this->readChannel())\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readChannel() {\n  ChannelElement channel(_data);\n  // Assemble channel\n  Channel *ch = nullptr;\n  if (D868UVCodeplug::channel_t::MODE_ANALOG == channel.mode()) {\n    ch = new AnalogChannel(\n          channel.name(), channel.rxFrequency(), channel.txFrequency(), channel.power(), 0,\n          channel.rxOnly(), channel.admitAnalog(), 2, channel.rxSignaling(), channel.txSignaling(),\n          channel.bandwidth(), nullptr, nullptr, nullptr);\n  } else if (D868UVCodeplug::channel_t::MODE_DIGITAL == channel.mode()) {\n    ch = new DigitalChannel(\n          channel.name(), channel.rxFrequency(), channel.txFrequency(), channel.power(), 0,\n          channel.rxOnly(), channel.admitDigital(), channel.colorCode(), channel.timeSlot(),\n          nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);\n  }\n  // Store channel in context & config if not VFO channel\n  if ((4000 != channel.index()) && (4001 != channel.index())) {\n    logDebug() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n               << \": Add channel '\" << channel.name() << \"'.\";\n    _context.addChannel(ch, channel.index());\n  }\n  // Advance pointer\n  _data += channel.size();\n  return true;\n}\n\nbool\nD868UVFileReader::linkChannels() {\n  // Get number of channels and advance pointer\n  uint16_t numChannels = qFromLittleEndian(*(uint16_t *)_data); _data += 0x02;\n  // Link each channel\n  for (uint16_t i=0; i<numChannels; i++) {\n    if (! this->linkChannel())\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::linkChannel() {\n  ChannelElement channel(_data);\n  // Advance pointer\n  _data += channel.size();\n  return true;\n}\n\n\nbool\nD868UVFileReader::readRadioIDs() {\n  // Read number of radio IDs and advance pointer\n  uint8_t numIDs = *_data; _data += 1;\n  // Read each radio ID\n  for (uint8_t i=0; i<numIDs; i++) {\n    if (! this->readRadioID())\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readRadioID() {\n  RadioIDElement radioid(_data);\n  // Assemble radio ID & add to context/config\n  if (0 == radioid.index()) {\n    _context.setDefaultRadioId(radioid.id(), radioid.index());\n    _context.config()->setName(radioid.name());\n    logDebug() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n               << \": Set default radio ID \" << radioid.id() << \" ('\" << radioid.name() << \"').\";\n  } else {\n    _context.addRadioId(radioid.id(), radioid.index());\n    logDebug() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n               << \": Add radio ID \" << radioid.id() << \" ('\" << radioid.name() << \"').\";\n  }\n  // Advance pointer\n  _data += radioid.size();\n  return true;\n}\n\nbool\nD868UVFileReader::linkRadioIDs() {\n  // Read number of radio IDs and advance pointer\n  uint8_t numIDs = *_data; _data += 1;\n  // skip each radio ID\n  for (uint8_t i=0; i<numIDs; i++) {\n    RadioIDElement id(_data); _data += id.size();\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readZones() {\n  // Read number of zones and advance pointer\n  uint8_t numZones = *_data; _data += 1;\n  for (uint8_t i=0; i<numZones; i++) {\n    if (! this->readZone())\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readZone() {\n  ZoneElement zone(_data);\n  // Assemble Zone & add to context/config\n  Zone *z = new Zone(zone.name());\n  if (! _context.config()->zones()->addZone(z)) {\n    _message = QObject::tr(\"0x%1: Cannot add zone to config.\").arg(uint(_data-_start), 8,16,QChar('0'));\n    return false;\n  }\n  for (uint8_t i=0; i<zone.numChannels(); i++) {\n    if (! _context.hasChannel(zone.channel(i))) {\n      _message = QObject::tr(\"0x%1: Cannot add channel %2 to zone '%3'.\")\n          .arg(uint(_data-_start), 8,16,QChar('0')).arg(zone.channel(i)).arg(zone.name());\n      return false;\n    }\n    z->A()->addChannel(_context.getChannel(zone.channel(i)));\n    logDebug() << \"Add channel '\" << _context.getChannel(zone.channel(i))->name()\n               << \"' to zone '\" << zone.name() << \"'.\";\n  }\n  logDebug() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n             << \": Add zone '\" << zone.name() << \"'.\";\n  // Advance pointer\n  _data += zone.size();\n  return true;\n}\n\nbool\nD868UVFileReader::linkZones() {\n  // Read number of zones and advance pointer\n  uint8_t numZones = *_data; _data += 1;\n  // Skip each zone, zones are linked during creation\n  for (uint8_t i=0; i<numZones; i++) {\n    ZoneElement zone(_data); _data += zone.size();\n  }\n  return true;\n}\n\n\nbool\nD868UVFileReader::readScanLists() {\n  // Read number of scan lists and advance pointer\n  uint8_t numScanlists = *_data; _data += 1;\n  // Read each scan list\n  for (uint8_t i=0; i<numScanlists; i++) {\n    if (! this->readScanList())\n      return false;\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readScanList() {\n  ScanListElement sl(_data);\n\n  ScanList *scanlist = new ScanList(sl.name());\n  _context.addScanList(scanlist, sl.index());\n  logDebug() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n             << \": Add scan list '\" << sl.name() << \"'.\";\n\n  for (uint8_t i=0; i<sl.numChannels(); i++) {\n    if (! _context.hasChannel(sl.channel(i))) {\n      _message = QObject::tr(\"0x%1: Cannot add channel %2 to scan list '%3'.\")\n          .arg(uint(_data-_start), 8,16,QChar('0')).arg(sl.channel(i)).arg(sl.name());\n      return false;\n    }\n    scanlist->addChannel(_context.getChannel(sl.channel(i)));\n    logDebug() << \"Add channel '\" << _context.getChannel(sl.channel(i))->name()\n               << \"' to zone '\" << sl.name() << \"'.\";\n    if ((D868UVCodeplug::scanlist_t::PRIO_CHAN_SEL1 & sl.prioChannelSelect()) &&\n        (0 != sl.prioChannel1()))\n    {\n      if (1 == sl.prioChannel1())\n        scanlist->setPriorityChannel(SelectedChannel::get());\n      else if (_context.hasChannel(sl.prioChannel1()-2))\n        scanlist->setPriorityChannel(_context.getChannel(sl.prioChannel1()-2));\n      else\n        logWarn() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n                  << \"Cannot link priority channel index \" << (sl.prioChannel1()-2)\n                  << \" in scanlist '\" << sl.name() << \"'.\";\n    }\n    if ((D868UVCodeplug::scanlist_t::PRIO_CHAN_SEL2 & sl.prioChannelSelect()) &&\n        (0 != sl.prioChannel2()))\n    {\n      if (1 == sl.prioChannel2())\n        scanlist->setSecPriorityChannel(SelectedChannel::get());\n      else if (_context.hasChannel(sl.prioChannel2()-2))\n        scanlist->setSecPriorityChannel(_context.getChannel(sl.prioChannel2()-2));\n      else\n        logWarn() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n                  << \"Cannot link sec. priority channel index \" << (sl.prioChannel2()-2)\n                  << \" in scanlist '\" << sl.name() << \"'.\";\n    }\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::linkScanLists() {\n  // Read number of scan lists and advance pointer\n  uint8_t numScanlists = *_data; _data += 1;\n  // Skip each scan list, scan lists are linked during creation\n  for (uint8_t i=0; i<numScanlists; i++) {\n    ScanListElement sl(_data); _data += sl.size();\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readAnalogContacts() {\n  // Read number of scan lists and advance pointer\n  uint8_t numContacts = *_data; _data += 1;\n  // Read analog contact\n  for (uint8_t i=0; i<numContacts; i++) {\n    this->readAnalogContact();\n  }\n  return true;\n}\n\nbool\nD868UVFileReader::readAnalogContact() {\n  AnalogContactElement ac(_data);\n  if (validDTMFNumber(ac.number())) {\n    DTMFContact *contact = new DTMFContact(ac.name(), ac.number());\n    _context.addAnalogContact(contact, ac.index());\n  } else {\n    logWarn() << QString(\"0x%1\").arg(uint(_data-_start), 8,16,QChar('0'))\n              << \": Skip analog contact '\" << ac.name()\n              << \". Invalid DTMF number '\" << ac.number() << \"'.\";\n  }\n  _data += ac.size();\n  return true;\n}\n\nbool\nD868UVFileReader::linkAnalogContacts() {\n  // Read number of scan lists and advance pointer\n  uint8_t numContacts = *_data; _data += 1;\n  // Skip each analog contact, analog contacts need no linking\n  for (uint8_t i=0; i<numContacts; i++) {\n    AnalogContactElement elm(_data); _data += elm.size();\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/d868uv_filereader.hh",
    "content": "#ifndef D868UVFILEREADER_HH\n#define D868UVFILEREADER_HH\n\n#include \"d868uv_codeplug.hh\"\n#include \"anytone_filereader.hh\"\n\n/** Reads manufacturer CPS files.\n * @warning This is mostly incomplete. */\nclass D868UVFileReader: public AnytoneFileReader\n{\npublic:\n  /** Representsa a channel encoded in the manufacturer CPS file. */\n  class ChannelElement: public AnytoneFileReader::Element\n  {\n  public:\n    /** Constructor. */\n    ChannelElement(const uint8_t *ptr);\n    /** Returns the index of the channel.*/\n    uint16_t index() const;\n    /** Returns the channel mode (Analog, Digital, Mixed...). */\n    D868UVCodeplug::channel_t::Mode mode() const;\n    /** Return the RX frequency. */\n    double rxFrequency() const;\n    /** Return the TX frequency. */\n    double txFrequency() const;\n    /** Returns the power setting. */\n    Channel::Power power() const;\n    /** Returns the bandwidth. */\n    AnalogChannel::Bandwidth bandwidth() const;\n    /** Returns @c true if the channel is RX only. */\n    bool rxOnly() const;\n    /** Returns the RX CTCSS/DCS settings. */\n    Signaling::Code rxSignaling() const;\n    /** Returns the TX CTCSS/DCS settings. */\n    Signaling::Code txSignaling() const;\n    /** Returns the admit cirterion for the channel (if digital). */\n    DigitalChannel::Admit admitDigital() const;\n    /** Returns the admit cirterion for the channel (if analog). */\n    AnalogChannel::Admit admitAnalog() const;\n    /** Returns the color code of the channel (if digital). */\n    uint8_t colorCode() const;\n    /** Returns the time slot of the channel (if digital). */\n    DigitalChannel::TimeSlot timeSlot() const;\n    /** Returns the name of the channel. */\n    QString name() const;\n\n    size_t size() const;\n\n  protected:\n    /** The length of the name. */\n    size_t _nameLength;\n  };\n\n  /** Decodes a radio ID from the manufacturer CPS file. */\n  class RadioIDElement: public AnytoneFileReader::Element\n  {\n  public:\n    /** Constructor for radio ID element. */\n    RadioIDElement(const uint8_t *ptr);\n\n    /** Returns the index of the ID. */\n    uint8_t index() const;\n    /** Returns the ID. */\n    uint32_t id() const;\n    /** Returns the name of the radio ID. */\n    QString name() const;\n\n    size_t size() const;\n\n  protected:\n    /** Holds the length of the name. */\n    size_t _nameLength;\n  };\n\n  /** Represents a zone within the manufacturer CPS file. */\n  class ZoneElement: public AnytoneFileReader::Element\n  {\n  public:\n    /** Constructor. */\n    ZoneElement(const uint8_t *ptr);\n\n    /** Returns the index of the zone. */\n    uint8_t index() const;\n    /** Returns the number of channels in A. */\n    uint16_t channelA() const;\n    /** Returns the number of channels in B. */\n    uint16_t channelB() const;\n    /** Returns the total number of channels in zone. */\n    uint8_t numChannels() const;\n    /** Returns the channel index for the i-th entry. */\n    uint16_t channel(uint8_t index) const;\n    /** Returns the name of the zone. */\n    QString name() const;\n\n    size_t size() const;\n\n  protected:\n    /** Holds the number of channels. */\n    uint8_t _numChannels;\n    /** Holds the name length. */\n    size_t _nameLength;\n  };\n\n  /** Represents a scan list in the manufacturer CPS file. */\n  class ScanListElement: public AnytoneFileReader::Element\n  {\n  public:\n    /** Constructor. */\n    ScanListElement(const uint8_t *ptr);\n\n    /** Returns the index of the scan list. */\n    uint8_t index() const;\n    /** Returns the number of channels in the scan list. */\n    uint8_t numChannels() const;\n    /** Returns the priority channel selection. */\n    AnytoneCodeplug::ScanListElement::PriChannel prioChannelSelect() const;\n    /** Returns the priority channel 1 index. */\n    uint16_t prioChannel1() const;\n    /** Returns the priority channel 2 index. */\n    uint16_t prioChannel2() const;\n    /** Returns the i-th channel index. */\n    uint16_t channel(uint8_t index) const;\n    /** Returns the name of the scan list. */\n    QString name() const;\n\n    size_t size() const;\n\n  protected:\n    /** Holds the number of channels. */\n    uint8_t _numChannels;\n    /** Holds the name length. */\n    size_t _nameLength;\n  };\n\n  /** Represents an analog contact within the manufacturer CPS file. */\n  class AnalogContactElement: public AnytoneFileReader::Element\n  {\n  public:\n    /** Constructor. */\n    AnalogContactElement(const uint8_t *ptr);\n\n    /** Returns the index of the analog contact. */\n    uint8_t index() const;\n    /** Returns the number. */\n    QString number() const;\n    /** Returns the name. */\n    QString name() const;\n\n    size_t size() const;\n\n  protected:\n    /** Returns the length of the number. */\n    uint8_t _numberLength;\n    /** Returns the length of the name. */\n    size_t _nameLength;\n  };\n\n\npublic:\n  /** Constructs a file reader for the given manufacturer CPS file (data, size). */\n  D868UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message);\n\n  bool linkHeader();\n  bool readHeader();\n\n  bool readChannels();\n  /** Reads a channel. */\n  virtual bool readChannel();\n  bool linkChannels();\n  /** Links a channel. */\n  virtual bool linkChannel();\n\n  bool readRadioIDs();\n  /** Reads a radio ID. */\n  virtual bool readRadioID();\n  bool linkRadioIDs();\n\n  bool readZones();\n  /** Reads a zone. */\n  virtual bool readZone();\n  bool linkZones();\n\n  bool readScanLists();\n  /** Reads a scan list. */\n  virtual bool readScanList();\n  bool linkScanLists();\n\n  bool readAnalogContacts();\n  /** Reads an analog contact. */\n  virtual bool readAnalogContact();\n  bool linkAnalogContacts();\n};\n\n#endif // D868UVFILEREADER_HH\n"
  },
  {
    "path": "lib/d868uv_limits.cc",
    "content": "#include \"d868uv_limits.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"d868uv_codeplug.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nD868UVLimits::D868UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                           const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                           const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V102\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 200000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n            {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n            {\"password\", new RadioLimitPin(D868UVCodeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned)DMRContact::PrivateCall,\n                (unsigned)DMRContact::GroupCall,\n                (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n            { \"number\", new RadioLimitString(1, 14, RadioLimitString::DTMF) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1,16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored()}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(DMRAPRSSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get()) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Ignore positioning systems. */\n  add(\"positioning\", new RadioLimitList({\n        { DMRAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n          { \"name\", new RadioLimitStringIgnored() },\n          { \"period\", new RadioLimitUInt(0, 7650) },\n          { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n          { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) );\n\n\n  /* Ignore roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/d868uv_limits.hh",
    "content": "#ifndef D868UVLIMITS_HH\n#define D868UVLIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the AnyTone AT-D878UV.\n * @ingroup d868uv */\nclass D868UVLimits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  D868UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n               const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n               const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D868UVLIMITS_HH\n"
  },
  {
    "path": "lib/d878uv.cc",
    "content": "#include \"userdatabase.hh\"\n#include \"d878uv.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv_limits.hh\"\n// uses same callsign db as 878\n#include \"d868uv_callsigndb.hh\"\n\n#define RBSIZE 16\n#define WBSIZE 16\n\n\nD878UV::D878UV(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"Anytone AT-D878UV\", device, parent), _limits(nullptr)\n{\n  _codeplug = new D878UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D868UVCallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n  case 0x01:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this);\n    break;\n  case 0x0d:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x0e:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                 {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0f:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                 {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x10:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x11:\n    _limits = new D878UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                 {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                               { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Do not check frequency range.\";\n    _limits = new D878UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nD878UV::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nD878UV::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::D878UV, \"d878uv\", \"AT-D878UV\", \"AnyTone\", {AnytoneGD32Interface::interfaceInfo()});\n}\n"
  },
  {
    "path": "lib/d878uv.hh",
    "content": "/** @defgroup d878uv Anytone AT-D878UV\n * Device specific classes for Anytone AT-D878UV.\n *\n * \\image html d878uv.jpg \"AT-D878UV\" width=200px\n * \\image latex d878uv.jpg \"AT-D878UV\" width=200px\n *\n * @ingroup anytone */\n#ifndef __D878UV_HH__\n#define __D878UV_HH__\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n\n\n/** Implements an interface to Anytone AT-D878UV VHF/UHF 7W DMR (Tier I & II) radios.\n *\n * The reverse-engineering of the D878UVCodeplug was quiet hard as it is huge and the radio\n * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the\n * windows CPS does not directly relate to the data being written to the device. These two issues\n * (a lot of features and a huge code-plug) require that the transfer of the code-plug to the\n * device is performed in 4 steps.\n *\n * First only the bitmaps of all lists are downloaded from the device. Then all elements that are\n * not touched or only updated by the common code-plug config are downloaded. Then, the common\n * config gets applied to the binary code-plug. That is, all channels, contacts, zones, group-lists\n * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config\n * gets updated from the common code-plug settings. Finally, the resulting binary code-plug gets\n * written back to the device.\n *\n * This rather complex method of writing a code-plug to the device is needed to maintain all\n * settings within the radio that are not defined within the common code-plug config while keeping\n * the amount of data being read from and written to the device small.\n *\n * @ingroup d878uv */\nclass D878UV: public AnytoneRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit D878UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  RadioLimits *_limits;\n};\n\n#endif // __D878UV_HH__\n"
  },
  {
    "path": "lib/d878uv2.cc",
    "content": "#include \"userdatabase.hh\"\n#include \"d878uv2.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n\n#include \"d878uv2_limits.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"d878uv2_callsigndb.hh\"\n\n#define RBSIZE 16\n#define WBSIZE 16\n\n\nD878UV2::D878UV2(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"Anytone AT-D878UVII\", device, parent), _limits(nullptr)\n{\n  _codeplug = new D878UV2Codeplug(this);\n  _codeplug->clear();\n  _callsigns = new D878UV2CallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n  case 0x01:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(434.), Frequency::fromMHz(438.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(434.), Frequency::fromMHz(437.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                  {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(490.)} }, info.version, this);\n    break;\n  case 0x0d:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x0e:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(220.),Frequency::fromMHz(225.)},\n                                  {Frequency::fromMHz(400.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x0f:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                  {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                  {Frequency::fromMHz(420.), Frequency::fromMHz(520.)} }, info.version, this);\n    break;\n  case 0x10:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                { {Frequency::fromMHz(144.), Frequency::fromMHz(147.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x11:\n    _limits = new D878UV2Limits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                  {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Do not check frequency range.\";\n    _limits = new D878UV2Limits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nD878UV2::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nD878UV2::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::D878UVII, \"d878uv2\", \"AT-D878UVII\", \"AnyTone\",\n        {AnytoneGD32Interface::interfaceInfo()});\n}\n"
  },
  {
    "path": "lib/d878uv2.hh",
    "content": "/** @defgroup d878uv2 Anytone AT-D878UVII\n * Device specific classes for Anytone AT-D878UVII.\n *\n * \\image html d878uv.jpg \"AT-D878UV\" width=200px\n * \\image latex d878uv.jpg \"AT-D878UV\" width=200px\n *\n * @ingroup anytone */\n#ifndef __D878UV2_HH__\n#define __D878UV2_HH__\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n#include \"d878uv2_callsigndb.hh\"\n\n/** Implements an interface to Anytone AT-D878UVII VHF/UHF 7W DMR (Tier I & II) radios.\n *\n * The reverse-engineering of the D878UVCodeplug was quiet hard as it is huge and the radio\n * provides a lot of bells and whistles. Moreover, the binary code-plug file created by the\n * windows CPS does not directly relate to the data being written to the device. These two issues\n * (a lot of features and a huge code-plug) require that the transfer of the code-plug to the\n * device is performed in 4 steps.\n *\n * First only the bitmaps of all lists are downloaded from the device. Then all elements that are\n * not touched or only updated by the common code-plug config are downloaded. Then, the common\n * config gets applied to the binary code-plug. That is, all channels, contacts, zones, group-lists\n * and scan-lists are generated and their bitmaps gets updated accordingly. Also the general config\n * gets updated from the common code-plug settings. Finally, the resulting binary code-plug gets\n * written back to the device.\n *\n * This rather complex method of writing a code-plug to the device is needed to maintain all\n * settings within the radio that are not defined within the common code-plug config while keeping\n * the amount of data being read from and written to the device small.\n *\n * @ingroup d878uv2 */\nclass D878UV2: public AnytoneRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit D878UV2(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** The limits for the radio. */\n  RadioLimits *_limits;\n};\n\n#endif // __D878UV2_HH__\n"
  },
  {
    "path": "lib/d878uv2_callsigndb.cc",
    "content": "#include \"userdatabase.hh\"\n#include \"d878uv2_callsigndb.hh\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n#include <QtEndian>\n\n/* ********************************************************************************************* *\n * Implementation of D878UVCallsignDB\n * ********************************************************************************************* */\nD878UV2CallsignDB::D878UV2CallsignDB(QObject *parent)\n  : D868UVCallsignDB(parent)\n{\n  // pass...\n}\n\nbool\nD878UV2CallsignDB::encode(UserDatabase *db, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Determine size of call-sign DB in memory\n  qint64 n = std::min(db->count(), qint64(Limit::entries()));\n  // If DB size is limited by settings\n  if (selection.hasCountLimit())\n    n = std::min(n, (qint64)selection.countLimit());\n  logDebug() << \"Encode \" << n << \" entries.\";\n\n  // Select n users and sort them in ascending order of their IDs\n  QVector<UserDatabase::User> users;\n  users.reserve(n);\n  for (unsigned i=0; i<n; i++)\n    users.append(db->user(i));\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n\n  logDebug() << \"Encode call-signs from \" << users.first().id << \": \" << users.first().call << \", \"\n             << users.first().name << \" in \" << users.first().city << \" to \" << users.last().id\n             << \": \" << users.last().call << \", \" << users.last().name << \" in \" << users.last().city << \".\";\n\n  // Compute total size of callsign db entries\n  size_t dbSize = 0;\n  size_t indexSize = n*IndexEntryElement::size();\n  for (qint64 i=0; i<n; i++)\n    dbSize += EntryElement::size(users[i]);\n\n  // Allocate DB limits\n  image(0).addElement(Offset::limits(), LimitsElement::size());\n  // Store DB limits\n  LimitsElement limits(data(Offset::limits()));\n  limits.clear();\n  limits.setCount(n);\n  limits.setTotalSize(dbSize);\n\n  // Allocate index banks\n  for (int i=0; 0<indexSize; i++, indexSize-=std::min(indexSize, size_t(IndexBankElement::size()))) {\n    size_t addr = Offset::index() + i*Offset::betweenIndexBanks();\n    size_t size = align_size(std::min(indexSize, size_t(IndexBankElement::size())), 16);\n    image(0).addElement(addr, size);\n    memset(data(addr), 0xff, size);\n  }\n\n  // Allocate entry banks\n  for (int i=0; 0<dbSize; i++, dbSize-=std::min(dbSize, size_t(EntryBankElement::size()))) {\n    size_t addr = Offset::callsigns() + i*Offset::betweenCallsignBanks();\n    size_t size = align_size(std::min(dbSize, size_t(EntryBankElement::size())), 16);\n    image(0).addElement(addr, size);\n    memset(data(addr), 0x00, size);\n  }\n\n  // Fill index, the offset of the entry is not the real memory offset,\n  // but a virtual one without the gaps.\n  uint32_t entry_offset = 0;\n  uint32_t index_offset = 0;\n  uint32_t index_bank   = 0;\n  for (qint64 i=0; i<n; i++, index_offset+=IndexEntryElement::size()) {\n    if (IndexBankElement::size() <= index_offset) {\n      index_offset = 0; index_bank += 1;\n    }\n    IndexEntryElement index(data(Offset::index()+index_bank*Offset::betweenIndexBanks()+index_offset));\n    index.setID(users[i].id, false);\n    index.setIndex(entry_offset);\n    entry_offset += EntryElement::size(users[i]);\n  }\n\n  // Then store DB entries\n  uint32_t entry_bank = 0;\n  entry_offset = 0;\n  for (qint64 i=0; i<n; i++) {\n    // Get size of current entry\n    uint32_t entry_size = EntryElement::size(users[i]);\n    // Check if entry fits into bank\n    if (EntryBankElement::size() < (entry_offset+entry_size)) {\n      // If not, split\n      uint8_t buffer[100]; EntryElement(buffer).fromUser(users[i]);\n      uint32_t n1 = (EntryBankElement::size()-entry_offset);\n      uint32_t n2 = entry_size-n1;\n      if (0 != n1) {\n        // Copy first half\n        uint8_t *ptr = data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset);\n        memcpy(ptr, buffer, n1);\n      }\n      // advance bank counter\n      entry_bank++; entry_offset = 0;\n      // copy second half\n      uint8_t *ptr = data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset);\n      memcpy(ptr, buffer+n1, n2);\n      // Update offset\n      entry_offset += n2;\n    } else {\n      // when it fits, just add\n      EntryElement(data(Offset::callsigns()+entry_bank*Offset::betweenCallsignBanks()+entry_offset))\n          .fromUser(users[i]);\n      entry_offset += entry_size;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/d878uv2_callsigndb.hh",
    "content": "#ifndef D878UV2CALLSIGNDB_HH\n#define D878UV2CALLSIGNDB_HH\n\n#include \"d868uv_callsigndb.hh\"\n\n/** Represents and encodes the binary format for the call-sign database within the radio.\n *\n * <table>\n *  <tr><th colspan=\"3\">Callsign database</th></tr>\n *  <tr><th>Start</th>    <th>Size</th>        <th>Content</th></tr>\n *  <tr><td>04000000</td> <td>variable</th>    <td>Index of callsign entries. Follows the same\n *   weird format as @c D868UVCodeplug::contact_map_t. Sorted by ID. Empty entries set to\n *   0xffffffffffffffff.</td></tr>\n *  <tr><td>04840000</td> <td>000010</td>      <td>Database limits, see @c limits_t.</td></tr>\n *  <tr><td>05500000</td> <td>variable</td>    <td>The actual DB entries, each entry is of\n *   variable size but shares the same header, see @c entry_t. Order arbitrary.\n *   Filled with 0x00.</td></tr>\n * </table>\n *\n * @ingroup d878uv2 */\nclass D878UV2CallsignDB : public D868UVCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Same index entry used by the codeplug to map normal digital contacts to an contact index. Here\n   * it maps to the byte offset within the database entries. */\n  typedef D868UVCodeplug::ContactMapElement IndexEntryElement;\n\npublic:\n  /** Constructor, does not allocate any memory yet. */\n  explicit D878UV2CallsignDB(QObject *parent=nullptr);\n\n  /** Tries to encode as many entries of the given user-database. */\n  bool encode(UserDatabase *db, const Flags &selection=Flags(),\n              const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits of the call-sign DB. */\n  struct Limit : public D868UVCallsignDB::Limit {\n    /// Specifies the max number of entries in the call-sign DB. */\n    static constexpr unsigned int entries() { return 500000; }\n  };\n\nprotected:\n  /** Some internal offsets within the call-sign DB. */\n  struct Offset : public D868UVCallsignDB::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int callsigns()            { return 0x05500000; }\n    static constexpr unsigned int limits()               { return 0x04840000; }\n\n    /// @endcond\n  };\n};\n\n#endif // D868UVCALLSIGNDB_HH\n"
  },
  {
    "path": "lib/d878uv2_codeplug.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"utils.hh\"\n#include \"channel.hh\"\n#include \"config.h\"\n\n#include <QTimeZone>\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UV2Codeplug\n * ******************************************************************************************** */\nD878UV2Codeplug::D878UV2Codeplug(const QString &label, QObject *parent)\n  : D878UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nD878UV2Codeplug::D878UV2Codeplug(QObject *parent)\n  : D878UVCodeplug(\"AnyTone AT-D878UVII Codeplug\", parent)\n{\n  // pass...\n}\n\n/* The address of the contact ID<->Index table has changed hence allocation and encoding must\n * be reimplemented. Otherwise, everything remains the same. */\nvoid\nD878UV2Codeplug::allocateContacts() {\n  /* Allocate contacts */\n  ContactBitmapElement contact_bitmap(data(Offset::contactBitmap()));\n  unsigned contactCount=0;\n  for (uint16_t i=0; i<Limit::numContacts(); i++) {\n    // enabled if false (ass hole)\n    if (! contact_bitmap.isEncoded(i))\n      continue;\n    contactCount++;\n    uint32_t bank_addr = Offset::contactBanks() + (contactCount/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + ((i%Limit::contactsPerBank())/Limit::contactsPerBlock())*Offset::betweenContactBlocks();\n    if (! isAllocated(addr, 0)) {\n      image(0).addElement(addr, Offset::betweenContactBlocks());\n      memset(data(addr), 0x00, Offset::betweenContactBlocks());\n    }\n  }\n  if (contactCount) {\n    image(0).addElement(Offset::contactIndex(), align_size(4*contactCount, 16));\n    memset(data(Offset::contactIndex()), 0xff, align_size(4*contactCount, 16));\n    image(0).addElement(Offset::contactIdTable(), align_size(ContactMapElement::size()*(1+contactCount), 16));\n    memset(data(Offset::contactIdTable()), 0xff, align_size(ContactMapElement::size()*(1+contactCount), 16));\n  }\n}\n\nbool\nD878UV2Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  QVector<DMRContact*> contacts;\n  // Encode contacts and also collect id<->index map\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    uint32_t bank_addr = Offset::contactBanks() + (i/Limit::contactsPerBank())*Offset::betweenContactBanks();\n    uint32_t addr = bank_addr + (i%Limit::contactsPerBank())*ContactElement::size();\n    ContactElement con(data(addr));\n    DMRContact *contact = ctx.get<DMRContact>(i);\n    if(! con.fromContactObj(contact, ctx))\n      return false;\n    ((uint32_t *)data(Offset::contactIndex()))[i] = qToLittleEndian(i);\n    contacts.append(contact);\n  }\n  // encode index map for contacts\n  std::sort(contacts.begin(), contacts.end(),\n            [](DMRContact *a, DMRContact *b) {\n    return a->number() < b->number();\n  });\n  for (int i=0; i<contacts.size(); i++) {\n    ContactMapElement el(data(Offset::contactIdTable() + i*ContactMapElement::size()));\n    el.setID(contacts[i]->number(), (DMRContact::GroupCall==contacts[i]->type()));\n    el.setIndex(ctx.index(contacts[i]));\n  }\n  return true;\n}\n\n\n"
  },
  {
    "path": "lib/d878uv2_codeplug.hh",
    "content": "#ifndef D878UV2_CODEPLUG_HH\n#define D878UV2_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"d878uv_codeplug.hh\"\n\nclass Channel;\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\n\n\n/** Represents the device specific binary codeplug for AnyTone AT-D878UVII radios.\n *\n * This class only implements the difference to the AT-D878UV codeplug. In fact there is only a\n * difference in the address of the contact ID<->Index map.\n *\n * @ingroup d878uv2 */\nclass D878UV2Codeplug : public D878UVCodeplug\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit D878UV2Codeplug(const QString &label, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit D878UV2Codeplug(QObject *parent = nullptr);\n\nprotected:\n  void allocateContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Internal used offsets within the codeplug. */\n  struct Offset: public D878UVCodeplug::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int contactIdTable()       { return 0x04800000; }\n    /// @endcond\n  };\n};\n\n#endif // D878UVCODEPLUG_HH\n"
  },
  {
    "path": "lib/d878uv2_limits.cc",
    "content": "#include \"d878uv2_limits.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nD878UV2Limits::D878UV2Limits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                             const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                             const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V101\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 500000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                       unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n            {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n            {\"password\", new RadioLimitPin(D878UV2Codeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n            { \"number\", new RadioLimitString(1, 14, RadioLimitString::DTMF) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                           unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::Mid),\n                                           unsigned(Channel::Power::High), unsigned(Channel::Power::Max)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, true)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/d878uv2_limits.hh",
    "content": "#ifndef D878UV2LIMITS_HH\n#define D878UV2LIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the AnyTone AT-D878UV2.\n * @ingroup d878uv2 */\nclass D878UV2Limits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  D878UV2Limits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D878UV2LIMITS_HH\n"
  },
  {
    "path": "lib/d878uv_codeplug.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"roamingchannel.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"channel.hh\"\n#include \"config.h\"\n#include \"logger.hh\"\n#include \"channel.hh\"\n\n#include \"intermediaterepresentation.hh\"\n\n#include <QTimeZone>\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::NameColor\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::NameColor::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case White: return AnytoneDisplaySettingsExtension::Color::White;\n  case Orange: return AnytoneDisplaySettingsExtension::Color::Orange;\n  case Red: return AnytoneDisplaySettingsExtension::Color::Red;\n  case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow;\n  case Green: return AnytoneDisplaySettingsExtension::Color::Green;\n  case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise;\n  case Blue: return AnytoneDisplaySettingsExtension::Color::Blue;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::White;\n}\n\nuint8_t\nD878UVCodeplug::NameColor::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange;\n  case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow;\n  case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green;\n  case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue;\n  default: break;\n  }\n  return (uint8_t) White;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::TextColor\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::TextColor::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case White: return AnytoneDisplaySettingsExtension::Color::White;\n  case Orange: return AnytoneDisplaySettingsExtension::Color::Orange;\n  case Red: return AnytoneDisplaySettingsExtension::Color::Red;\n  case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow;\n  case Green: return AnytoneDisplaySettingsExtension::Color::Green;\n  case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise;\n  case Blue: return AnytoneDisplaySettingsExtension::Color::Blue;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::White;\n}\n\nuint8_t\nD878UVCodeplug::TextColor::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange;\n  case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow;\n  case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green;\n  case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue;\n  default: break;\n  }\n  return (uint8_t) White;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nD878UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)\n  : D868UVCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : D868UVCodeplug::ChannelElement(ptr, ChannelElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::ChannelElement::clear() {\n  D868UVCodeplug::ChannelElement::clear();\n  setPTTIDSetting(PTTId::Off);\n}\n\nD878UVCodeplug::ChannelElement::PTTId\nD878UVCodeplug::ChannelElement::pttIDSetting() const {\n  return (PTTId)getUInt2(Offset::pttIDSetting(), 0);\n}\nvoid\nD878UVCodeplug::ChannelElement::setPTTIDSetting(PTTId ptt) {\n  setUInt2(Offset::pttIDSetting(), 0, (unsigned)ptt);\n}\n\nbool\nD878UVCodeplug::ChannelElement::roamingEnabled() const {\n  // inverted\n  return !getBit(Offset::roaming());\n}\nvoid\nD878UVCodeplug::ChannelElement::enableRoaming(bool enable) {\n  // inverted\n  setBit(Offset::roaming(), !enable);\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::dataACK() const {\n  // inverted\n  return !getBit(Offset::dataACK());\n}\n\nvoid\nD878UVCodeplug::ChannelElement::enableDataACK(bool enable) {\n  // inverted\n  setBit(Offset::dataACK(), !enable);\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::autoScan() const {\n  return getBit(Offset::autoScan());\n}\nvoid\nD878UVCodeplug::ChannelElement::enableAutoScan(bool enable) {\n  setBit(Offset::autoScan(), enable);\n}\n\nD878UVCodeplug::ChannelElement::APRSType\nD878UVCodeplug::ChannelElement::txAPRSType() const {\n  return (APRSType)getUInt8(Offset::txAPRSType());\n}\nvoid\nD878UVCodeplug::ChannelElement::setTXAPRSType(APRSType aprsType) {\n  setUInt8(Offset::txAPRSType(), (uint8_t)aprsType);\n}\n\nAnytoneChannelExtension::APRSPTT\nD878UVCodeplug::ChannelElement::analogAPRSPTTSetting() const {\n  switch ((APRSPTT)getUInt8(Offset::fmAPRSPTTSetting())) {\n  case APRSPTT::Off: return AnytoneChannelExtension::APRSPTT::Off;\n  case APRSPTT::Start: return AnytoneChannelExtension::APRSPTT::Start;\n  case APRSPTT::End: return AnytoneChannelExtension::APRSPTT::End;\n  }\n  return AnytoneChannelExtension::APRSPTT::Start;\n}\nvoid\nD878UVCodeplug::ChannelElement::setAnalogAPRSPTTSetting(AnytoneChannelExtension::APRSPTT ptt) {\n  // There is no start/end option. Either send it or not. If enabled -> encode as \"at start\".\n  switch (ptt) {\n  case AnytoneChannelExtension::APRSPTT::Off:   setUInt8(Offset::fmAPRSPTTSetting(), (unsigned int)APRSPTT::Off); break;\n  case AnytoneChannelExtension::APRSPTT::Start: setUInt8(Offset::fmAPRSPTTSetting(), (unsigned int)APRSPTT::Start); break;\n  case AnytoneChannelExtension::APRSPTT::End:   setUInt8(Offset::fmAPRSPTTSetting(), (unsigned int)APRSPTT::End); break;\n  }\n}\n\nAnytoneChannelExtension::APRSPTT\nD878UVCodeplug::ChannelElement::digitalAPRSPTTSetting() const {\n  return getUInt8(Offset::dmrAPRSPTTSetting()) ?\n        AnytoneChannelExtension::APRSPTT::Start :\n        AnytoneChannelExtension::APRSPTT::Off;\n}\nvoid\nD878UVCodeplug::ChannelElement::setDigitalAPRSPTTSetting(AnytoneChannelExtension::APRSPTT ptt) {\n  // There is no start/end option. Either send it or not. If enabled -> encode as \"at start\".\n  switch (ptt) {\n  case AnytoneChannelExtension::APRSPTT::Off:   setUInt8(Offset::dmrAPRSPTTSetting(), 0); break;\n  case AnytoneChannelExtension::APRSPTT::Start:\n  case AnytoneChannelExtension::APRSPTT::End:   setUInt8(Offset::dmrAPRSPTTSetting(), 1); break;\n  }\n}\n\nunsigned\nD878UVCodeplug::ChannelElement::digitalAPRSSystemIndex() const {\n  return getUInt8(Offset::dmrAPRSSystemIndex());\n}\nvoid\nD878UVCodeplug::ChannelElement::setDigitalAPRSSystemIndex(unsigned idx) {\n  setUInt8(Offset::dmrAPRSSystemIndex(), idx);\n}\n\nint\nD878UVCodeplug::ChannelElement::frequenyCorrection() const {\n  return ((int)getInt8(Offset::frequenyCorrection()))*10;\n}\nvoid\nD878UVCodeplug::ChannelElement::setFrequencyCorrection(int corr) {\n  setInt8(Offset::frequenyCorrection(), corr/10);\n}\n\nunsigned int\nD878UVCodeplug::ChannelElement::fmAPRSFrequencyIndex() const {\n  return getUInt8(Offset::fmAPRSFrequencyIndex());\n}\nvoid\nD878UVCodeplug::ChannelElement::setFMAPRSFrequencyIndex(unsigned int idx) {\n  setUInt8(Offset::fmAPRSFrequencyIndex(), std::min(7U, idx));\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::sendTalkerAlias() const {\n  return getBit(Offset::talkerAlias());\n}\n\nvoid\nD878UVCodeplug::ChannelElement::enableSendTalkerAlias(bool enable) {\n  setBit(Offset::talkerAlias(), enable);\n}\n\n\nD878UVCodeplug::ChannelElement::AdvancedEncryptionType\nD878UVCodeplug::ChannelElement::advancedEncryptionType() const {\n  return getBit(Offset::dmrEncryptionType()) ? AdvancedEncryptionType::ARC4 : AdvancedEncryptionType::AES;\n}\n\nvoid\nD878UVCodeplug::ChannelElement::setEncryptionType(AdvancedEncryptionType type) {\n  setBit(Offset::dmrEncryptionType(), AdvancedEncryptionType::ARC4 == type);\n}\n\n\nD878UVCodeplug::ChannelElement::DMREncryptionType\nD878UVCodeplug::ChannelElement::dmrEncryptionType() const {\n  return DMREncryptionType::Basic;\n}\nvoid\nD878UVCodeplug::ChannelElement::setDMREncryptionType(DMREncryptionType type) {\n  Q_UNUSED(type);\n}\nbool\nD878UVCodeplug::ChannelElement::hasDMREncryptionKeyIndex() const {\n  return false;\n}\nunsigned\nD878UVCodeplug::ChannelElement::dmrEncryptionKeyIndex() const {\n  return 0xff;\n}\nvoid\nD878UVCodeplug::ChannelElement::setDMREncryptionKeyIndex(unsigned idx) {\n  Q_UNUSED(idx);\n}\nvoid\nD878UVCodeplug::ChannelElement::clearDMREncryptionKeyIndex() {\n  // pass...\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::adaptiveTDMA() const {\n  // Removed feature\n  return false;\n}\n\nvoid\nD878UVCodeplug::ChannelElement::enableAdaptiveTDMA(bool enable) {\n  Q_UNUSED(enable);\n  // removed feature.\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::hasAESEncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::aesKeyIndex());\n}\nunsigned\nD878UVCodeplug::ChannelElement::aesEncryptionKeyIndex() const {\n  return getUInt8(Offset::aesKeyIndex()) - 1;\n}\nvoid\nD878UVCodeplug::ChannelElement::setAESEncryptionKeyIndex(unsigned idx) {\n  setUInt8(Offset::aesKeyIndex(), idx+1);\n}\nvoid\nD878UVCodeplug::ChannelElement::clearAESEncryptionKeyIndex() {\n  setUInt8(Offset::aesKeyIndex(), 0);\n}\n\n\nbool\nD878UVCodeplug::ChannelElement::hasARC4EncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::arc4KeyIndex());\n}\nunsigned\nD878UVCodeplug::ChannelElement::arc4EncryptionKeyIndex() const {\n  return getUInt8(Offset::arc4KeyIndex()) - 1;\n}\nvoid\nD878UVCodeplug::ChannelElement::setARC4EncryptionKeyIndex(unsigned idx) {\n  setUInt8(Offset::arc4KeyIndex(), idx+1);\n}\nvoid\nD878UVCodeplug::ChannelElement::clearARC4EncryptionKeyIndex() {\n  setUInt8(Offset::arc4KeyIndex(), 0);\n}\n\n\nChannel *\nD878UVCodeplug::ChannelElement::toChannelObj(Context &ctx) const {\n  Channel *ch = D868UVCodeplug::ChannelElement::toChannelObj(ctx);\n\n  if (nullptr == ch)\n    return nullptr;\n\n  // Get extensions\n  AnytoneChannelExtension *ext = nullptr;\n  if (DMRChannel *dch = ch->as<DMRChannel>()) {\n    ext = dch->anytoneChannelExtension();\n  } else if (FMChannel *fch = ch->as<FMChannel>()){\n    ext = fch->anytoneChannelExtension();\n  }\n\n  // If extension is present, update\n  if (nullptr != ext) {\n    ext->setFrequencyCorrection(frequenyCorrection());\n    if (APRSType::FM == txAPRSType())\n      ext->setAPRSPTT(analogAPRSPTTSetting());\n    else if (APRSType::DMR == txAPRSType())\n      ext->setAPRSPTT(digitalAPRSPTTSetting());\n    else\n      ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off);\n  }\n\n  return ch;\n}\n\nbool\nD878UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const {\n  if (! AnytoneCodeplug::ChannelElement::linkChannelObj(c, ctx))\n    return false;\n\n  if (c->is<DMRChannel>()) {\n    DMRChannel *dc = c->as<DMRChannel>();\n    // Link to GPS system\n    if ((APRSType::DMR == txAPRSType()) && ctx.has<DMRAPRSSystem>(digitalAPRSSystemIndex()))\n      dc->setAPRS(ctx.get<DMRAPRSSystem>(digitalAPRSSystemIndex()));\n    // Link APRS system if one is defined\n    //  There can only be one active APRS system, hence the index is fixed to one.\n    if ((APRSType::FM == txAPRSType()) && ctx.has<FMAPRSSystem>(0))\n      dc->setAPRS(ctx.get<FMAPRSSystem>(0));\n\n    // If roaming is not disabled -> link to default roaming zone\n    if (roamingEnabled())\n      dc->setRoaming(DefaultRoamingZone::get());\n\n    if (auto *ext = dc->anytoneChannelExtension()) {\n      // If not default FM APRS frequency\n      if (0 != fmAPRSFrequencyIndex()) {\n        if (ctx.has<AnytoneAPRSFrequency>(fmAPRSFrequencyIndex()))\n          ext->fmAPRSFrequency()->set(ctx.get<AnytoneAPRSFrequency>(fmAPRSFrequencyIndex()));\n      }\n    }\n    bool hasExtension = ctx.config()->settings()->anytoneExtension();\n    bool hasStrongEncryption = hasExtension &&\n        (AnytoneDMRSettingsExtension::EncryptionType::AES ==\n         ctx.config()->settings()->anytoneExtension()->dmrSettings()->encryption());\n\n    if (hasAESEncryptionKeyIndex()) {\n      auto cex = dc->commercialExtension();\n      if (nullptr == cex)\n        dc->setCommercialExtension(cex = new CommercialChannelExtension());\n\n      if (hasStrongEncryption && (AdvancedEncryptionType::AES == advancedEncryptionType())) {\n        if (! ctx.has<AESEncryptionKey>(aesEncryptionKeyIndex())) {\n          logWarn() << \"Cannot link encryption key: no AES key with index \"\n                    << aesEncryptionKeyIndex() << \" defined.\";\n        } else {\n          cex->setEncryptionKey(ctx.get<AESEncryptionKey>(aesEncryptionKeyIndex()));\n        }\n      }\n    } else if (hasARC4EncryptionKeyIndex()) {\n      auto cex = dc->commercialExtension();\n      if (nullptr == cex)\n        dc->setCommercialExtension(cex = new CommercialChannelExtension());\n\n      if (hasStrongEncryption && (AdvancedEncryptionType::ARC4 == advancedEncryptionType())) {\n        if (! ctx.has<ARC4EncryptionKey>(arc4EncryptionKeyIndex())) {\n          logWarn() << \"Cannot link encryption key: no ARC4 key with index \"\n                    << arc4EncryptionKeyIndex() << \" defined.\";\n        } else {\n          cex->setEncryptionKey(ctx.get<ARC4EncryptionKey>(arc4EncryptionKeyIndex()));\n        }\n      }\n    } else if (hasDMREncryptionKeyIndex()) {\n      auto cex = dc->commercialExtension();\n      if (nullptr == cex)\n        dc->setCommercialExtension(cex = new CommercialChannelExtension());\n\n      if ((! hasStrongEncryption) && (D868UVCodeplug::ChannelElement::DMREncryptionType::Basic\n                                      == D868UVCodeplug::ChannelElement::dmrEncryptionType())){\n        if (! ctx.has<BasicEncryptionKey>(dmrEncryptionKeyIndex())) {\n          logWarn() << \"Cannot link encryption key: no basic DMR key with index \"\n                    << dmrEncryptionKeyIndex() << \" defined.\";\n        } else {\n          cex->setEncryptionKey(ctx.get<BasicEncryptionKey>(dmrEncryptionKeyIndex()));\n        }\n      }\n    }\n  } else if (c->is<FMChannel>()) {\n    FMChannel *ac = c->as<FMChannel>();\n    // Link APRS system if one is defined\n    //  There can only be one active APRS system, hence the index is fixed to one.\n    if ((APRSType::FM == txAPRSType()) && ctx.has<FMAPRSSystem>(0))\n      ac->setAPRS(ctx.get<FMAPRSSystem>(0));\n\n    if (auto *ext = ac->anytoneChannelExtension()) {\n      // If not default FM APRS frequency\n      if (0 != fmAPRSFrequencyIndex()) {\n        if (ctx.has<AnytoneAPRSFrequency>(fmAPRSFrequencyIndex()))\n          ext->fmAPRSFrequency()->set(ctx.get<AnytoneAPRSFrequency>(fmAPRSFrequencyIndex()));\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  if (! AnytoneCodeplug::ChannelElement::fromChannelObj(c, ctx))\n    return false;\n\n  AnytoneChannelExtension *ch_ext = nullptr;\n\n  if (const DMRChannel *dc = c->as<DMRChannel>()) {\n    // Set GPS system index\n    setTXAPRSType(APRSType::Off);\n    enableRXAPRS(false);\n    if (dc->aprs() && dc->aprs()->is<DMRAPRSSystem>()) {\n      enableRXAPRS(true);\n      setTXAPRSType(APRSType::DMR);\n      setDigitalAPRSSystemIndex(ctx.index(dc->aprs()->as<DMRAPRSSystem>()));\n    } else if (dc->aprs() && dc->aprs()->is<FMAPRSSystem>()) {\n      setTXAPRSType(APRSType::FM);\n    }\n\n    // Enable roaming\n    if (dc->roamingRef())\n      enableRoaming(true);\n\n    enableDataACK(dc->extended()->dataConfirm());\n    /// Handles bug in AnyTone firmware.\n    /// @todo Remove once fixed by AnyTone.\n    enableRXAPRS(! dc->extended()->sms());\n\n    clearDMREncryptionKeyIndex();\n    clearAESEncryptionKeyIndex();\n    clearARC4EncryptionKeyIndex();\n\n    // By default, we assume we have strong encryption unless otherwise set by AnyTone DMR extension.\n    bool hasStrongEncryption = (! ctx.config()->settings()->anytoneExtension()) ||\n        ( ctx.config()->settings()->anytoneExtension() &&\n          (AnytoneDMRSettingsExtension::EncryptionType::AES ==\n           ctx.config()->settings()->anytoneExtension()->dmrSettings()->encryption()) );\n\n    // Apply commercial extension\n    if (CommercialChannelExtension *cex = dc->commercialExtension()) {\n      if (hasStrongEncryption && cex->encryptionKey() && cex->encryptionKey()->is<AESEncryptionKey>()) {\n        setEncryptionType(AdvancedEncryptionType::AES);\n        setAESEncryptionKeyIndex(ctx.index(cex->encryptionKey()));\n      } else if (hasStrongEncryption && cex->encryptionKey() && cex->encryptionKey()->is<ARC4EncryptionKey>()) {\n        setEncryptionType(AdvancedEncryptionType::ARC4);\n        setARC4EncryptionKeyIndex(ctx.index(cex->encryptionKey()));\n      } else if ((! hasStrongEncryption) && cex->encryptionKey() && cex->encryptionKey()->is<BasicEncryptionKey>()) {\n        D868UVCodeplug::ChannelElement::setDMREncryptionType(\n              D868UVCodeplug::ChannelElement::DMREncryptionType::Basic);\n        setDMREncryptionKeyIndex(ctx.index(cex->encryptionKey()));\n      }\n    }\n  } else if (const FMChannel *ac = c->as<FMChannel>()) {\n    // Set APRS system\n    enableRXAPRS(false);\n    setTXAPRSType(APRSType::Off);\n    if (nullptr != ac->aprs()) {\n      setTXAPRSType(APRSType::FM);\n      if (ac == ac->aprs()->revertChannel()) {\n        enableRXAPRS(true);\n      }\n    }\n    // Apply extension settings\n    if (AnytoneFMChannelExtension *ext = ac->anytoneChannelExtension()) {\n      ch_ext = ext;\n      if (! ext->fmAPRSFrequency()->isNull()) {\n        int idx = ctx.index(ext->fmAPRSFrequency()->as<AnytoneAPRSFrequency>());\n        if ((0 <= idx) && (7 >= idx))\n          setFMAPRSFrequencyIndex(idx);\n        else\n          setFMAPRSFrequencyIndex(0);\n      } else {\n        // Use default\n        setFMAPRSFrequencyIndex(0);\n      }\n    }\n  }\n\n  // Apply common channel extension\n  if (nullptr != ch_ext) {\n    setFrequencyCorrection(ch_ext->frequencyCorrection());\n\n    if (APRSType::DMR == txAPRSType())\n      setDigitalAPRSPTTSetting(ch_ext->aprsPTT());\n    else if (APRSType::FM == txAPRSType())\n      setAnalogAPRSPTTSetting(ch_ext->aprsPTT());\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::ChannelExtensionElement\n * ******************************************************************************************** */\nD878UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  /// pass...\n}\n\n\nvoid\nD878UVCodeplug::ChannelExtensionElement::clear() {\n  Element::clear();\n  memset(_data, 0, size());\n}\n\n\nunsigned int\nD878UVCodeplug::ChannelExtensionElement::bot5ToneIDIndex() const {\n  return getUInt8(Offset::bot5ToneIDIndex());\n}\n\nvoid\nD878UVCodeplug::ChannelExtensionElement::setBOT5ToneIDIndex(unsigned int idx) {\n  setUInt8(Offset::bot5ToneIDIndex(), idx);\n}\n\n\nunsigned int\nD878UVCodeplug::ChannelExtensionElement::eot5ToneIDIndex() const {\n  return getUInt8(Offset::eot5ToneIDIndex());\n}\n\nvoid\nD878UVCodeplug::ChannelExtensionElement::setEOT5ToneIDIndex(unsigned int idx) {\n  setUInt8(Offset::eot5ToneIDIndex(), idx);\n}\n\n\nunsigned int\nD878UVCodeplug::ChannelExtensionElement::txColorCode() const {\n  return getUInt8(Offset::txColorCode());\n}\n\nvoid\nD878UVCodeplug::ChannelExtensionElement::setTXColorCode(unsigned int cc) {\n  setUInt8(Offset::txColorCode(), cc);\n}\n\n\nbool\nD878UVCodeplug::ChannelExtensionElement::updateChannelObj(Channel *c, Context &ctx) const {\n  Q_UNUSED(c); Q_UNUSED(ctx);\n  return true;\n}\n\nbool\nD878UVCodeplug::ChannelExtensionElement::linkChannelObj(Channel *c, Context &ctx) const {\n  Q_UNUSED(c); Q_UNUSED(ctx);\n  return true;\n}\n\nbool\nD878UVCodeplug::ChannelExtensionElement::fromChannelObj(const Channel *c, Context &ctx) {\n  Q_UNUSED(ctx);\n\n  if (c->is<DMRChannel>()) {\n    auto dmr = c->as<DMRChannel>();\n    setTXColorCode(dmr->colorCode());\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::RoamingChannelElement\n * ******************************************************************************************** */\nD878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr)\n  : Element(ptr, RoamingChannelElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::RoamingChannelElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nunsigned\nD878UVCodeplug::RoamingChannelElement::rxFrequency() const {\n  return getBCD8_be(Offset::rxFrequency())*10;\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::setRXFrequency(unsigned hz) {\n  setBCD8_be(Offset::rxFrequency(), hz/10);\n}\nunsigned\nD878UVCodeplug::RoamingChannelElement::txFrequency() const {\n  return getBCD8_be(Offset::txFrequency())*10;\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::setTXFrequency(unsigned hz) {\n  setBCD8_be(Offset::txFrequency(), hz/10);\n}\n\nbool\nD878UVCodeplug::RoamingChannelElement::hasColorCode() const {\n  return ColorCodeValue::Disabled != getUInt8(Offset::colorCode());\n}\nunsigned\nD878UVCodeplug::RoamingChannelElement::colorCode() const {\n  return std::min(15u, (unsigned)getUInt8(Offset::colorCode()));\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::setColorCode(unsigned cc) {\n  setUInt8(Offset::colorCode(), cc);\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::disableColorCode() {\n  setUInt8(Offset::colorCode(), ColorCodeValue::Disabled);\n}\n\nDMRChannel::TimeSlot\nD878UVCodeplug::RoamingChannelElement::timeSlot() const {\n  switch (getUInt8(Offset::timeSlot())) {\n  case TimeSlotValue::TS1: return DMRChannel::TimeSlot::TS1;\n  case TimeSlotValue::TS2: return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), TimeSlotValue::TS1); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), TimeSlotValue::TS2); break;\n  }\n}\n\nQString\nD878UVCodeplug::RoamingChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nD878UVCodeplug::RoamingChannelElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nbool\nD878UVCodeplug::RoamingChannelElement::fromChannel(const RoamingChannel* ch) {\n  setName(ch->name());\n  setRXFrequency(ch->rxFrequency().inHz());\n  setTXFrequency(ch->txFrequency().inHz());\n  if (ch->colorCodeOverridden())\n    setColorCode(ch->colorCode());\n  else\n    disableColorCode();\n  setTimeSlot(ch->timeSlot());\n  return true;\n}\n\nRoamingChannel *\nD878UVCodeplug::RoamingChannelElement::toChannel(Context &ctx) {\n  RoamingChannel *roam = new RoamingChannel();\n  roam->setName(name());\n  roam->setRXFrequency(Frequency::fromHz(rxFrequency()));\n  roam->setTXFrequency(Frequency::fromHz(txFrequency()));\n  if (hasColorCode())\n    roam->setColorCode(colorCode());\n  else\n    roam->overrideColorCode(false);\n  roam->overrideTimeSlot(true);\n  roam->setTimeSlot(timeSlot());\n\n  ctx.config()->roamingChannels()->add(roam);\n\n  return roam;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::RoamingChannelBitmapElement\n * ******************************************************************************************** */\nD878UVCodeplug::RoamingChannelBitmapElement::RoamingChannelBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::RoamingChannelBitmapElement::RoamingChannelBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, RoamingChannelBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::RoamingZoneElement\n * ******************************************************************************************** */\nD878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr)\n  : Element(ptr, RoamingZoneElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::RoamingZoneElement::clear() {\n  memset(_data, 0x00, _size);\n  memset(_data+Offset::members(), 0xff, Limit::numMembers());\n}\n\nbool\nD878UVCodeplug::RoamingZoneElement::hasMember(unsigned n) const {\n  return (0xff != member(n));\n}\nunsigned\nD878UVCodeplug::RoamingZoneElement::member(unsigned n) const {\n  return getUInt8(Offset::members() + n*Offset::betweenMembers());\n}\nvoid\nD878UVCodeplug::RoamingZoneElement::setMember(unsigned n, unsigned idx) {\n  if (n >= Limit::numMembers())\n    return;\n  setUInt8(Offset::members() + n*Offset::betweenMembers(), idx);\n}\nvoid\nD878UVCodeplug::RoamingZoneElement::clearMember(unsigned n) {\n  if (n >= Limit::numMembers())\n    return;\n  setMember(n, 0xff);\n}\n\nQString\nD878UVCodeplug::RoamingZoneElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nD878UVCodeplug::RoamingZoneElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nbool\nD878UVCodeplug::RoamingZoneElement::fromRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack& err)\n{\n  Q_UNUSED(err)\n\n  clear();\n  setName(zone->name());\n  for (unsigned int i=0; i<std::min(Limit::numMembers(), (unsigned int)zone->count()); i++) {\n    setMember(i, ctx.index(zone->channel(i)));\n  }\n  return true;\n}\n\nRoamingZone *\nD878UVCodeplug::RoamingZoneElement::toRoamingZone(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new RoamingZone(name());\n}\n\nbool\nD878UVCodeplug::RoamingZoneElement::linkRoamingZone(RoamingZone *zone, Context &ctx, const ErrorStack &err)\n{\n  for (uint8_t i=0; (i<Limit::numMembers())&&hasMember(i); i++) {\n    if (ctx.has<RoamingChannel>(member(i))) {\n      zone->addChannel(ctx.get<RoamingChannel>(member(i)));\n    } else {\n      errMsg(err) << \"Cannot link roaming zone '\" << zone->name()\n                  << \"': Roaming channel index \" << member(i) << \" is not defined.\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::RoamingZoneBitmapElement\n * ******************************************************************************************** */\nD878UVCodeplug::RoamingZoneBitmapElement::RoamingZoneBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::RoamingZoneBitmapElement::RoamingZoneBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, RoamingZoneBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::GeneralSettingsElement::KeyFunction\n * ******************************************************************************************** */\nuint8_t\nD878UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;\n  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;\n  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;\n  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;\n  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;\n  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;\n  case AnytoneKeySettingsExtension::KeyFunction::VOX:               return (uint8_t)KeyFunction::VOX;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;\n  case AnytoneKeySettingsExtension::KeyFunction::SubPTT:            return (uint8_t)KeyFunction::SubPTT;\n  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;\n  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;\n  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;\n  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;\n  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;\n  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;\n  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;\n  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;\n  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;\n  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;\n  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;\n  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;\n  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;\n  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;\n  case AnytoneKeySettingsExtension::KeyFunction::Roaming:           return (uint8_t)KeyFunction::Roaming;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;\n  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;\n  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch:    return (uint8_t)KeyFunction::APRSType;\n  case AnytoneKeySettingsExtension::KeyFunction::Zone:              return (uint8_t)KeyFunction::Zone;\n  case AnytoneKeySettingsExtension::KeyFunction::RoamingSet:        return (uint8_t)KeyFunction::RoamingSet;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSet:           return (uint8_t)KeyFunction::APRSSet;\n  case AnytoneKeySettingsExtension::KeyFunction::Mute:              return (uint8_t)KeyFunction::Mute;\n  case AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet:       return (uint8_t)KeyFunction::CtcssDcsSet;\n  case AnytoneKeySettingsExtension::KeyFunction::TBSTSend:          return (uint8_t)KeyFunction::TBSTSend;\n  case AnytoneKeySettingsExtension::KeyFunction::Bluetooth:         return (uint8_t)KeyFunction::Bluetooth;\n  case AnytoneKeySettingsExtension::KeyFunction::GPS:               return (uint8_t)KeyFunction::GPS;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelName:       return (uint8_t)KeyFunction::ChannelName;\n  case AnytoneKeySettingsExtension::KeyFunction::CDTScan:           return (uint8_t)KeyFunction::CDTScan;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSend:          return (uint8_t)KeyFunction::APRSSend;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSInfo:          return (uint8_t)KeyFunction::APRSInfo;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSRoaming:        return (uint8_t)KeyFunction::GPSRoaming;\n  case AnytoneKeySettingsExtension::KeyFunction::DIMShut:           return (uint8_t)KeyFunction::DIMShut;\n  case AnytoneKeySettingsExtension::KeyFunction::Squelch:           return (uint8_t)KeyFunction::Squelch;\n  default:                                                          return (uint8_t)KeyFunction::Off;\n  }\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) {\n  switch ((KeyFunctionCode)code) {\n  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;\n  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;\n  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;\n  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;\n  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;\n  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;\n  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;\n  case KeyFunction::VOX:               return AnytoneKeySettingsExtension::KeyFunction::VOX;\n  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;\n  case KeyFunction::SubPTT:            return AnytoneKeySettingsExtension::KeyFunction::SubPTT;\n  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;\n  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;\n  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;\n  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;\n  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;\n  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;\n  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;\n  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;\n  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;\n  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;\n  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;\n  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;\n  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;\n  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;\n  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;\n  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;\n  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;\n  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;\n  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;\n  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;\n  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;\n  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;\n  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;\n  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;\n  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;\n  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;\n  case KeyFunction::Roaming:           return AnytoneKeySettingsExtension::KeyFunction::Roaming;\n  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;\n  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;\n  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;\n  case KeyFunction::APRSType:          return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch;\n  case KeyFunction::Zone:              return AnytoneKeySettingsExtension::KeyFunction::Zone;\n  case KeyFunction::RoamingSet:        return AnytoneKeySettingsExtension::KeyFunction::RoamingSet;\n  case KeyFunction::APRSSet:           return AnytoneKeySettingsExtension::KeyFunction::APRSSet;\n  case KeyFunction::Mute:              return AnytoneKeySettingsExtension::KeyFunction::Mute;\n  case KeyFunction::CtcssDcsSet:       return AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet;\n  case KeyFunction::TBSTSend:          return AnytoneKeySettingsExtension::KeyFunction::TBSTSend;\n  case KeyFunction::Bluetooth:         return AnytoneKeySettingsExtension::KeyFunction::Bluetooth;\n  case KeyFunction::GPS:               return AnytoneKeySettingsExtension::KeyFunction::GPS;\n  case KeyFunction::ChannelName:       return AnytoneKeySettingsExtension::KeyFunction::ChannelName;\n  case KeyFunction::CDTScan:           return AnytoneKeySettingsExtension::KeyFunction::CDTScan;\n  case KeyFunction::APRSSend:          return AnytoneKeySettingsExtension::KeyFunction::APRSSend;\n  case KeyFunction::APRSInfo:          return AnytoneKeySettingsExtension::KeyFunction::APRSInfo;\n  case KeyFunction::GPSRoaming:        return AnytoneKeySettingsExtension::KeyFunction::GPSRoaming;\n  case KeyFunction::DIMShut:           return AnytoneKeySettingsExtension::KeyFunction::DIMShut;\n  case KeyFunction::Squelch:           return AnytoneKeySettingsExtension::KeyFunction::Squelch;\n  default: {\n    logWarn() << \"Unmapped Key Code: 0x\" << QString::number(code, 16);\n    return AnytoneKeySettingsExtension::KeyFunction::Off;\n  }\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::GeneralSettingsElement::TimeZone\n * ******************************************************************************************** */\nQVector<QTimeZone>\nD878UVCodeplug::GeneralSettingsElement::TimeZone::_timeZones = {\n  QTimeZone(-43200), QTimeZone(-41400), QTimeZone(-39600), QTimeZone(-37800),\n  QTimeZone(-36000), QTimeZone(-34200), QTimeZone(-32400), QTimeZone(-30600),\n  QTimeZone(-28800), QTimeZone(-27000), QTimeZone(-25200), QTimeZone(-23400),\n  QTimeZone(-21600), QTimeZone(-19800), QTimeZone(-18000), QTimeZone(-16200),\n  QTimeZone(-14400), QTimeZone(-12600), QTimeZone(-10800), QTimeZone( -9000),\n  QTimeZone( -7200), QTimeZone( -5400), QTimeZone( -3600), QTimeZone( -1800),\n  QTimeZone(     0), QTimeZone(  1800), QTimeZone(  3600), QTimeZone(  5400),\n  QTimeZone(  7200), QTimeZone(  9000), QTimeZone( 10800), QTimeZone( 12600),\n  QTimeZone( 14400), QTimeZone( 16200), QTimeZone( 18000), QTimeZone( 19800),\n  QTimeZone( 21600), QTimeZone( 23400), QTimeZone( 25200), QTimeZone( 27000),\n  QTimeZone( 28800), QTimeZone( 30600), QTimeZone( 32400), QTimeZone( 34200),\n  QTimeZone( 36000), QTimeZone( 37800), QTimeZone( 39600), QTimeZone( 41400),\n  QTimeZone( 43200), QTimeZone( 45000), QTimeZone( 46800)\n};\n\nQTimeZone\nD878UVCodeplug::GeneralSettingsElement::TimeZone::decode(uint8_t code) {\n  if (code >= _timeZones.size())\n    return _timeZones.back();\n  return _timeZones.at(code);\n}\nuint8_t\nD878UVCodeplug::GeneralSettingsElement::TimeZone::encode(const QTimeZone &zone) {\n  if (! _timeZones.contains(zone))\n    return 13; //<- UTC\n  return _timeZones.indexOf(zone);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nD878UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : D868UVCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::GeneralSettingsElement::clear() {\n  AnytoneCodeplug::GeneralSettingsElement::clear();\n}\n\nQTimeZone\nD878UVCodeplug::GeneralSettingsElement::gpsTimeZone() const {\n  return TimeZone::decode(getUInt8(Offset::gpsTimeZone()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setGPSTimeZone(const QTimeZone &zone) {\n  setUInt8(Offset::gpsTimeZone(), TimeZone::encode(zone)); // <- Set to UTC\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::transmitTimeout() const {\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::transmitTimeout())*30);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setTransmitTimeout(const Interval &tot) {\n  setUInt8(Offset::transmitTimeout(), tot.seconds()/30);\n}\n\nAnytoneDisplaySettingsExtension::Language\nD878UVCodeplug::GeneralSettingsElement::language() const {\n  return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) {\n  setUInt8(Offset::language(), (unsigned)lang);\n}\n\nFrequency\nD878UVCodeplug::GeneralSettingsElement::vfoFrequencyStep() const {\n  // Directly map enum to frequency\n  switch (getUInt8(Offset::vfoFrequencyStep())) {\n  case FREQ_STEP_2_5kHz: return Frequency::fromkHz(2.5);\n  case FREQ_STEP_5kHz: return Frequency::fromkHz(5);\n  case FREQ_STEP_6_25kHz: return Frequency::fromkHz(6.25);\n  case FREQ_STEP_8_33kHz: return Frequency::fromkHz(8.33);\n  case FREQ_STEP_10kHz: return Frequency::fromkHz(10);\n  case FREQ_STEP_12_5kHz: return Frequency::fromkHz(12.5);\n  case FREQ_STEP_20kHz: return Frequency::fromkHz(20);\n  case FREQ_STEP_25kHz: return Frequency::fromkHz(25);\n  case FREQ_STEP_50kHz: return Frequency::fromkHz(50);\n  }\n  // otherwise return default step size (2.5kHz)\n  return Frequency::fromkHz(2.5);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setVFOFrequencyStep(Frequency freq) {\n  static auto map = Frequency::MapNearest<FreqStep>(\n        {\n          {Frequency::fromkHz(2.5),  FREQ_STEP_2_5kHz},\n          {Frequency::fromkHz(5),    FREQ_STEP_5kHz},\n          {Frequency::fromkHz(6.25), FREQ_STEP_6_25kHz},\n          {Frequency::fromkHz(8.33), FREQ_STEP_8_33kHz},\n          {Frequency::fromkHz(10),   FREQ_STEP_10kHz},\n          {Frequency::fromkHz(12.5), FREQ_STEP_12_5kHz},\n          {Frequency::fromkHz(20),   FREQ_STEP_20kHz},\n          {Frequency::fromkHz(25),   FREQ_STEP_25kHz},\n          {Frequency::fromkHz(50),   FREQ_STEP_50kHz},\n        });\n\n  setUInt8(Offset::vfoFrequencyStep(), (uint8_t) map.value(freq));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyAShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyBShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyCShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKey1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKey2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyALong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyBLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKeyCLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKey1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nD878UVCodeplug::GeneralSettingsElement::funcKey2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));\n}\n\nAnytoneSettingsExtension::STEType\nD878UVCodeplug::GeneralSettingsElement::steType() const {\n  return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) {\n  setUInt8(Offset::steType(), (unsigned)type);\n}\ndouble\nD878UVCodeplug::GeneralSettingsElement::steFrequency() const {\n  switch ((STEFrequency)getUInt8(Offset::steFrequency())) {\n  case STEFrequency::Off: return 0;\n  case STEFrequency::Hz55_2: return 55.2;\n  case STEFrequency::Hz259_2: return 259.2;\n  }\n  return 0;\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) {\n  if (0 >= freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off);\n  } else if (100 > freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2);\n  } else {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2);\n  }\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::groupCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) {\n  setUInt8(Offset::groupCallHangTime(), intv.seconds());\n}\nInterval\nD878UVCodeplug::GeneralSettingsElement::privateCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) {\n  setUInt8(Offset::privateCallHangTime(), intv.seconds());\n}\nInterval\nD878UVCodeplug::GeneralSettingsElement::preWaveDelay() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preWaveDelay())*20);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setPreWaveDelay(Interval intv) {\n  setUInt8(Offset::preWaveDelay(), intv.milliseconds()/20);\n}\nInterval\nD878UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) {\n  setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20);\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const {\n  return getUInt8(Offset::wfmChannelIndex());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) {\n  setUInt8(Offset::wfmChannelIndex(), idx);\n}\nbool\nD878UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const {\n  return getUInt8(Offset::wfmVFOEnabled());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) {\n  setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00));\n}\n\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::backlightDuration() const {\n  switch ((BacklightDuration)getUInt8(Offset::backlightDuration())) {\n  case BacklightDuration::Infinite: return Interval::infinity();\n  case BacklightDuration::_5s:  return Interval::fromSeconds(5);\n  case BacklightDuration::_10s: return Interval::fromSeconds(10);\n  case BacklightDuration::_15s: return Interval::fromSeconds(15);\n  case BacklightDuration::_20s: return Interval::fromSeconds(20);\n  case BacklightDuration::_25s: return Interval::fromSeconds(25);\n  case BacklightDuration::_30s: return Interval::fromSeconds(30);\n  case BacklightDuration::_1min: return Interval::fromMinutes(1);\n  case BacklightDuration::_2min: return Interval::fromMinutes(2);\n  case BacklightDuration::_3min: return Interval::fromMinutes(3);\n  case BacklightDuration::_4min: return Interval::fromMinutes(4);\n  case BacklightDuration::_5min: return Interval::fromMinutes(5);\n  case BacklightDuration::_15min: return Interval::fromMinutes(15);\n  case BacklightDuration::_30min: return Interval::fromMinutes(30);\n  case BacklightDuration::_45min: return Interval::fromMinutes(45);\n  case BacklightDuration::_1h:    return Interval::fromMinutes(60);\n  }\n  return Interval::infinity();\n}\n\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBacklightDuration(Interval intv) {\n  if (intv <= Interval::fromSeconds(5))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_5s);\n  else if (intv <= Interval::fromSeconds(10))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_10s);\n  else if (intv <= Interval::fromSeconds(15))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_15s);\n  else if (intv <= Interval::fromSeconds(20))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_20s);\n  else if (intv <= Interval::fromSeconds(25))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_25s);\n  else if (intv <= Interval::fromSeconds(30))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_30s);\n  else if (intv <= Interval::fromMinutes(1))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_1min);\n  else if (intv <= Interval::fromMinutes(2))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_2min);\n  else if (intv <= Interval::fromMinutes(3))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_3min);\n  else if (intv <= Interval::fromMinutes(4))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_4min);\n  else if (intv <= Interval::fromMinutes(5))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_5min);\n  else if (intv <= Interval::fromMinutes(15))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_15min);\n  else if (intv <= Interval::fromMinutes(30))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_30min);\n  else if (intv <= Interval::fromMinutes(45))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_45min);\n  else if (intv <= Interval::fromMinutes(60))\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::_1h);\n  else\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::Infinite);\n}\n\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::dtmfToneDuration() const {\n  switch (getUInt8(Offset::dtmfToneDuration())) {\n  case DTMF_DUR_50ms:  return 50;\n  case DTMF_DUR_100ms: return 100;\n  case DTMF_DUR_200ms: return 200;\n  case DTMF_DUR_300ms: return 300;\n  case DTMF_DUR_500ms: return 500;\n  }\n  return 50;\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDTMFToneDuration(unsigned ms) {\n  if (ms<=50) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_50ms);\n  } else if (ms<=100) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_100ms);\n  } else if (ms<=200) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_200ms);\n  } else if (ms<=300) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_300ms);\n  } else {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_500ms);\n  }\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::manDown() const {\n  return getUInt8(Offset::manDown());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableManDown(bool enable) {\n  setUInt8(Offset::manDown(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::wfmMonitor() const {\n  return getUInt8(Offset::wfmMonitor());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) {\n  setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nD878UVCodeplug::GeneralSettingsElement::tbstFrequency() const {\n  switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) {\n  case TBSTFrequency::Hz1000: return Frequency::fromHz(1000);\n  case TBSTFrequency::Hz1450: return Frequency::fromHz(1450);\n  case TBSTFrequency::Hz1750: return Frequency::fromHz(1750);\n  case TBSTFrequency::Hz2100: return Frequency::fromHz(2100);\n  }\n  return Frequency::fromHz(1750);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) {\n  if (1000 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000);\n  } else if (1450 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450);\n  } else if (1750 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  } else if (2100 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100);\n  } else {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  }\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::proMode() const {\n  return getUInt8(Offset::proMode());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableProMode(bool enable) {\n  setUInt8(Offset::proMode(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::filterOwnID() const {\n  return getUInt8(Offset::filterOwnID());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) {\n  setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::remoteStunKill() const {\n  return getUInt8(Offset::remoteStunKill());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) {\n  setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::remoteMonitor() const {\n  return getUInt8(Offset::remoteMonitor());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) {\n  setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneDMRSettingsExtension::SlotMatch\nD878UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const {\n  return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) {\n  setUInt8(Offset::monSlotMatch(), (unsigned)match);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const {\n  return getUInt8(Offset::monColorCodeMatch());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) {\n  setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::monitorIDMatch() const {\n  return getUInt8(Offset::monIDMatch());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) {\n  setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const {\n  return getUInt8(Offset::monTimeSlotHold());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) {\n  setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::manDownDelay() const {\n  return Interval::fromSeconds(getUInt8(Offset::manDownDelay()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setManDownDelay(Interval sec) {\n  setUInt8(Offset::manDownDelay(), sec.seconds());\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::fmCallHold() const {\n  return getUInt8(Offset::fmCallHold());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) {\n  setUInt8(Offset::fmCallHold(), sec);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const {\n  return getUInt8(Offset::enableGPSMessage());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) {\n  setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::maintainCallChannel() const {\n  return getUInt8(Offset::maintainCallChannel());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableMaintainCallChannel(bool enable) {\n  setUInt8(Offset::maintainCallChannel(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const {\n  return getUInt8(Offset::priorityZoneA());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneA(), idx);\n}\nunsigned\nD878UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const {\n  return getUInt8(Offset::priorityZoneB());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneB(), idx);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::bluetooth() const {\n  return 0 != getUInt8(Offset::bluetooth());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableBluetooth(bool enable) {\n  setUInt8(Offset::bluetooth(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::btAndInternalMic() const {\n  return 0 != getUInt8(Offset::btAndInternalMic());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableBTAndInternalMic(bool enable) {\n  setUInt8(Offset::btAndInternalMic(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::btAndInternalSpeaker() const {\n  return 0 != getUInt8(Offset::btAndInternalSpeaker());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableBTAndInternalSpeaker(bool enable) {\n  setUInt8(Offset::btAndInternalSpeaker(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::pluginRecTone() const {\n  return 0 != getUInt8(Offset::pluginRecTone());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enablePluginRecTone(bool enable) {\n  setUInt8(Offset::pluginRecTone(), enable ? 0x01 : 0x00);\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const {\n  return Interval::fromSeconds(getUInt8(Offset::gpsRangingInterval()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) {\n  setUInt8(Offset::gpsRangingInterval(), intv.seconds());\n}\n\nunsigned int\nD878UVCodeplug::GeneralSettingsElement::btMicGain() const {\n  return (getUInt8(Offset::btMicGain())+1)*2;\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBTMicGain(unsigned int gain) {\n  gain = std::min(10U, std::max(2U, gain));\n  setUInt8(Offset::btMicGain(), gain/2-1);\n}\n\nunsigned int\nD878UVCodeplug::GeneralSettingsElement::btSpeakerGain() const {\n  return (getUInt8(Offset::btSpeakerGain())+1)*2;\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBTSpeakerGain(unsigned int gain) {\n  gain = std::min(10U, std::max(2U, gain));\n  setUInt8(Offset::btSpeakerGain(), gain/2-1);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::displayChannelNumber() const {\n  return getUInt8(Offset::showChannelNumber());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableDisplayChannelNumber(bool enable) {\n  setUInt8(Offset::showChannelNumber(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::showCurrentContact() const {\n  return getUInt8(Offset::showCurrentContact());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) {\n  setUInt8(Offset::showCurrentContact(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::autoRoamPeriod() const {\n  return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRoamPeriod(Interval intv) {\n  setUInt8(Offset::autoRoamPeriod(), intv.minutes());\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::keyToneLevelAdjustable() const {\n  return keyToneLevel().isNull();\n}\nLevel\nD878UVCodeplug::GeneralSettingsElement::keyToneLevel() const {\n  return Level::fromValue(getUInt8(Offset::keyToneLevel()), Limit::keyTone());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setKeyToneLevel(Level level) {\n  setUInt8(Offset::keyToneLevel(), level.mapTo(Limit::keyTone()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setKeyToneLevelAdjustable() {\n  setUInt8(Offset::keyToneLevel(), 0);\n}\n\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::GeneralSettingsElement::callDisplayColor() const {\n  return NameColor::decode(getUInt8(Offset::callColor()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::callColor(), NameColor::encode(color));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const {\n  return getUInt8(Offset::gpsUnits());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) {\n  setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::knobLock() const {\n  return getBit(Offset::knobLock(), 0);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {\n  setBit(Offset::knobLock(), 0, enable);\n}\nbool\nD878UVCodeplug::GeneralSettingsElement::keypadLock() const {\n  return getBit(Offset::keypadLock(), 1);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) {\n  setBit(Offset::keypadLock(), 1, enable);\n}\nbool\nD878UVCodeplug::GeneralSettingsElement::sidekeysLock() const {\n  return getBit(Offset::sideKeyLock(), 3);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) {\n  setBit(Offset::sideKeyLock(), 3, enable);\n}\nbool\nD878UVCodeplug::GeneralSettingsElement::keyLockForced() const {\n  return getBit(Offset::forceKeyLock(), 4);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) {\n  setBit(Offset::forceKeyLock(), 4, enable);\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::autoRoamDelay() const {\n  return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRoamDelay(Interval intv) {\n  setUInt8(Offset::autoRoamDelay(), intv.seconds());\n}\n\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::GeneralSettingsElement::standbyTextColor() const {\n  return TextColor::decode(getUInt8(Offset::standbyTextColor()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::standbyTextColor(), TextColor::encode(color));\n}\n\nD878UVCodeplug::GeneralSettingsElement::BackgroundImage\nD878UVCodeplug::GeneralSettingsElement::standbyBackgroundImage() const {\n  return (D878UVCodeplug::GeneralSettingsElement::BackgroundImage)getUInt8(Offset::standbyBackground());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setStandbyBackgroundImage(D878UVCodeplug::GeneralSettingsElement::BackgroundImage img) {\n  setUInt8(Offset::standbyBackground(), (unsigned)img);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::showLastHeard() const {\n  return getUInt8(Offset::showLastHeard());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) {\n  setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00));\n}\n\n\nSMSExtension::Format\nD878UVCodeplug::GeneralSettingsElement::smsFormat() const {\n  switch((SMSFormat) getUInt8(Offset::smsFormat())) {\n  case SMSFormat::Motorola: return SMSExtension::Format::Motorola;\n  case SMSFormat::Hytera: return SMSExtension::Format::Hytera;\n  case SMSFormat::DMR: return SMSExtension::Format::DMR;\n  }\n  return SMSExtension::Format::Motorola;\n}\n\nvoid\nD878UVCodeplug::GeneralSettingsElement::setSMSFormat(SMSExtension::Format fmt) {\n  switch (fmt) {\n    case SMSExtension::Format::Motorola:\n      setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Motorola);\n      break;\n    case SMSExtension::Format::Hytera:\n      setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::Hytera);\n      break;\n    case SMSExtension::Format::DMR:\n      setUInt8(Offset::smsFormat(), (unsigned)SMSFormat::DMR);\n      break;\n  }\n}\n\n\nFrequency\nD878UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10);\n}\nFrequency\nD878UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10);\n}\n\nFrequency\nD878UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10);\n}\nFrequency\nD878UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10);\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nD878UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirB(), (unsigned)dir);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const {\n  return 0 != getUInt8(Offset::fmSendIDAndContact());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) {\n  setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::defaultChannel() const {\n  return getUInt8(Offset::defaultChannels());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) {\n  setUInt8(Offset::defaultChannels(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const {\n  return getUInt8(Offset::defaultZoneA());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned idx) {\n  setUInt8(Offset::defaultZoneA(), idx);\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const {\n  return getUInt8(Offset::defaultZoneB());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned idx) {\n  setUInt8(Offset::defaultZoneB(), idx);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const {\n  return 0xff == defaultChannelAIndex();\n}\nunsigned\nD878UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const {\n  return getUInt8(Offset::defaultChannelA());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelA(), idx);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() {\n  setDefaultChannelAIndex(0xff);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const {\n  return 0xff == defaultChannelBIndex();\n}\nunsigned\nD878UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const {\n  return getUInt8(Offset::defaultChannelB());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned idx) {\n  setUInt8(Offset::defaultChannelB(), idx);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() {\n  setDefaultChannelBIndex(0xff);\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::defaultRoamingZoneIndex() const {\n  return getUInt8(Offset::defaultRoamingZone());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setDefaultRoamingZoneIndex(unsigned idx) {\n  setUInt8(Offset::defaultRoamingZone(), idx);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::repeaterRangeCheck() const {\n  return getUInt8(Offset::repRangeCheck());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableRepeaterRangeCheck(bool enable) {\n  setUInt8(Offset::repRangeCheck(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::repeaterRangeCheckInterval() const {\n  return Interval::fromSeconds(((unsigned)getUInt8(Offset::rangeCheckInterval()))*5);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckInterval(Interval intv) {\n  setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5);\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::repeaterRangeCheckCount() const {\n  return getUInt8(Offset::rangeCheckCount());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setRepeaterRangeCheckCount(unsigned n) {\n  setUInt8(Offset::rangeCheckCount(), n);\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nD878UVCodeplug::GeneralSettingsElement::roamingStartCondition() const {\n  return (AnytoneRoamingSettingsExtension::RoamStart)getUInt8(Offset::roamStartCondition());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) {\n  setUInt8(Offset::roamStartCondition(), (unsigned)cond);\n}\n\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::txBacklightDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::txBacklightDuration()));\n}\n\nvoid\nD878UVCodeplug::GeneralSettingsElement::setTXBacklightDuration(Interval intv) {\n  auto seconds = std::min(30ULL, intv.seconds());\n  setUInt8(Offset::txBacklightDuration(), seconds);\n}\n\n\nbool\nD878UVCodeplug::GeneralSettingsElement::separateDisplay() const {\n  return getUInt8(Offset::displaySeparator());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableSeparateDisplay(bool enable) {\n  setUInt8(Offset::displaySeparator(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::keepLastCaller() const {\n  return getUInt8(Offset::keepLastCaller());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) {\n  setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::GeneralSettingsElement::channelNameColor() const {\n  return NameColor::decode(getUInt8(Offset::channelNameColor()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setChannelNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelNameColor(), NameColor::encode(color));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::repeaterCheckNotification() const {\n  return getUInt8(Offset::repCheckNotify());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableRepeaterCheckNotification(bool enable) {\n  setUInt8(Offset::repCheckNotify(), (enable ? 0x01 : 0x00));\n}\n\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::rxBacklightDuration() const {\n  auto seconds = getUInt8(Offset::rxBacklightDuration());\n  if (0 == seconds)\n    return Interval::infinity();\n  return Interval::fromSeconds(seconds);\n}\n\nvoid\nD878UVCodeplug::GeneralSettingsElement::setRXBacklightDuration(Interval intv) {\n  if (intv.isFinite() || intv > Interval::fromSeconds(30))\n    setUInt8(Offset::rxBacklightDuration(), 0);\n  setUInt8(Offset::rxBacklightDuration(), intv.seconds());\n}\n\n\nbool\nD878UVCodeplug::GeneralSettingsElement::roaming() const {\n  return getUInt8(Offset::roaming());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableRoaming(bool enable) {\n  setUInt8(Offset::roaming(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::muteDelay() const {\n  return Interval::fromMinutes(getUInt8(Offset::muteDelay())+1);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setMuteDelay(Interval min) {\n  setUInt8(Offset::muteDelay(), std::max(1ULL, min.minutes())-1);\n}\n\nunsigned\nD878UVCodeplug::GeneralSettingsElement::repeaterCheckNumNotifications() const {\n  return getUInt8(Offset::repCheckNumNotify())+1;\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setRepeaterCheckNumNotifications(unsigned num) {\n  num = Limit::repeaterOORNotificationCount().limit(num);\n  setUInt8(Offset::repCheckNumNotify(), num-1);\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::bootGPSCheck() const {\n  return getUInt8(Offset::bootGPSCheck());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableBootGPSCheck(bool enable) {\n  setUInt8(Offset::bootGPSCheck(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::bootReset() const {\n  return getUInt8(Offset::bootReset());\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::enableBootReset(bool enable) {\n  setUInt8(Offset::bootReset(), (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::btHoldTimeEnabled() const {\n  return 0x00 != getUInt8(Offset::btHoldTime());\n}\nbool\nD878UVCodeplug::GeneralSettingsElement::btHoldTimeInfinite() const {\n  return 121U == getUInt8(Offset::btHoldTime());\n}\nInterval\nD878UVCodeplug::GeneralSettingsElement::btHoldTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::btHoldTime()));\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBTHoldTime(Interval interval) {\n  unsigned int seconds = std::min(120ULL, std::max(1ULL, interval.seconds()));\n  setUInt8(Offset::btHoldTime(), seconds);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBTHoldTimeInfinite() {\n  setUInt8(Offset::btHoldTime(), 121);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::disableBTHoldTime() {\n  setUInt8(Offset::btHoldTime(), 0);\n}\n\nInterval\nD878UVCodeplug::GeneralSettingsElement::btRXDelay() const {\n  if (0 == getUInt8(Offset::btRXDelay()))\n    return Interval::fromMilliseconds(30);\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::btRXDelay())+1)*500);\n}\nvoid\nD878UVCodeplug::GeneralSettingsElement::setBTRXDelay(Interval delay) {\n  if (500 >= delay.milliseconds()) {\n    setUInt8(Offset::btRXDelay(), 0);\n  } else {\n    unsigned int millis = std::min(5500ULL, std::max(500ULL, delay.milliseconds()));\n    setUInt8(Offset::btRXDelay(), (millis-500)/500);\n  }\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  // Set transmit timeout\n  setTransmitTimeout(ctx.config()->settings()->tot());\n\n  enableBootReset(ctx.config()->settings()->boot()->resetEnabled());\n\n  // Encode tone settings\n  enableIdleChannelTone(\n    ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::DMR));\n  setKeyToneLevel(ctx.config()->settings()->tone()->keyToneVolume());\n\n  enableGPSUnitsImperial(GNSSSettings::Units::Archaic == ctx.config()->settings()->gnss()->units());\n\n  setGroupCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n  setPrivateCallHangTime(ctx.config()->settings()->dmr()->privateCallHangTime());\n\n  setPreWaveDelay(ctx.config()->settings()->dmr()->preamble());\n  setSMSFormat(ctx.config()->smsExtension()->format());\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Encode boot settings\n  if (ext->bootSettings()->priorityZoneA()->isNull())\n    setPriorityZoneAIndex(0xff);\n  else\n    setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as<Zone>()));\n  if (ext->bootSettings()->priorityZoneB()->isNull())\n    setPriorityZoneBIndex(0xff);\n  else\n    setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as<Zone>()));\n  if (! ext->roamingSettings()->defaultZone()->isNull())\n    setDefaultRoamingZoneIndex(ctx.index(ext->roamingSettings()->defaultZone()->as<RoamingZone>()));\n  enableBootGPSCheck(ext->bootSettings()->gpsCheckEnabled());\n\n  // Encode key settings\n  enableKnobLock(ext->keySettings()->knobLockEnabled());\n  enableKeypadLock(ext->keySettings()->keypadLockEnabled());\n  enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled());\n  enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled());\n\n  // Encode audio settings\n  setMuteDelay(ext->audioSettings()->muteDelay());\n\n  // Encode display settings\n  setCallDisplayColor(ext->displaySettings()->callColor());\n  setLanguage(ext->displaySettings()->language());\n  enableDisplayChannelNumber(ext->displaySettings()->showChannelNumberEnabled());\n  enableShowCurrentContact(ext->displaySettings()->showContact());\n  setStandbyTextColor(ext->displaySettings()->standbyTextColor());\n  //setStandbyBackgroundImage(ext->displaySettings()->standbyBackgroundColor());\n  enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled());\n  setChannelNameColor(ext->displaySettings()->channelNameColor());\n  setBacklightDuration(ext->displaySettings()->backlightDuration());\n  setRXBacklightDuration(ext->displaySettings()->backlightDurationRX());\n  setTXBacklightDuration(ext->displaySettings()->backlightDurationTX());\n\n  // Encode menu settings\n  enableSeparateDisplay(ext->menuSettings()->separatorEnabled());\n\n  // Encode auto-repeater settings\n  setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB());\n  setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin());\n  setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax());\n  setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin());\n  setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax());\n\n  // Encode DMR settings\n  setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod());\n  enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled());\n  setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch());\n  enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled());\n  enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled());\n  enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled());\n\n  // Encode GPS settings\n  setGPSTimeZone(ext->gpsSettings()->timeZone());\n  enableGPSMessage(ext->gpsSettings()->positionReportingEnabled());\n  setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod());\n\n  // Encode ranging/roaming settings.\n  setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod());\n  setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay());\n  enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled());\n  setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval());\n  setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount());\n  setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition());\n  enableRepeaterCheckNotification(ext->roamingSettings()->notificationEnabled());\n  setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount());\n\n  // Encode other settings\n  enableKeepLastCaller(ext->keepLastCallerEnabled());\n  setVFOFrequencyStep(ext->vfoStep());\n  setSTEType(ext->steType());\n  setSTEFrequency(ext->steFrequency());\n  setTBSTFrequency(ext->tbstFrequency());\n  enableProMode(ext->proModeEnabled());\n  enableMaintainCallChannel(ext->maintainCallChannelEnabled());\n\n  return true;\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  ctx.config()->settings()->setTOT(transmitTimeout());\n\n  ctx.config()->settings()->boot()->enableReset(this->bootReset());\n\n  // Decode tone settings\n  ctx.config()->settings()->tone()->setKeyToneVolume(keyToneLevel());\n  ctx.config()->settings()->tone()->setChannelIdle(\n    idleChannelTone() ? Channel::Type::DMR : Channel::Type::None\n  );\n\n  ctx.config()->settings()->gnss()->setUnits(\n        this->gpsUnitsImperial() ? GNSSSettings::Units::Archaic :\n                                   GNSSSettings::Units::Metric);\n\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(this->groupCallHangTime());\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(this->privateCallHangTime());\n  ctx.config()->settings()->dmr()->setPreamble(this->preWaveDelay());\n  ctx.config()->smsExtension()->setFormat(this->smsFormat());\n\n\n  // Get or add settings extension\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension()) {\n    ext = ctx.config()->settings()->anytoneExtension();\n  } else {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Decode boot settings\n  ext->bootSettings()->enableGPSCheck(this->bootGPSCheck());\n\n  // Decode key settings\n  ext->keySettings()->enableKnobLock(this->knobLock());\n  ext->keySettings()->enableKeypadLock(this->keypadLock());\n  ext->keySettings()->enableSideKeysLock(this->sidekeysLock());\n  ext->keySettings()->enableForcedKeyLock(this->keyLockForced());\n\n  // Store audio settings\n  ext->audioSettings()->setMuteDelay(this->muteDelay());\n\n  // Decode display settings\n  ext->displaySettings()->setCallColor(this->callDisplayColor());\n  ext->displaySettings()->setLanguage(this->language());\n  ext->displaySettings()->enableShowChannelNumber(this->displayChannelNumber());\n  ext->displaySettings()->enableShowContact(this->showCurrentContact());\n  ext->displaySettings()->setStandbyTextColor(this->standbyTextColor());\n  //ext->displaySettings()->setStandbyBackgroundColor(this->standbyBackgroundImage());\n  ext->displaySettings()->enableShowLastHeard(this->showLastHeard());\n  ext->displaySettings()->setBacklightDurationTX(this->txBacklightDuration());\n  ext->displaySettings()->setChannelNameColor(this->channelNameColor());\n  ext->displaySettings()->setBacklightDuration(this->backlightDuration());\n  ext->displaySettings()->setBacklightDurationTX(this->txBacklightDuration());\n  ext->displaySettings()->setBacklightDurationRX(this->rxBacklightDuration());\n\n  // Decode menu settings\n  ext->menuSettings()->enableSeparator(this->separateDisplay());\n\n  // Decode auto-repeater settings\n  ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB());\n  ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF());\n  ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF());\n  ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF());\n  ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF());\n\n  // Decode dmr settings\n  ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod());\n  ext->dmrSettings()->enableFilterOwnID(this->filterOwnID());\n  ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch());\n  ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch());\n  ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch());\n  ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold());\n\n  // Encode GPS settings\n  ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled());\n  ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod());\n\n  // Encode ranging/roaming settings\n  ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod());\n  ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay());\n  ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheck());\n  ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval());\n  ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount());\n  ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition());\n  ext->roamingSettings()->enableNotification(this->repeaterCheckNotification());\n  ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications());\n\n  // Decode other settings\n  ext->enableKeepLastCaller(this->keepLastCaller());\n  ext->setVFOStep(this->vfoFrequencyStep());\n  ext->setSTEType(this->steType());\n  ext->setSTEFrequency(this->steFrequency());\n  ext->setTBSTFrequency(this->tbstFrequency());\n  ext->enableProMode(this->proMode());\n  ext->enableMaintainCallChannel(this->maintainCallChannel());\n\n  return true;\n}\n\nbool\nD878UVCodeplug::GeneralSettingsElement::linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::GeneralSettingsElement::linkSettings(settings, ctx, err))\n    return false;\n\n  AnytoneSettingsExtension *ext = settings->anytoneExtension();\n\n  if (0xff != priorityZoneAIndex()) {\n    if (! ctx.has<Zone>(priorityZoneAIndex())) {\n      errMsg(err) << \"Cannot link priority zone A index \" << priorityZoneAIndex()\n                  << \": Zone with that index not defined.\";\n      return false;\n    }\n    ext->bootSettings()->priorityZoneA()->set(ctx.get<Zone>(priorityZoneAIndex()));\n  }\n  if (0xff != priorityZoneBIndex()) {\n    if (! ctx.has<Zone>(priorityZoneBIndex())) {\n      errMsg(err) << \"Cannot link priority zone B index \" << priorityZoneBIndex()\n                  << \": Zone with that index not defined.\";\n      return false;\n    }\n    ext->bootSettings()->priorityZoneB()->set(ctx.get<Zone>(priorityZoneBIndex()));\n  }\n\n  if (ctx.has<RoamingZone>(defaultRoamingZoneIndex())) {\n    ext->roamingSettings()->defaultZone()->set(ctx.get<RoamingZone>(this->defaultRoamingZoneIndex()));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::GeneralSettingsExtensionElement\n * ******************************************************************************************** */\nD878UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, ExtendedSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::ExtendedSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n  setEncryption(AnytoneDMRSettingsExtension::EncryptionType::AES);\n  clearAutoRepeaterVHF2OffsetIndex();\n  clearAutoRepeaterUHF2OffsetIndex();\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::sendTalkerAlias() const {\n  return getUInt8(Offset::sendTalkerAlias());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableSendTalkerAlias(bool enable) {\n  setUInt8(Offset::sendTalkerAlias(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneDMRSettingsExtension::TalkerAliasSource\nD878UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const {\n  return (AnytoneDMRSettingsExtension::TalkerAliasSource)getUInt8(Offset::talkerAliasDisplay());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode) {\n  setUInt8(Offset::talkerAliasDisplay(), (unsigned)mode);\n}\n\n\nDMRSettings::TalkerAliasEncoding\nD878UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const {\n  switch ((TalkerAliasEncoding)getUInt8(Offset::talkerAliasEncoding())) {\n  case TalkerAliasEncoding::ISO7: return DMRSettings::TalkerAliasEncoding::Iso7;\n  case TalkerAliasEncoding::ISO8: return DMRSettings::TalkerAliasEncoding::Iso8;\n  case TalkerAliasEncoding::Unicode: return DMRSettings::TalkerAliasEncoding::Unicode;\n  }\n  return DMRSettings::TalkerAliasEncoding::Iso8;\n}\n\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding enc) {\n  switch (enc) {\n  case DMRSettings::TalkerAliasEncoding::Iso7:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO7);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Iso8:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::ISO8);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Unicode:\n    setUInt8(Offset::talkerAliasEncoding(), (unsigned)TalkerAliasEncoding::Unicode);\n    break;\n  }\n}\n\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::bluetoothPTTLatch() const {\n  return getUInt8(Offset::btPTTLatch());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableBluetoothPTTLatch(bool enable) {\n  setUInt8(Offset::btPTTLatch(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::infiniteBluetoothPTTSleepDelay() const {\n  return  bluetoothPTTSleepDelay().isNull();\n}\nInterval\nD878UVCodeplug::ExtendedSettingsElement::bluetoothPTTSleepDelay() const {\n  return Interval::fromMinutes(getUInt8(Offset::btPTTSleepDelay()));\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setBluetoothPTTSleepDelay(Interval delay) {\n  unsigned int t = std::min(Limit::maxBluetoothPTTSleepDelay(), (unsigned int)delay.minutes());\n  setUInt8(Offset::btPTTSleepDelay(), t);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setInfiniteBluetoothPTTSleepDelay() {\n  setBluetoothPTTSleepDelay(Interval::fromMinutes(0));\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::hasAutoRepeaterUHF2OffsetIndex() const {\n  return 0xff != getUInt8(Offset::autoRepeaterUHF2OffsetIndex());\n}\nunsigned\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2OffsetIndex() const {\n  return getUInt8(Offset::autoRepeaterUHF2OffsetIndex());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2OffsetIndex(unsigned idx) {\n  setUInt8(Offset::autoRepeaterUHF2OffsetIndex(), idx);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::clearAutoRepeaterUHF2OffsetIndex() {\n  setUInt8(Offset::autoRepeaterUHF2OffsetIndex(), 0xff);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::hasAutoRepeaterVHF2OffsetIndex() const {\n  return 0xff != getUInt8(Offset::autoRepeaterVHF2OffsetIndex());\n}\nunsigned\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2OffsetIndex() const {\n  return getUInt8(Offset::autoRepeaterVHF2OffsetIndex());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2OffsetIndex(unsigned idx) {\n  setUInt8(Offset::autoRepeaterVHF2OffsetIndex(), idx);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::clearAutoRepeaterVHF2OffsetIndex() {\n  setUInt8(Offset::autoRepeaterVHF2OffsetIndex(), 0xff);\n}\n\nFrequency\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2MinFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterVHF2MinFrequency()))*10);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2MinFrequency(Frequency hz) {\n  setUInt32_le(Offset::autoRepeaterVHF2MinFrequency(), hz.inHz()/10);\n}\nFrequency\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterVHF2MaxFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterVHF2MaxFrequency()))*10);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterVHF2MaxFrequency(Frequency hz) {\n  setUInt32_le(Offset::autoRepeaterVHF2MaxFrequency(), hz.inHz()/10);\n}\nFrequency\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2MinFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterUHF2MinFrequency()))*10);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2MinFrequency(Frequency hz) {\n  setUInt32_le(Offset::autoRepeaterUHF2MinFrequency(), hz.inHz()/10);\n}\nFrequency\nD878UVCodeplug::ExtendedSettingsElement::autoRepeaterUHF2MaxFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getUInt32_le(Offset::autoRepeaterUHF2MaxFrequency()))*10);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAutoRepeaterUHF2MaxFrequency(Frequency hz) {\n  setUInt32_le(Offset::autoRepeaterUHF2MaxFrequency(), hz.inHz()/10);\n}\n\n\nGNSSSettings::Systems\nD878UVCodeplug::ExtendedSettingsElement::gnss() const {\n  switch ((GNSS)getUInt8(Offset::gpsMode())) {\n  case GNSS::GPS:\n    return GNSSSettings::System::GPS;\n  case GNSS::Beidou:\n    return GNSSSettings::System::Beidou;\n  case GNSS::GPS_Beidou:\n    return GNSSSettings::System::GPS | GNSSSettings::System::Beidou;\n  case GNSS::Glonass:\n    return GNSSSettings::System::Glonass;\n  case GNSS::GPS_Glonass:\n    return GNSSSettings::System::GPS | GNSSSettings::System::Glonass;\n  case GNSS::Beidou_Glonass:\n    return GNSSSettings::System::Beidou | GNSSSettings::System::Glonass;\n  case GNSS::GPS_Beidou_Glonass:\n    return GNSSSettings::System::GPS | GNSSSettings::System::Beidou | GNSSSettings::System::Glonass;\n  }\n  return GNSSSettings::System::GPS;\n}\n\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setGNSS(GNSSSettings::Systems mode) {\n  int value = 0;\n  if (mode.testFlags(GNSSSettings::System::GPS))\n    value = (int)GNSS::GPS;\n  if (mode.testFlags(GNSSSettings::System::Beidou))\n    value = (int)GNSS::Beidou;\n  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou))\n    value = (int)GNSS::GPS_Beidou;\n  if (mode.testFlags(GNSSSettings::System::Glonass))\n    value = (int)GNSS::Glonass;\n  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Glonass))\n    value = (int)GNSS::GPS_Glonass;\n  if (mode.testFlags(GNSSSettings::System::Beidou|GNSSSettings::System::Glonass))\n    value = (int)GNSS::Beidou_Glonass;\n  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou|GNSSSettings::System::Glonass))\n    value = (int)GNSS::GPS_Beidou_Glonass;\n  setUInt8(Offset::gpsMode(), value);\n}\n\n\nInterval\nD878UVCodeplug::ExtendedSettingsElement::steDuration() const {\n  return Interval::fromMilliseconds((getUInt8(Offset::steDuration())+1)*10);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setSTEDuration(Interval dur) {\n  unsigned int t = std::max(10U, std::min(1000U, (unsigned int)dur.milliseconds()));\n  setUInt8(Offset::steDuration(), t/10-1);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::infiniteManDialGroupCallHangTime() const {\n  return manDialGroupCallHangTime().isNull();\n}\nInterval\nD878UVCodeplug::ExtendedSettingsElement::manDialGroupCallHangTime() const {\n  unsigned int t = getUInt8(Offset::manGrpCallHangTime())+1;\n  if (t<=30)\n    return Interval::fromSeconds(t);\n  else if (31==t)\n    return Interval::fromMinutes(30);\n  return Interval();\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTime(Interval dur) {\n  unsigned int t = dur.seconds();\n  if (t > 30)\n    t = 31;  // = 30min\n  else if (0 == t)\n    t = 32;  // = infinite\n  setUInt8(Offset::manGrpCallHangTime(), t-1);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setManDialGroupCallHangTimeInfinite() {\n  setManDialGroupCallHangTime(Interval::fromSeconds(0));\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::infiniteManDialPrivateCallHangTime() const {\n  return manDialPrivateCallHangTime().isNull();\n}\nInterval\nD878UVCodeplug::ExtendedSettingsElement::manDialPrivateCallHangTime() const {\n  unsigned int t = getUInt8(Offset::manPrivCallHangTime())+1;\n  if (t<=30)\n    return Interval::fromSeconds(t);\n  else if (31==t)\n    return Interval::fromMinutes(30);\n  return Interval();\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTime(Interval dur) {\n  unsigned int t = dur.seconds();\n  if (t > 30)\n    t = 31;  // = 30min\n  else if (0 == t)\n    t = 32;  // = infinite\n  setUInt8(Offset::manPrivCallHangTime(), t-1);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setManDialPrivateCallHangTimeInfinite() {\n  setManDialPrivateCallHangTime(Interval::fromSeconds(0));\n}\n\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::ExtendedSettingsElement::channelBNameColor() const {\n  return NameColor::decode(getUInt8(Offset::channelBNameColor()));\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelBNameColor(), NameColor::encode(color));\n}\n\nAnytoneDMRSettingsExtension::EncryptionType\nD878UVCodeplug::ExtendedSettingsElement::encryption() const {\n  return (AnytoneDMRSettingsExtension::EncryptionType)getUInt8(Offset::encryptionType());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode) {\n  setUInt8(Offset::encryptionType(), (unsigned int)mode);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::totNotification() const {\n  return 0x00 != getUInt8(Offset::totNotification());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableTOTNotification(bool enable) {\n  setUInt8(Offset::totNotification(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::atpc() const {\n  return 0x00 != getUInt8(Offset::atpc());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableATPC(bool enable) {\n  setUInt8(Offset::atpc(), enable ? 0x01 : 0x00);\n}\n\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::ExtendedSettingsElement::zoneANameColor() const {\n  return NameColor::decode(getUInt8(Offset::zoneANameColor()));\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneANameColor(), NameColor::encode(color));\n}\nAnytoneDisplaySettingsExtension::Color\nD878UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const {\n  return NameColor::decode(getUInt8(Offset::zoneBNameColor()));\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneBNameColor(), NameColor::encode(color));\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::resetAutoShutdownOnCall() const {\n  return 0x00 != getUInt8(Offset::autoShutdownMode());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableResetAutoShutdownOnCall(bool enable) {\n  setUInt8(Offset::autoShutdownMode(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::showColorCode() const {\n  return getBit(Offset::displayColorCode());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableShowColorCode(bool enable) {\n  setBit(Offset::displayColorCode(), enable);\n}\nbool\nD878UVCodeplug::ExtendedSettingsElement::showTimeSlot() const {\n  return getBit(Offset::displayTimeSlot());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableShowTimeSlot(bool enable) {\n  setBit(Offset::displayTimeSlot(), enable);\n}\nbool\nD878UVCodeplug::ExtendedSettingsElement::showChannelType() const {\n  return getBit(Offset::displayChannelType());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableShowChannelType(bool enable) {\n  setBit(Offset::displayChannelType(), enable);\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::fmIdleTone() const {\n  return 0x00 != getUInt8(Offset::fmIdleTone());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableFMIdleTone(bool enable) {\n  setUInt8(Offset::fmIdleTone(), enable ? 0x01 : 0x00);\n}\n\nAnytoneDisplaySettingsExtension::DateFormat\nD878UVCodeplug::ExtendedSettingsElement::dateFormat() const {\n  return (AnytoneDisplaySettingsExtension::DateFormat)getUInt8(Offset::dateFormat());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format) {\n  setUInt8(Offset::dateFormat(), (unsigned int)format);\n}\n\nLevel\nD878UVCodeplug::ExtendedSettingsElement::fmMicGain() const {\n  return Level::fromValue(getUInt8(Offset::analogMicGain()), Limit::micGain());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setFMMicGain(Level gain) {\n  setUInt8(Offset::analogMicGain(), gain.mapTo(Limit::micGain()));\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::gpsRoaming() const {\n  return 0x00 != getUInt8(Offset::gpsRoaming());\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::enableGPSRoaming(bool enable) {\n  setUInt8(Offset::gpsRoaming(), enable ? 0x01 : 0x00);\n}\n\nvoid\nD878UVCodeplug::ExtendedSettingsElement::callEndToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::callEndTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::callEndDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setCallEndToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::callEndTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::callEndDurations()+2*i, tones.at(i).second);\n  }\n}\n\nvoid\nD878UVCodeplug::ExtendedSettingsElement::allCallToneMelody(Melody &melody) const {\n  QVector<QPair<double, unsigned int>> tones; tones.reserve(5);\n  for (int i=0; i<5; i++) {\n    double freq = getUInt16_le(Offset::allCallTones()+2*i);\n    unsigned int duration = getUInt16_le(Offset::allCallDurations()+2*i);\n    if (duration) tones.append({freq, duration});\n  }\n  melody.infer(tones);\n}\nvoid\nD878UVCodeplug::ExtendedSettingsElement::setAllCallToneMelody(const Melody &melody) {\n  unsigned int n=std::min(5U, (unsigned int)melody.count());\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  for (unsigned int i=0; i<n; i++) {\n    setUInt16_le(Offset::allCallTones()+2*i, tones.at(i).first);\n    setUInt16_le(Offset::allCallDurations()+2*i, tones.at(i).second);\n  }\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (! flags.updateCodeplug())\n    this->clear();\n\n  if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  // Encode audio settings\n  if (ctx.config()->settings()->audio()->fmMicGainEnabled())\n    setFMMicGain(ctx.config()->settings()->audio()->fmMicGain());\n  else\n    setFMMicGain(ctx.config()->settings()->audio()->micGain());\n\n  // Encode tone settings.\n  enableFMIdleTone(ctx.config()->settings()->tone()->channelIdle().testFlag(Channel::Type::FM));\n  setCallEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());\n\n  // Encode GPS settings\n  setGNSS(ctx.config()->settings()->gnss()->systems());\n\n  enableSendTalkerAlias(ctx.config()->settings()->dmr()->sendTalkerAliasEnabled());\n  setTalkerAliasEncoding(ctx.config()->settings()->dmr()->talkerAliasEncoding());\n\n  // Get extension\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Encode DMR settings\n  setTalkerAliasSource(ext->dmrSettings()->talkerAliasSource());\n\n  // Some general settings\n  setSTEDuration(ext->steDuration());\n\n  // Power save settings\n  enableATPC(ext->powerSaveSettings()->atpc());\n  enableResetAutoShutdownOnCall(ext->powerSaveSettings()->resetAutoShutdownOnCall());\n\n  // Encode tone settings\n  enableTOTNotification(ext->toneSettings()->totNotification());\n\n  // Encode DMR settings\n  setManDialGroupCallHangTime(ext->dmrSettings()->manualGroupCallHangTime());\n  setManDialPrivateCallHangTime(ext->dmrSettings()->manualPrivateCallHangTime());\n  setEncryption(ext->dmrSettings()->encryption());\n\n  // Encode display settings\n  enableShowColorCode(ext->displaySettings()->showColorCode());\n  enableShowTimeSlot(ext->displaySettings()->showTimeSlot());\n  enableShowChannelType(ext->displaySettings()->showChannelType());\n  setDateFormat(ext->displaySettings()->dateFormat());\n\n  // Encode auto-repeater frequency ranges\n  setAutoRepeaterVHF2MinFrequency(ext->autoRepeaterSettings()->vhf2Min());\n  setAutoRepeaterVHF2MaxFrequency(ext->autoRepeaterSettings()->vhf2Max());\n  setAutoRepeaterUHF2MinFrequency(ext->autoRepeaterSettings()->uhf2Min());\n  setAutoRepeaterUHF2MaxFrequency(ext->autoRepeaterSettings()->uhf2Max());\n\n  // Encode auto-repeater offset indices\n  clearAutoRepeaterVHF2OffsetIndex();\n  if (! ext->autoRepeaterSettings()->vhf2Ref()->isNull()) {\n    setAutoRepeaterVHF2OffsetIndex(\n          ctx.index(\n            ext->autoRepeaterSettings()->vhf2Ref()->as<AnytoneAutoRepeaterOffset>()));\n  }\n  clearAutoRepeaterUHF2OffsetIndex();\n  if (! ext->autoRepeaterSettings()->uhf2Ref()->isNull()) {\n    setAutoRepeaterUHF2OffsetIndex(\n          ctx.index(\n            ext->autoRepeaterSettings()->uhf2Ref()->as<AnytoneAutoRepeaterOffset>()));\n  }\n\n  // Encode roaming settings\n  enableGPSRoaming(ext->roamingSettings()->gpsRoaming());\n\n  // Encode bluetooth settings\n  enableBluetoothPTTLatch(ext->bluetoothSettings()->pttLatch());\n  setBluetoothPTTSleepDelay(ext->bluetoothSettings()->pttSleepTimer());\n\n  return true;\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  // Store GPS settings\n  ctx.config()->settings()->gnss()->setSystems(this->gnss());\n\n  // Store tone settings\n  ctx.config()->settings()->tone()->setChannelIdle(\n    ctx.config()->settings()->tone()->channelIdle()\n      | (fmIdleTone() ? Channel::Type::FM : Channel::Type::None));\n  callEndToneMelody(*ctx.config()->settings()->tone()->callEndMelody());\n\n  // Store DMR settings\n  ctx.config()->settings()->dmr()->enableSendTalkerAlias(sendTalkerAlias());\n  ctx.config()->settings()->dmr()->setTalkerAliasEncoding(talkerAliasEncoding());\n\n  // Get or add extension if not present\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Store DMR settings\n  ext->dmrSettings()->setTalkerAliasSource(talkerAliasSource());\n\n  // Some general settings\n  ext->setSTEDuration(this->steDuration());\n\n  // Some power-save settings\n  ext->powerSaveSettings()->enableATPC(this->atpc());\n  ext->powerSaveSettings()->enableResetAutoShutdownOnCall(this->resetAutoShutdownOnCall());\n\n  // Store tone settings\n  ext->toneSettings()->enableTOTNotification(this->totNotification());\n\n  // Store display settings\n  ext->displaySettings()->enableShowColorCode(this->showColorCode());\n  ext->displaySettings()->enableShowTimeSlot(this->showTimeSlot());\n  ext->displaySettings()->enableShowChannelType(this->showChannelType());\n  ext->displaySettings()->setDateFormat(this->dateFormat());\n\n  // Store some DMR settings\n  ext->dmrSettings()->setManualGroupCallHangTime(this->manDialGroupCallHangTime());\n  ext->dmrSettings()->setManualPrivateCallHangTime(this->manDialPrivateCallHangTime());\n  ext->dmrSettings()->setEncryption(this->encryption());\n\n  // Store auto-repeater frequency ranges\n  ext->autoRepeaterSettings()->setVHF2Min(this->autoRepeaterVHF2MinFrequency());\n  ext->autoRepeaterSettings()->setVHF2Max(this->autoRepeaterVHF2MaxFrequency());\n  ext->autoRepeaterSettings()->setUHF2Min(this->autoRepeaterUHF2MinFrequency());\n  ext->autoRepeaterSettings()->setUHF2Max(this->autoRepeaterUHF2MaxFrequency());\n\n  // Store roaming settings\n  ext->roamingSettings()->enableGPSRoaming(this->gpsRoaming());\n\n  // Store bluetooth settings\n  ext->bluetoothSettings()->enablePTTLatch(this->bluetoothPTTLatch());\n  ext->bluetoothSettings()->setPTTSleepTimer(this->bluetoothPTTSleepDelay());\n\n  return true;\n}\n\nbool\nD878UVCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::ExtendedSettingsElement::linkConfig(ctx, err))\n    return false;\n\n  // Store FM mic gain separately, if different\n  if (ctx.config()->settings()->audio()->micGain() == fmMicGain())\n    ctx.config()->settings()->audio()->disableFMMicGain();\n  else\n    ctx.config()->settings()->audio()->setFMMicGain(fmMicGain());\n\n  // Get or add extension if not present\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    errMsg(err) << \"Cannot link config extension: not set.\";\n    return false;\n  }\n\n  if (hasAutoRepeaterVHF2OffsetIndex()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterVHF2OffsetIndex())) {\n      errMsg(err) << \"Cannot link AnyTone settings extension: \"\n                     \"second auto-repeater VHF offset index \"\n                  << this->autoRepeaterVHF2OffsetIndex() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->vhf2Ref()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(\n            this->autoRepeaterVHF2OffsetIndex()));\n  }\n\n  if (hasAutoRepeaterUHF2OffsetIndex()) {\n    if (! ctx.has<AnytoneAutoRepeaterOffset>(this->autoRepeaterUHF2OffsetIndex())) {\n      errMsg(err) << \"Cannot link AnyTone settings extension: \"\n                     \"second auto-repeater UHF offset index \"\n                  << this->autoRepeaterUHF2OffsetIndex() << \" not defined.\";\n      return false;\n    }\n    ext->autoRepeaterSettings()->uhf2Ref()->set(\n          ctx.get<AnytoneAutoRepeaterOffset>(\n            this->autoRepeaterUHF2OffsetIndex()));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::AnalogAPRSSettingsElement\n * ******************************************************************************************** */\nD878UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr)\n  : Element(ptr, APRSSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::APRSSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n  setUInt8(0x0000, 0xff);\n  setFMTXDelay(Interval::fromMilliseconds(60));\n  setUInt8(0x003d, 0x01); setUInt8(0x003e, 0x03); setUInt8(0x003f, 0xff);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::isValid() const {\n  if (! Codeplug::Element::isValid())\n    return false;\n  return (! destination().simplified().isEmpty())\n      && (! source().simplified().isEmpty());\n}\n\nInterval D878UVCodeplug::APRSSettingsElement::fmTXDelay() const {\n  return Interval::fromMilliseconds( ((unsigned)getUInt8(Offset::fmTXDelay())) * 20);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setFMTXDelay(Interval ms) {\n  setUInt8(Offset::fmTXDelay(), ms.milliseconds()/20);\n}\n\nSelectiveCall\nD878UVCodeplug::APRSSettingsElement::txTone() const {\n  if ((uint8_t)SignalingType::Off ==getUInt8(Offset::fmSigType())) { // none\n    return SelectiveCall();\n  } else if ((uint8_t)SignalingType::CTCSS == getUInt8(Offset::fmSigType())) { // CTCSS\n    return CTCSS::decode(getUInt8(Offset::fmCTCSS()));\n  } else if ((uint8_t)SignalingType::DCS == getUInt8(Offset::fmSigType())) { // DCS\n    uint16_t code = getUInt16_le(Offset::fmDCS());\n    if (512 < code)\n      return SelectiveCall::fromBinaryDCS(code, false);\n    return SelectiveCall::fromBinaryDCS(code-512, true);\n  }\n\n  return SelectiveCall();\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setTXTone(const SelectiveCall &code) {\n  if (code.isInvalid()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::Off);\n  } else if (code.isCTCSS()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::CTCSS);\n    setUInt8(Offset::fmCTCSS(), CTCSS::encode(code));\n  } else if (code.isDCS()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::DCS);\n    setUInt16_le(Offset::fmDCS(), code.binCode() + (code.isInverted() ? 512 : 0));\n  }\n}\n\nInterval\nD878UVCodeplug::APRSSettingsElement::manualTXInterval() const {\n  return Interval::fromSeconds(getUInt8(Offset::manualTXInterval()));\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setManualTXInterval(Interval sec) {\n  setUInt8(Offset::manualTXInterval(), sec.seconds());\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::autoTX() const {\n  return ! autoTXInterval().isNull();\n}\nInterval\nD878UVCodeplug::APRSSettingsElement::autoTXInterval() const {\n  return Interval::fromSeconds( ((unsigned)getUInt8(Offset::autoTXInterval()))*30 );\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setAutoTXInterval(Interval sec) {\n  setUInt8(Offset::autoTXInterval(), sec.seconds()/30);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::disableAutoTX() {\n  setAutoTXInterval(Interval::fromMilliseconds(0));\n}\n\n\nbool\nD878UVCodeplug::APRSSettingsElement::fixedLocationEnabled() const {\n  return getUInt8(Offset::fixedLocation());\n}\n\nQGeoCoordinate\nD878UVCodeplug::APRSSettingsElement::fixedLocation() const {\n  double latitude  = getUInt8(Offset::fixedLatDeg()) + double(getUInt8(Offset::fixedLatMin()))/60\n      + double(getUInt8(Offset::fixedLatSec()))/3600;\n  if (getUInt8(Offset::fixedLatSouth()))\n    latitude *= -1;\n\n  double longitude = getUInt8(Offset::fixedLonDeg()) + double(getUInt8(Offset::fixedLonMin()))/60\n      + double(getUInt8(Offset::fixedLonSec()))/3600;\n  if (getUInt8(Offset::fixedLonWest()))\n    longitude *= -1;\n\n  double height_ft = getUInt16_le(Offset::fixedHeight());\n  return QGeoCoordinate(latitude, longitude, height_ft/3.281);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setFixedLocation(const QGeoCoordinate &loc) {\n  double latitude = loc.latitude();\n  bool south = (0 > latitude); latitude = std::abs(latitude);\n  unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60;\n  unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60;\n  unsigned lat_sec = int(latitude);\n  double longitude = loc.longitude();\n  bool west = (0 > longitude); longitude = std::abs(longitude);\n  unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60;\n  unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60;\n  unsigned lon_sec = int(longitude);\n  unsigned height_ft = 0;\n  if (QGeoCoordinate::Coordinate3D == loc.type())\n    height_ft = int(loc.altitude()*3.281);\n  setUInt8(Offset::fixedLatDeg(), lat_deg);\n  setUInt8(Offset::fixedLatMin(), lat_min);\n  setUInt8(Offset::fixedLatSec(), lat_sec);\n  setUInt8(Offset::fixedLatSouth(), (south ? 0x01 : 0x00));\n  setUInt8(Offset::fixedLonDeg(), lon_deg);\n  setUInt8(Offset::fixedLonMin(), lon_min);\n  setUInt8(Offset::fixedLonSec(), lon_sec);\n  setUInt8(Offset::fixedLonWest(), (west ? 0x01 : 0x00));\n  setUInt16_le(Offset::fixedHeight(), height_ft);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableFixedLocation(bool enable) {\n  setUInt8(Offset::fixedLocation(), enable ? 0x01 : 0x00);\n}\n\nQString\nD878UVCodeplug::APRSSettingsElement::destination() const {\n  // Terminated/padded with space\n  return readASCII(Offset::destinationCall(), 6, ' ');\n}\nunsigned\nD878UVCodeplug::APRSSettingsElement::destinationSSID() const {\n  return getUInt8(Offset::destinationSSID());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDestination(const QString &call, unsigned ssid) {\n  // Terminated/padded with space\n  writeASCII(Offset::destinationCall(), call, 6, ' ');\n  setUInt8(Offset::destinationSSID(), ssid);\n}\nQString\nD878UVCodeplug::APRSSettingsElement::source() const {\n  // Terminated/padded with space\n  return readASCII(Offset::sourceCall(), 6, ' ');\n}\nunsigned\nD878UVCodeplug::APRSSettingsElement::sourceSSID() const {\n  return getUInt8(Offset::sourceSSID());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setSource(const QString &call, unsigned ssid) {\n  // Terminated/padded with space\n  writeASCII(Offset::sourceCall(), call, 6, ' ');\n  setUInt8(Offset::sourceSSID(), ssid);\n}\n\nQString\nD878UVCodeplug::APRSSettingsElement::path() const {\n  return readASCII(Offset::path(), 20, 0x00);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setPath(const QString &path) {\n  writeASCII(Offset::path(), path, 20, 0x00);\n}\n\nFMAPRSSystem::Icon\nD878UVCodeplug::APRSSettingsElement::icon() const {\n  return code2aprsicon(getUInt8(Offset::symbolTable()), getUInt8(Offset::symbol()));\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setIcon(FMAPRSSystem::Icon icon) {\n  setUInt8(Offset::symbolTable(), aprsicon2tablecode(icon));\n  setUInt8(Offset::symbol(), aprsicon2iconcode(icon));\n}\n\nChannel::Power\nD878UVCodeplug::APRSSettingsElement::power() const {\n  switch (getUInt8(Offset::fmPower())) {\n  case 0: return Channel::Power::Low;\n  case 1: return Channel::Power::Mid;\n  case 2: return Channel::Power::High;\n  case 3: return Channel::Power::Max;\n  }\n  return Channel::Power::Low;\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:  setUInt8(Offset::fmPower(), 0x00); break;\n  case Channel::Power::Mid:  setUInt8(Offset::fmPower(), 0x01); break;\n  case Channel::Power::High: setUInt8(Offset::fmPower(), 0x02); break;\n  case Channel::Power::Max:  setUInt8(Offset::fmPower(), 0x03); break;\n  }\n}\n\nInterval\nD878UVCodeplug::APRSSettingsElement::fmPreWaveDelay() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmPrewaveDelay()))*10);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setFMPreWaveDelay(Interval ms) {\n  setUInt8(Offset::fmPrewaveDelay(), ms.milliseconds()/10);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::dmrChannelIsSelected(unsigned n) const {\n  return 0xfa2 == dmrChannelIndex(n);\n}\nunsigned\nD878UVCodeplug::APRSSettingsElement::dmrChannelIndex(unsigned n) const {\n  return getUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRChannelIndex(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices(), idx);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRChannelSelected(unsigned n) {\n  setDMRChannelIndex(n, 0xfa2);\n}\n\nunsigned\nD878UVCodeplug::APRSSettingsElement::dmrDestination(unsigned n) const {\n  return getBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRDestination(unsigned n, unsigned idx) {\n  setBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations(), idx);\n}\n\nDMRContact::Type\nD878UVCodeplug::APRSSettingsElement::dmrCallType(unsigned n) const {\n  switch(getUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes())) {\n  case 0: return DMRContact::PrivateCall;\n  case 1: return DMRContact::GroupCall;\n  case 2: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRCallType(unsigned n, DMRContact::Type type) {\n  switch(type) {\n  case DMRContact::PrivateCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x00); break;\n  case DMRContact::GroupCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x01); break;\n  case DMRContact::AllCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x02); break;\n  }\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::dmrTimeSlotOverride(unsigned n) {\n  return 0x00 != getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots());\n}\nDMRChannel::TimeSlot\nD878UVCodeplug::APRSSettingsElement::dmrTimeSlot(unsigned n) const {\n  switch (getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots())) {\n  case 1: return DMRChannel::TimeSlot::TS1;\n  case 2: return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x01); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x02); break;\n  }\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::clearDMRTimeSlotOverride(unsigned n) {\n  setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::dmrRoaming() const {\n  return getUInt8(Offset::roamingSupport());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableDMRRoaming(bool enable) {\n  setUInt8(Offset::roamingSupport(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nD878UVCodeplug::APRSSettingsElement::dmrPreWaveDelay() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::dmrPrewaveDelay()))*100);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDMRPreWaveDelay(Interval ms) {\n  setUInt8(Offset::dmrPrewaveDelay(), ms.milliseconds()/100);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::infiniteDisplayTime() const {\n  return 13 == getUInt8(Offset::displayInterval());\n}\nInterval\nD878UVCodeplug::APRSSettingsElement::displayTime() const {\n  return Interval::fromSeconds(3 + getUInt8(Offset::displayInterval()));\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDisplayTime(Interval dur) {\n  unsigned int sec = dur.seconds();\n  sec = std::min(16U, std::max(3U, sec));\n  setUInt8(Offset::displayInterval(), sec-3);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setDisplayTimeInifinite() {\n  setUInt8(Offset::displayInterval(), 13);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportPosition() const {\n  return getBit(Offset::reportPosition(), 0);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportPosition(bool enable) {\n  setBit(Offset::reportPosition(), 0, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportMicE() const {\n  return getBit(Offset::reportMicE(), 1);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportMicE(bool enable) {\n  setBit(Offset::reportMicE(), 1, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportObject() const {\n  return getBit(Offset::reportObject(), 2);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportObject(bool enable) {\n  setBit(Offset::reportObject(), 2, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportItem() const {\n  return getBit(Offset::reportItem(), 3);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportItem(bool enable) {\n  setBit(Offset::reportItem(), 3, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportMessage() const {\n  return getBit(Offset::reportMessage(), 4);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportMessage(bool enable) {\n  setBit(Offset::reportMessage(), 4, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportWeather() const {\n  return getBit(Offset::reportWeather(), 5);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportWeather(bool enable) {\n  setBit(Offset::reportWeather(), 5, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportNMEA() const {\n  return getBit(Offset::reportNMEA(), 6);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportNMEA(bool enable) {\n  setBit(Offset::reportNMEA(), 6, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportStatus() const {\n  return getBit(Offset::reportStatus(), 7);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportStatus(bool enable) {\n  setBit(Offset::reportStatus(), 7, enable);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::reportOther() const {\n  return getBit(Offset::reportOther(), 0);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableReportOther(bool enable) {\n  setBit(Offset::reportOther(), 0, enable);\n}\n\nAnytoneFMAPRSSettingsExtension::Bandwidth\nD878UVCodeplug::APRSSettingsElement::fmChannelWidth() const {\n  return (AnytoneFMAPRSSettingsExtension::Bandwidth)getUInt8(Offset::fmWidth());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setFMChannelWidth(AnytoneFMAPRSSettingsExtension::Bandwidth width) {\n  setUInt8(Offset::fmWidth(), (uint8_t)width);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::fmPassAll() const {\n  return 0x00 != getUInt8(Offset::passAll());\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::enableFMPassAll(bool enable) {\n  setUInt8(Offset::passAll(), enable ? 0x01 : 0x00);\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::fmFrequencySet(unsigned int n) const {\n  n = std::min(Limit::fmFrequencies()-1, n);\n  return 0 != getBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies());\n}\nFrequency\nD878UVCodeplug::APRSSettingsElement::fmFrequency(unsigned int n) const {\n  n = std::min(Limit::fmFrequencies()-1, n);\n  return Frequency::fromHz(getBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies())*10);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::setFMFrequency(unsigned int n, Frequency f) {\n  n = std::min(Limit::fmFrequencies()-1, n);\n  setBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies(), f.inHz()/10);\n}\nvoid\nD878UVCodeplug::APRSSettingsElement::clearFMFrequency(unsigned int n) {\n  n = std::min(Limit::fmFrequencies()-1, n);\n  setBCD8_be(Offset::fmFrequencies() + n*Offset::betweenFMFrequencies(), 0);\n}\n\n\nbool\nD878UVCodeplug::APRSSettingsElement::fromConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Encode fixed location if valid\n  if (ctx.config()->settings()->gnss()->fixedPosition().isValid()) {\n    setFixedLocation(ctx.config()->settings()->gnss()->fixedPosition());\n    // Enable if there are no GNSS enabled\n    enableFixedLocation(ctx.config()->settings()->gnss()->fixedPositionEnabled());\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (fixedLocation().isValid()) {\n    ctx.config()->settings()->gnss()->setFixedPosition(fixedLocation());\n    ctx.config()->settings()->gnss()->enableFixedPosition(fixedLocationEnabled());\n  }\n\n  return true;\n}\n\n\nbool\nD878UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(\n    const FMAPRSSystem *sys, Context &ctx, const ErrorStack &err)\n{\n  Q_UNUSED(ctx)\n  clear();\n  if (! sys->hasRevertChannel()) {\n    errMsg(err) << \"Cannot encode APRS settings: \"\n                << \"No revert channel defined for APRS system '\" << sys->name() <<\"'.\";\n    return false;\n  }\n  setFMFrequency(0, sys->revertChannel()->txFrequency());\n  setTXTone(sys->revertChannel()->txTone());\n  setPower(sys->revertChannel()->power());\n  setFMChannelWidth(FMChannel::Bandwidth::Wide == sys->revertChannel()->bandwidth() ?\n                      AnytoneFMAPRSSettingsExtension::Bandwidth::Wide :\n                      AnytoneFMAPRSSettingsExtension::Bandwidth::Narrow);\n  setManualTXInterval(sys->period());\n  setAutoTXInterval(sys->period());\n  setDestination(sys->destination(), sys->destSSID());\n  setSource(sys->source(), sys->srcSSID());\n  setPath(sys->path());\n  setIcon(sys->icon());\n  setFMPreWaveDelay(Interval());\n\n  // Handle FM APRS Settings if set\n  AnytoneFMAPRSSettingsExtension *ext = sys->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  setFMTXDelay(ext->txDelay());\n  setFMPreWaveDelay(ext->preWaveDelay());\n  enableFMPassAll(ext->passAll());\n  enableReportPosition(ext->reportPosition());\n  enableReportMicE(ext->reportMicE());\n  enableReportObject(ext->reportObject());\n  enableReportItem(ext->reportItem());\n  enableReportMessage(ext->reportMessage());\n  enableReportWeather(ext->reportWeather());\n  enableReportNMEA(ext->reportNMEA());\n  enableReportStatus(ext->reportStatus());\n  enableReportOther(ext->reportOther());\n\n  // Encode additional FM APRS frequencies\n  for (int i=0; i<ext->frequencies()->count(); i++) {\n    setFMFrequency(ctx.index(ext->frequencies()->get(i)),\n                   ext->frequencies()->get(i)->as<AnytoneAPRSFrequency>()->frequency());\n  }\n\n  return true;\n}\n\nFMAPRSSystem *\nD878UVCodeplug::APRSSettingsElement::toFMAPRSSystem(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  QString name = QString(\"APRS %1\").arg(destination());\n  FMAPRSSystem *sys = new FMAPRSSystem(\n        name, nullptr,\n        destination(), destinationSSID(), source(), sourceSSID(),\n        path(), icon(), \"\", autoTXInterval());\n\n  // Decode extension\n  AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension();\n  sys->setAnytoneExtension(ext);\n\n  ext->setTXDelay(fmTXDelay());\n  ext->setPreWaveDelay(fmPreWaveDelay());\n  ext->enablePassAll(fmPassAll());\n\n  ext->enableReportPosition(reportPosition());\n  ext->enableReportMicE(reportMicE());\n  ext->enableReportObject(reportObject());\n  ext->enableReportItem(reportItem());\n  ext->enableReportMessage(reportMessage());\n  ext->enableReportWeather(reportWeather());\n  ext->enableReportNMEA(reportNMEA());\n  ext->enableReportStatus(reportStatus());\n  ext->enableReportOther(reportOther());\n\n  for (unsigned int i=1; i<Limit::fmFrequencies(); i++) {\n    if (! fmFrequencySet(i))\n      continue;\n    auto *f = new AnytoneAPRSFrequency();\n    f->setFrequency(fmFrequency(i));\n    f->setName(QString(\"APRS %1\").arg(i));\n    ext->frequencies()->add(f);\n    ctx.add(f, i);\n  }\n\n  return sys;\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::linkFMAPRSSystem(FMAPRSSystem *sys, Context &ctx) {\n  // First, try to find a matching analog channel in list\n  FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(fmFrequency(0));\n  if (! ch) {\n    // If no channel is found, create one with the settings from APRS channel:\n    ch = new FMChannel();\n    ch->setName(\"APRS Channel\");\n    ch->setRXFrequency(fmFrequency(0));\n    ch->setTXFrequency(fmFrequency(0));\n    ch->setPower(power());\n    ch->setTXTone(txTone());\n    ch->setBandwidth(AnytoneFMAPRSSettingsExtension::Bandwidth::Wide == fmChannelWidth() ?\n                       FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow);\n    logInfo() << \"No matching APRS channel found for TX frequency \" << double(fmFrequency(0).inHz())/1e6\n              << \"MHz, create one as 'APRS Channel'\";\n    ctx.config()->channelList()->add(ch);\n  }\n  sys->setRevertChannel(ch);\n  return true;\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::fromDMRAPRSSystems(Context &ctx) {\n  unsigned int n = std::min(ctx.count<DMRAPRSSystem>(), Limit::dmrSystems());\n  for (unsigned int idx=0; idx<n; idx++)\n    fromDMRAPRSSystemObj(idx, ctx.get<DMRAPRSSystem>(idx), ctx);\n  return true;\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::fromDMRAPRSSystemObj(unsigned int idx, DMRAPRSSystem *sys, Context &ctx) {\n  if (sys->hasContact()) {\n    setDMRDestination(idx, sys->contact()->number());\n    setDMRCallType(idx, sys->contact()->type());\n  }\n  if (sys->hasRevertChannel()) {\n    setDMRChannelIndex(idx, ctx.index(sys->revertChannel()));\n    clearDMRTimeSlotOverride(idx);\n  } else { // no revert channel specified or \"selected channel\":\n    setDMRChannelSelected(idx);\n  }\n  return true;\n}\n\nDMRAPRSSystem *\nD878UVCodeplug::APRSSettingsElement::toDMRAPRSSystemObj(int idx) const {\n  if (0 == dmrDestination(idx))\n    return nullptr;\n  return new DMRAPRSSystem(tr(\"GPS Sys #%1\").arg(idx+1));\n}\n\nbool\nD878UVCodeplug::APRSSettingsElement::linkDMRAPRSSystem(int idx, DMRAPRSSystem *sys, Context &ctx) const {\n  // if a revert channel is defined -> link to it\n  if (dmrChannelIsSelected(idx))\n    sys->resetRevertChannel();\n  else if (ctx.has<Channel>(dmrChannelIndex(idx)) && ctx.get<Channel>(dmrChannelIndex(idx))->is<DMRChannel>())\n    sys->setRevertChannel(ctx.get<Channel>(dmrChannelIndex(idx))->as<DMRChannel>());\n\n  // Search for a matching contact in contacts\n  DMRContact *cont = nullptr;\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    if (ctx.get<DMRContact>(i)->number() == dmrDestination(idx)) {\n      cont = ctx.get<DMRContact>(i);\n      break;\n    }\n  }\n  // If no matching contact is found, create one\n  if (nullptr == cont) {\n    cont = new DMRContact(dmrCallType(idx), tr(\"GPS #%1 Contact\").arg(idx+1),\n                          dmrDestination(idx), false);\n    ctx.config()->contacts()->add(cont);\n  }\n  // link contact to GPS system.\n  sys->setContact(cont);\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::AnalogAPRSMessageElement\n * ******************************************************************************************** */\nD878UVCodeplug::AnalogAPRSMessageElement::AnalogAPRSMessageElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::AnalogAPRSMessageElement::AnalogAPRSMessageElement(uint8_t *ptr)\n  : Element(ptr, AnalogAPRSMessageElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::AnalogAPRSMessageElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nQString\nD878UVCodeplug::AnalogAPRSMessageElement::message() const {\n  return readASCII(0, Limit::length(), 0x00);\n}\n\nvoid\nD878UVCodeplug::AnalogAPRSMessageElement::setMessage(const QString &msg) {\n  writeASCII(0, msg, Limit::length(), 0x00);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::AnalogAPRSRXEntryElement\n * ******************************************************************************************** */\nD878UVCodeplug::AnalogAPRSRXEntryElement::AnalogAPRSRXEntryElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::AnalogAPRSRXEntryElement::AnalogAPRSRXEntryElement(uint8_t *ptr)\n  : Element(ptr, 0x0008)\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::AnalogAPRSRXEntryElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nbool\nD878UVCodeplug::AnalogAPRSRXEntryElement::isValid() const {\n  return Element::isValid() && (0 != getUInt8(0x0000));\n}\n\nQString\nD878UVCodeplug::AnalogAPRSRXEntryElement::call() const {\n  return readASCII(0x0001, 6, 0x00);\n}\nunsigned\nD878UVCodeplug::AnalogAPRSRXEntryElement::ssid() const {\n  return getUInt8(0x0007);\n}\nvoid\nD878UVCodeplug::AnalogAPRSRXEntryElement::setCall(const QString &call, unsigned ssid) {\n  writeASCII(0x0001, call, 6, 0x00); // Store call\n  setUInt8(0x0007, ssid);            // Store SSID\n  setUInt8(0x0000, 0x01);            // Enable entry.\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::AESEncryptionKeyElement\n * ******************************************************************************************** */\nD878UVCodeplug::AESEncryptionKeyElement::AESEncryptionKeyElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::AESEncryptionKeyElement::AESEncryptionKeyElement(uint8_t *ptr)\n  : Element(ptr, AESEncryptionKeyElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::AESEncryptionKeyElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nbool\nD878UVCodeplug::AESEncryptionKeyElement::isValid() const {\n  return Element::isValid() && (0 != getUInt8(Offset::index())) &&\n      (0 != getUInt8(Offset::size()));\n}\n\nunsigned\nD878UVCodeplug::AESEncryptionKeyElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nD878UVCodeplug::AESEncryptionKeyElement::setIndex(unsigned idx) {\n  setUInt8(Offset::index(), idx+1);\n}\n\nQByteArray\nD878UVCodeplug::AESEncryptionKeyElement::key() const {\n  unsigned int size = getUInt8(Offset::size());\n  // convert nibble to bytes\n  size += (size%2); size /= 2;\n  // Limit size to 32 byte (256 bit)\n  size = std::min(Limit::keySize(), size);\n  // Determine, where the key starts\n  unsigned int o = Limit::keySize() - size;\n  // Copy\n  QByteArray ar(size, 0);\n  memcpy(ar.data(), _data+Offset::key()+o, size);\n  return ar;\n}\n\nvoid\nD878UVCodeplug::AESEncryptionKeyElement::setKey(const QByteArray &key) {\n  if (Limit::keySize() < key.size())\n    return;\n  unsigned int o = Limit::keySize() - key.size();\n  memcpy(_data+Offset::key()+o, key.constData(), key.size());\n  setUInt8(Offset::size(), key.size()*2);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::AESEncryptionKeyBitmapElement\n * ******************************************************************************************** */\nD878UVCodeplug::AESEncryptionKeyBitmapElement::AESEncryptionKeyBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::AESEncryptionKeyBitmapElement::AESEncryptionKeyBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, AESEncryptionKeyBitmapElement::size())\n{\n  // pass...\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::ARC4EncryptionKeyElement\n * ******************************************************************************************** */\nD878UVCodeplug::ARC4EncryptionKeyElement::ARC4EncryptionKeyElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::ARC4EncryptionKeyElement::ARC4EncryptionKeyElement(uint8_t *ptr)\n  : Element(ptr, ARC4EncryptionKeyElement::size())\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::ARC4EncryptionKeyElement::clear() {\n  memset(_data, 0x00, _size);\n}\n\nbool\nD878UVCodeplug::ARC4EncryptionKeyElement::isValid() const {\n  return Element::isValid() && (0 != getUInt8(Offset::index()));\n}\n\nunsigned\nD878UVCodeplug::ARC4EncryptionKeyElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nD878UVCodeplug::ARC4EncryptionKeyElement::setIndex(unsigned idx) {\n  setUInt8(Offset::index(), idx+1);\n}\n\nQByteArray\nD878UVCodeplug::ARC4EncryptionKeyElement::key() const {\n  QByteArray ar(Limit::keySize(), 0);\n  memcpy(ar.data(), _data+Offset::key(), Limit::keySize());\n  return ar;\n}\n\nvoid\nD878UVCodeplug::ARC4EncryptionKeyElement::setKey(const QByteArray &key) {\n  if (Limit::keySize() < key.size())\n    return;\n  unsigned int o = Limit::keySize() - key.size();\n  memcpy(_data+Offset::key()+o, key.constData(), key.size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::ARC4EncryptionKeyBitmapElement\n * ******************************************************************************************** */\nD878UVCodeplug::ARC4EncryptionKeyBitmapElement::ARC4EncryptionKeyBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::ARC4EncryptionKeyBitmapElement::ARC4EncryptionKeyBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, ARC4EncryptionKeyBitmapElement::size())\n{\n  // pass...\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::HiddenZoneBitmapElement\n * ******************************************************************************************** */\nD878UVCodeplug::HiddenZoneBitmapElement::HiddenZoneBitmapElement(uint8_t *ptr, size_t size)\n  : BitmapElement(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::HiddenZoneBitmapElement::HiddenZoneBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, HiddenZoneBitmapElement::size())\n{\n  // pass...\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug::RadioInfoElement\n * ******************************************************************************************** */\nD878UVCodeplug::RadioInfoElement::RadioInfoElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nD878UVCodeplug::RadioInfoElement::RadioInfoElement(uint8_t *ptr)\n  : Element(ptr, 0x0100)\n{\n  // pass...\n}\n\nvoid\nD878UVCodeplug::RadioInfoElement::clear() {\n  setBandSelectPassword(\"\");\n  setProgramPassword(\"\");\n}\n\nbool\nD878UVCodeplug::RadioInfoElement::fullTest() const {\n  return getUInt8(0x0002);\n}\n\nD878UVCodeplug::RadioInfoElement::FrequencyRange\nD878UVCodeplug::RadioInfoElement::frequencyRange() const {\n  return (FrequencyRange)getUInt8(0x0003);\n}\nvoid\nD878UVCodeplug::RadioInfoElement::setFrequencyRange(FrequencyRange range) {\n  setUInt8(0x0003, (unsigned)range);\n}\n\nbool\nD878UVCodeplug::RadioInfoElement::international() const {\n  return getUInt8(0x0004);\n}\nvoid\nD878UVCodeplug::RadioInfoElement::enableInternational(bool enable) {\n  setUInt8(0x0004, (enable ? 0x01 : 0x00));\n}\n\nbool\nD878UVCodeplug::RadioInfoElement::bandSelect() const {\n  return getUInt8(0x0006);\n}\nvoid\nD878UVCodeplug::RadioInfoElement::enableBandSelect(bool enable) {\n  setUInt8(0x0006, (enable ? 0x01 : 0x00));\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::bandSelectPassword() const {\n  return readASCII(0x000b, 4, 0x00);\n}\nvoid\nD878UVCodeplug::RadioInfoElement::setBandSelectPassword(const QString &passwd) {\n  writeASCII(0x000b, passwd, 4, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::radioType() const {\n  return readASCII(0x0010, 7, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::programPassword() const {\n  return readASCII(0x0028, 4, 0x00);\n}\nvoid\nD878UVCodeplug::RadioInfoElement::setProgramPassword(const QString &passwd) {\n  writeASCII(0x0028, passwd, 4, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::areaCode() const {\n  return readASCII(0x002c, 4, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::serialNumber() const {\n  return readASCII(0x0030, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::productionDate() const {\n  return readASCII(0x0040, 10, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::manufacturerCode() const {\n  return readASCII(0x0050, 8, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::maintainedDate() const {\n  return readASCII(0x0060, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::dealerCode() const {\n  return readASCII(0x0070, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::stockDate() const {\n  return readASCII(0x0080, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::sellDate() const {\n  return readASCII(0x0090, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::seller() const {\n  return readASCII(0x00a0, 16, 0x00);\n}\n\nQString\nD878UVCodeplug::RadioInfoElement::maintainerNote() const {\n  return readASCII(0x00b0, 128, 0x00);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of D878UVCodeplug\n * ******************************************************************************************** */\nD878UVCodeplug::D878UVCodeplug(const QString &label, QObject *parent)\n  : D868UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nD878UVCodeplug::D878UVCodeplug(QObject *parent)\n  : D868UVCodeplug(\"Anytone AT-D878UV Codeplug\", parent)\n{\n  // pass...\n}\n\nbool\nD878UVCodeplug::allocateBitmaps() {\n  if (! D868UVCodeplug::allocateBitmaps())\n    return false;\n\n  // Roaming channel bitmaps\n  image(0).addElement(Offset::roamingChannelBitmap(), RoamingChannelBitmapElement::size());\n  // Roaming zone bitmaps\n  image(0).addElement(Offset::roamingZoneBitmap(), RoamingZoneBitmapElement::size());\n  // AES encryption keys\n  image(0).addElement(Offset::aesKeyBitmap(), AESEncryptionKeyBitmapElement::size());\n  // ARC4 encryption keys\n  image(0).addElement(Offset::arc4KeyBitmap(), ARC4EncryptionKeyBitmapElement::size());\n\n  return true;\n}\n\nvoid\nD878UVCodeplug::allocateUpdated() {\n  // First allocate everything common between D868UV and D878UV codeplugs.\n  D868UVCodeplug::allocateUpdated();\n\n  // allocate APRS RX list\n  image(0).addElement(Offset::analogAPRSRXEntries(),\n                      Limit::analogAPRSRXEntries()*AnalogAPRSRXEntryElement::size());\n}\n\nvoid\nD878UVCodeplug::allocateForEncoding() {\n  // First allocate everything common between D868UV and D878UV codeplugs.\n  D868UVCodeplug::allocateForEncoding();\n  this->allocateRoaming();\n  this->allocateAESKeys();\n  this->allocateARC4Keys();\n}\n\nvoid\nD878UVCodeplug::allocateForDecoding() {\n  // First allocate everything common between D868UV and D878UV codeplugs.\n  D868UVCodeplug::allocateForDecoding();\n  this->allocateRoaming();\n  this->allocateAESKeys();\n  this->allocateARC4Keys();\n}\n\nvoid\nD878UVCodeplug::setBitmaps(Context& ctx)\n{\n  // First set everything common between D868UV and D878UV codeplugs.\n  D868UVCodeplug::setBitmaps(ctx);\n\n  // Mark roaming zones\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  unsigned int num_roaming_zones = std::min(Limit::roamingZones(), ctx.count<RoamingZone>());\n  roaming_zone_bitmap.clear(); roaming_zone_bitmap.enableFirst(num_roaming_zones);\n\n  // Mark roaming channels\n  RoamingChannelBitmapElement roaming_ch_bitmap(data(Offset::roamingChannelBitmap()));\n  unsigned int num_roaming_channel = std::min(Limit::roamingChannels(), ctx.count<RoamingChannel>());\n  roaming_ch_bitmap.clear(); roaming_ch_bitmap.enableFirst(num_roaming_channel);\n\n  // Mark AES keys\n  AESEncryptionKeyBitmapElement aes_key_bitmap(data(Offset::aesKeyBitmap()));\n  unsigned int num_aes_keys = std::min(Limit::aesKeys(), ctx.count<AESEncryptionKey>());\n  aes_key_bitmap.clear(); aes_key_bitmap.enableFirst(num_aes_keys);\n\n  // Mark ARC4 keys\n  ARC4EncryptionKeyBitmapElement arc4_key_bitmap(data(Offset::arc4KeyBitmap()));\n  unsigned int num_arc4_channels = std::min(Limit::arc4Keys(), ctx.count<ARC4EncryptionKey>());\n  arc4_key_bitmap.clear(); arc4_key_bitmap.enableFirst(num_arc4_channels);\n}\n\n\nConfig *\nD878UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  // Apply base preprocessing\n  auto intermediate = AnytoneCodeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot apply preprocessing for D868UVE.\";\n    return nullptr;\n  }\n\n  // Keep 16-bit DMR, 128-bit AES and 256-bit AES keys.\n  EncryptionKeyFilterVisitor filter(\n        { EncryptionKeyFilterVisitor::Filter(BasicEncryptionKey::staticMetaObject, 16, 16),\n          EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 128, 128),\n          EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 256, 256),});\n  if (! filter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot remove unsupported encryption.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nD878UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  // Encode everything common between d868uv and d878uv radios.\n  if (! D868UVCodeplug::encodeElements(flags, ctx, err))\n    return false;\n\n  if (! this->encodeRoaming(flags, ctx, err))\n    return false;\n\n  if (! this->encodeAESKeys(flags, ctx, err))\n    return false;\n\n  if (! this->encodeARC4Keys(flags, ctx, err))\n    return false;\n\n  return true;\n}\n\n\nbool\nD878UVCodeplug::createElements(Context &ctx, const ErrorStack &err) {\n  // Create everything commong between d868uv and d878uv codeplugs.\n  if (! D868UVCodeplug::createElements(ctx, err))\n    return false;\n\n  if (! this->createAESKeys(ctx, err))\n    return false;\n\n  if (! this->createARC4Keys(ctx, err))\n    return false;\n\n  if (! this->createRoaming(ctx, err))\n    return false;\n\n  return true;\n}\n\nbool\nD878UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) {\n  // Link all common elements\n  if (! D868UVCodeplug::linkElements(ctx, err))\n    return false;\n\n  if (! this->linkRoaming(ctx, err))\n    return false;\n\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateChannels() {\n  /* Allocate channels */\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // if disabled -> skip\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    // compute address for channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx=i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx * ChannelElement::size();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, ChannelElement::size());\n    }\n    if (!isAllocated(addr+Offset::toChannelExtension(), 0)) {\n      image(0).addElement(addr+Offset::toChannelExtension(), ChannelElement::size());\n      memset(data(addr+Offset::toChannelExtension()), 0x00, ChannelElement::size());\n    }\n  }\n}\n\nbool\nD878UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Encode channels\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    // enable channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n\n    ChannelElement ch(data(addr));\n    ch.fromChannelObj(ctx.get<Channel>(i), ctx);\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n    ext.fromChannelObj(ctx.get<Channel>(i), ctx);\n  }\n  return true;\n}\n\nbool\nD878UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create channels\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n\n    ChannelElement ch(data(addr));\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n\n    if (Channel *obj = ch.toChannelObj(ctx)) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i);\n      ext.updateChannelObj(obj, ctx);\n    }\n  }\n  return true;\n}\n\nbool\nD878UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n\n  // Link channel objects\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(addr));\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n\n    if (ctx.has<Channel>(i)) {\n      ch.linkChannelObj(ctx.get<Channel>(i), ctx);\n      ext.linkChannelObj(ctx.get<Channel>(i), ctx);\n    }\n  }\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateZones() {\n  D868UVCodeplug::allocateZones();\n  // Hidden zone map\n  image(0).addElement(Offset::hiddenZoneBitmap(), HiddenZoneBitmapElement::size());\n}\n\nbool\nD878UVCodeplug::encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! D868UVCodeplug::encodeZone(i, zone, flags, ctx, err))\n    return false;\n\n  AnytoneZoneExtension *ext = zone->anytoneExtension();\n  if (nullptr == ext)\n      return true;\n\n  HiddenZoneBitmapElement(data(Offset::hiddenZoneBitmap())).setEncoded(i, ext->hidden());\n\n  return true;\n}\n\nbool\nD878UVCodeplug::decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err) {\n  if (! D868UVCodeplug::decodeZone(i, zone, ctx, err))\n    return false;\n  AnytoneZoneExtension *ext = zone->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneZoneExtension();\n    zone->setAnytoneExtension(ext);\n  }\n\n  HiddenZoneBitmapElement bitmap(data(Offset::hiddenZoneBitmap()));\n  ext->enableHidden(bitmap.isEncoded(i));\n\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateGeneralSettings() {\n  // override allocation of general settings for D878UV code-plug. General settings are larger!\n  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());\n  image(0).addElement(Offset::dmrAPRSMessage(), DMRAPRSMessageElement::size());\n  image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size());\n  image(0).addElement(Offset::primaryId(), PrimaryRadioIdElement::size());\n}\nbool\nD878UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err);\n  DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).fromConfig(flags, ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx, err);\n  PrimaryRadioIdElement(data(Offset::primaryId())).encode(flags, ctx, err);\n  return true;\n}\nbool\nD878UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n  DMRAPRSMessageElement(data(Offset::dmrAPRSMessage())).updateConfig(ctx);\n  ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx, err);\n  return true;\n}\nbool\nD878UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).linkConfig(ctx, err)) {\n    errMsg(err) << \"Cannot link general settings extension.\";\n    return false;\n  }\n\n  return true;\n}\n\nvoid\nD878UVCodeplug::allocateGPSSystems() {\n  // replaces D868UVCodeplug::allocateGPSSystems\n\n  // APRS settings\n  image(0).addElement(Offset::aprsSettings(), APRSSettingsElement::size());\n  image(0).addElement(Offset::analogAPRSMessage(), AnalogAPRSMessageElement::size());\n}\n\nbool\nD878UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // replaces D868UVCodeplug::encodeGPSSystems\n\n  APRSSettingsElement aprs(data(Offset::aprsSettings()));\n\n  // Encode APRS system (there can only be one)\n  if (0 < ctx.count<FMAPRSSystem>()) {\n    aprs.fromFMAPRSSystem(ctx.get<FMAPRSSystem>(0), ctx, err);\n    AnalogAPRSMessageElement(data(Offset::analogAPRSMessage()))\n        .setMessage(ctx.get<FMAPRSSystem>(0)->message());\n  }\n  if (! aprs.fromConfig(ctx, err)) {\n    errMsg(err) << \"Cannot encode global APRS settings.\";\n    return false;\n  }\n\n  // Encode GPS systems\n  if (! aprs.fromDMRAPRSSystems(ctx))\n    return false;\n  if (0 < ctx.count<DMRAPRSSystem>()) {\n    // If there is at least one GPS system defined -> set auto TX interval.\n    //  This setting might be overridden by any analog APRS system below\n    APRSSettingsElement aprs(data(Offset::aprsSettings()));\n    aprs.setAutoTXInterval(ctx.get<DMRAPRSSystem>(0)->period());\n    aprs.setManualTXInterval(ctx.get<DMRAPRSSystem>(0)->period());\n  }\n  return true;\n}\n\nbool\nD878UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // replaces D868UVCodeplug::createGPSSystems\n\n  // Before creating any GPS/APRS systems, get global auto TX interval\n  APRSSettingsElement aprs(data(Offset::aprsSettings()));\n  AnalogAPRSMessageElement  aprsMessage(data(Offset::analogAPRSMessage()));\n\n  if (! aprs.updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot update global APRS settings.\";\n    return false;\n  }\n\n  // Create APRS system (if enabled)\n  if (aprs.isValid()) {\n    FMAPRSSystem *sys = aprs.toFMAPRSSystem(ctx, err);\n    if (nullptr == sys) {\n      errMsg(err) << \"Cannot decode positioning systems.\";\n      return false;\n    }\n    sys->setPeriod(aprs.autoTXInterval());\n    sys->setMessage(aprsMessage.message());\n    ctx.config()->posSystems()->add(sys); ctx.add(sys,0);\n  }\n\n  // Create GPS systems\n  for (unsigned int i=0; i<Limit::dmrAPRSSystems(); i++) {\n    if (0 == aprs.dmrDestination(i))\n      continue;\n    if (DMRAPRSSystem *sys = aprs.toDMRAPRSSystemObj(i)) {\n      logDebug() << \"Create GPS sys '\" << sys->name() << \"' at idx \" << i << \".\";\n      sys->setPeriod(aprs.autoTXInterval());\n      ctx.config()->posSystems()->add(sys); ctx.add(sys, i);\n    } else {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nD878UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  // replaces D868UVCodeplug::linkGPSSystems\n\n  // Link APRS system\n  APRSSettingsElement aprs(data(Offset::aprsSettings()));\n  if (aprs.isValid()) {\n    aprs.linkFMAPRSSystem(ctx.get<FMAPRSSystem>(0), ctx);\n  }\n\n  // Link GPS systems\n  for (unsigned int i=0; i<Limit::dmrAPRSSystems(); i++) {\n    if (0 == aprs.dmrDestination(i))\n      continue;\n    aprs.linkDMRAPRSSystem(i, ctx.get<DMRAPRSSystem>(i), ctx);\n  }\n\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateRoaming() {\n  /* Allocate roaming channels */\n  RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap()));\n  for (uint8_t i=0; i<Limit::roamingChannels(); i++) {\n    // Get byte and bit for roaming channel\n    // if disabled -> skip\n    if (! roaming_channel_bitmap.isEncoded(i))\n      continue;\n    // Allocate roaming channel\n    uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size();\n    if (!isAllocated(addr, 0)) {\n      //logDebug() << \"Allocate roaming channel at \" << QString::number(addr, 16) << \"h.\";\n      image(0).addElement(addr, RoamingChannelElement::size());\n    }\n  }\n\n  /* Allocate roaming zones. */\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  for (uint8_t i=0; i<Limit::roamingZones(); i++) {\n    // Get byte and bit for radio ID\n    // if disabled -> skip\n    if (! roaming_zone_bitmap.isEncoded(i))\n      continue;\n    // Allocate roaming zone\n    uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size();\n    if (!isAllocated(addr, 0)) {\n      logDebug() << \"Allocate roaming zone at \" << QString::number(addr, 16);\n      image(0).addElement(addr, RoamingZoneElement::size());\n    }\n  }\n}\n\nbool\nD878UVCodeplug::encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  // Encode roaming channels\n  unsigned int num_roaming_channel = std::min(\n        Limit::roamingChannels(), ctx.count<RoamingChannel>());\n  for (uint8_t i=0; i<num_roaming_channel; i++) {\n    // Encode roaming channel\n    uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size();\n    RoamingChannelElement rch_elm(data(addr));\n    RoamingChannel *rch = ctx.config()->roamingChannels()->get(i)->as<RoamingChannel>();\n    rch_elm.clear();\n    rch_elm.fromChannel(rch);\n    if (! ctx.add(rch, i)) {\n      errMsg(err) << \"Cannot add index \" << i << \" for roaming channel '\"\n                  << rch->name() << \"' to codeplug context.\";\n      return false;\n    }\n  }\n\n  // Encode roaming zones\n  for (unsigned int i=0; i<ctx.count<RoamingZone>(); i++){\n    uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size();\n    RoamingZoneElement zone(data(addr));\n    logDebug() << \"Encode roaming zone \" << ctx.config()->roamingZones()->zone(i)->name()\n               << \" (\" << (i+1) << \") at \" << QString::number(addr, 16)\n               << \" with \" << ctx.config()->roamingZones()->zone(i)->count() << \" elements.\";\n    zone.fromRoamingZone(ctx.config()->roamingZones()->zone(i), ctx);\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create or find roaming channels\n  RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap()));\n  for (unsigned int i=0; i<Limit::roamingChannels(); i++) {\n    if (! roaming_channel_bitmap.isEncoded(i))\n      continue;\n    uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size();\n    RoamingChannelElement ch(data(addr));\n    RoamingChannel *digi = ch.toChannel(ctx);\n    ctx.add(digi, i);\n  }\n\n  // Create and link roaming zones\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  for (unsigned int i=0; i<Limit::roamingZones(); i++) {\n    if (! roaming_zone_bitmap.isEncoded(i))\n      continue;\n    uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size();\n    RoamingZoneElement z(data(addr));\n    RoamingZone *zone = z.toRoamingZone(ctx, err);\n    ctx.config()->roamingZones()->add(zone); ctx.add(zone, i);\n    z.linkRoamingZone(zone, ctx, err);\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::linkRoaming(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  // Pass, no need to link roaming channels.\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateAESKeys() {\n  AESEncryptionKeyBitmapElement bitmap(data(Offset::aesKeyBitmap()));\n  for (uint8_t i=0; i<Limit::aesKeys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    // Allocate key\n    uint32_t addr = Offset::aesKeys() + i*AESEncryptionKeyElement::size();\n    if (! isAllocated(addr, 0)) {\n      image(0).addElement(addr, AESEncryptionKeyElement::size());\n    }\n  }\n}\n\nbool\nD878UVCodeplug::encodeAESKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(flags);\n\n  AESEncryptionKeyBitmapElement bitmap(data(Offset::aesKeyBitmap()));\n  for (uint8_t i=0; i<Limit::aesKeys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    AESEncryptionKeyElement key(data(Offset::aesKeys() + i*AESEncryptionKeyElement::size()));\n    key.setIndex(i);\n    key.setKey(ctx.get<AESEncryptionKey>(i)->key());\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::createAESKeys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  AESEncryptionKeyBitmapElement bitmap(data(Offset::aesKeyBitmap()));\n  for (uint8_t i=0; i<Limit::aesKeys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    // Decode\n    AESEncryptionKeyElement keyEl(data(Offset::aesKeys() + i*AESEncryptionKeyElement::size()));\n    if (! keyEl.isValid()) {\n      logInfo() << \"Skip invalid AES key at index \" << i+1 << \".\";\n      continue;\n    }\n    if (keyEl.key().size() < 16) {\n      logInfo() << \"Skip AES key at index \" << i+1\n                << \": Key size \" << keyEl.key().size()*8 << \" < 128.\";\n      continue;\n    }\n    auto key = new AESEncryptionKey();\n    key->setName(QString(\"AES Key %1\").arg(i+1));\n    if (! key->setKey(keyEl.key(), err)) {\n      delete key;\n      return false;\n    }\n    // Store\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n\n\nvoid\nD878UVCodeplug::allocateARC4Keys() {\n  ARC4EncryptionKeyBitmapElement bitmap(data(Offset::arc4KeyBitmap()));\n  for (uint8_t i=0; i<Limit::arc4Keys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    // Allocate key\n    uint32_t addr = Offset::arc4Keys() + i*ARC4EncryptionKeyElement::size();\n    if (! isAllocated(addr, 0)) {\n      image(0).addElement(addr, ARC4EncryptionKeyElement::size());\n    }\n  }\n}\n\nbool\nD878UVCodeplug::encodeARC4Keys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(flags);\n\n  ARC4EncryptionKeyBitmapElement bitmap(data(Offset::arc4KeyBitmap()));\n  for (uint8_t i=0; i<Limit::arc4Keys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    ARC4EncryptionKeyElement key(data(Offset::arc4Keys() + i*ARC4EncryptionKeyElement::size()));\n    key.setIndex(i);\n    key.setKey(ctx.get<ARC4EncryptionKey>(i)->key());\n  }\n\n  return true;\n}\n\nbool\nD878UVCodeplug::createARC4Keys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  ARC4EncryptionKeyBitmapElement bitmap(data(Offset::arc4KeyBitmap()));\n  for (uint8_t i=0; i<Limit::arc4Keys(); i++) {\n    // if disabled -> skip\n    if (! bitmap.isEncoded(i))\n      continue;\n    // Decode\n    ARC4EncryptionKeyElement keyEl(data(Offset::arc4Keys() + i*ARC4EncryptionKeyElement::size()));\n    auto key = new ARC4EncryptionKey();\n    key->setName(QString(\"ARC4 Key %1\").arg(i+1));\n    if (! key->setKey(keyEl.key(), err)) {\n      delete key;\n      return false;\n    }\n    // Store\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/d878uv_codeplug.hh",
    "content": "#ifndef D878UV_CODEPLUG_HH\n#define D878UV_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"d868uv_codeplug.hh\"\n#include \"signaling.hh\"\n#include \"gpssystem.hh\"\n#include \"gnsssettings.hh\"\n#include \"dmrsettings.hh\"\n#include \"smsextension.hh\"\n\nclass Channel;\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\nclass RoamingChannel;\n\n\n/** Represents the device specific binary codeplug for Anytone AT-D878UV radios.\n *\n * In contrast to many other code-plugs, the code-plug for Anytone radios are spread over a large\n * memory area. In principle, this is a good idea, as it allows one to upload only the portion of the\n * codeplug that is actually configured. For example, if only a small portion of the available\n * contacts and channels are used, the amount of data that is written to the device can be\n * reduced.\n *\n * However, the implementation of this idea in this device is utter shit. The amount of\n * fragmentation of the codeplug is overwhelming. For example, while channels are organized more or\n * less nicely in continuous banks, zones are distributed throughout the entire code-plug. That is,\n * the names of zones are located in a different memory section that the channel lists. Some lists\n * are defined though bit-masks others by byte-masks. All bit-masks are positive, that is 1\n * indicates an enabled item while the bit-mask for contacts is inverted.\n *\n * In general the code-plug is huge and complex. Moreover, the radio provides a huge amount of\n * options and features. To this end, reverse-engineering this code-plug was a nightmare.\n *\n * More over, the binary code-plug file generate by the windows CPS does not directly relate to\n * the data being written to the radio. To this end the code-plug has been reverse-engineered\n * using wireshark to monitor the USB communication between the windows CPS (running in a virtual\n * box) and the device. The latter makes the reverse-engineering particularly cumbersome.\n *\n * For more details, see https://dmr-tools.github.io/codeplugs.\n *\n * @ingroup d878uv */\nclass D878UVCodeplug : public D868UVCodeplug\n{\n  Q_OBJECT\n\nprotected:\n  /** Channel name and call-sign colors supported by the D878UV. */\n  struct NameColor {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      Orange=0, Red=1, Yellow=2, Green=3, Turquoise=4, Blue=5, White = 6\n    } CodedColor;\n  };\n\n  /** Text colors supported by the D878UV. */\n  struct TextColor {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      White=0, Black=1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7\n    } CodedColor;\n  };\n\npublic:\n  /** Represents the actual channel encoded within the binary D878UV codeplug.\n   *\n   * This class implements only the differences to the generic @c AnytoneCodeplug::ChannelElement\n   * (i.e. D868UVII).\n   *\n   * Memory layout of encoded channel (size 0x40 bytes):\n   * @verbinclude d878uv_channel.txt\n   */\n  class ChannelElement: public D868UVCodeplug::ChannelElement\n  {\n  public:\n    /** Possible PTT ID settings. */\n    enum class PTTId {\n      Off = 0,                    ///< Never send PTT-ID.\n      Start = 1,                  ///< Send PTT-ID at start.\n      End = 2,                    ///< Send PTT-ID at end.\n      Both = 3                    ///< Send PTT-ID at start and end.\n    };\n\n    /** Possible APRS modes. */\n    enum class APRSType {\n      Off = 0, FM = 1, DMR = 2\n    };\n\n    /** Defines all possible APRS PTT settings. */\n    enum class APRSPTT {\n      Off   = 0,                  ///< Do not send APRS on PTT.\n      Start = 1,                  ///< Send APRS at start of transmission.\n      End   = 2                   ///< Send APRS at end of transmission.\n    };\n\n    /** Possible encryption types. */\n    enum class AdvancedEncryptionType {\n      AES, ARC4\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    /** Resets the channel. */\n    void clear()  override;\n\n    /** Returns the PTT ID settings. */\n    virtual PTTId pttIDSetting() const;\n    /** Sets the PTT ID setting. */\n    virtual void setPTTIDSetting(PTTId ptt);\n\n    /** Returns @c true if roaming is enabled. */\n    virtual bool roamingEnabled() const;\n    /** Enables/disables roaming. */\n    virtual void enableRoaming(bool enable);\n    // Moved\n    /** Returns @c true if the data ACK is enabled. */\n    bool dataACK() const override;\n    /** Enables/disables data ACK. */\n    void enableDataACK(bool enable) override;\n    /** Returns @c true, if auto scan is enabled. */\n    virtual bool autoScan() const ;\n    /** Enable/disable auto scan. */\n    virtual void enableAutoScan(bool enable);\n\n    /** Returns APRS type for reporting the position. */\n    APRSType txAPRSType() const;\n    /** Sets APRS type for reporting the position. */\n    void setTXAPRSType(APRSType aprsType);\n\n    /** Returns the analog APRS PTT setting. */\n    virtual AnytoneChannelExtension::APRSPTT analogAPRSPTTSetting() const;\n    /** Sets the analog APRS PTT setting. */\n    virtual void setAnalogAPRSPTTSetting(AnytoneChannelExtension::APRSPTT ptt);\n    /** Returns the digital APRS PTT setting. */\n    virtual AnytoneChannelExtension::APRSPTT digitalAPRSPTTSetting() const;\n    /** Sets the digital APRS PTT setting. */\n    virtual void setDigitalAPRSPTTSetting(AnytoneChannelExtension::APRSPTT ptt);\n\n    /** Returns the DMR APRS system index. */\n    unsigned digitalAPRSSystemIndex() const override;\n    /** Sets the DMR APRS system index. */\n    void setDigitalAPRSSystemIndex(unsigned idx) override;\n\n    /** Returns the frequency correction in ???. */\n    virtual int frequenyCorrection() const;\n    /** Sets the frequency correction in ???. */\n    virtual void setFrequencyCorrection(int corr);\n\n    /** Returns the index of the FM APRS frequency [0,7]. */\n    virtual unsigned int fmAPRSFrequencyIndex() const;\n    /** Sets the FM APRS frequency index [0,7]. */\n    virtual void setFMAPRSFrequencyIndex(unsigned int idx);\n\n    /** If set, transmission of talker alias for this channel is enabled. */\n    virtual bool sendTalkerAlias() const;\n    /** Enable transmission of talker alias. */\n    virtual void enableSendTalkerAlias(bool enable);\n\n    /** Returns the encryption type. */\n    virtual AdvancedEncryptionType advancedEncryptionType() const;\n    /** Sets the encryptionType. */\n    virtual void setEncryptionType(AdvancedEncryptionType type);\n\n    /** Returns @c true, if an AES encryption key index is set. */\n    virtual bool hasAESEncryptionKeyIndex() const;\n    /** Returns the AES encryption key index.\n     * The index is 0-based. */\n    virtual unsigned int aesEncryptionKeyIndex() const;\n    /** Sets the AES encryption key index. */\n    virtual void setAESEncryptionKeyIndex(unsigned int index);\n    /** Clears the AES encryption key index. */\n    virtual void clearAESEncryptionKeyIndex();\n\n    /** Returns @c true, if an ARC4 encryption key index is set. */\n    virtual bool hasARC4EncryptionKeyIndex() const;\n    /** Returns the ARC4 encryption key index.\n     * The index is 0-based. */\n    virtual unsigned int arc4EncryptionKeyIndex() const;\n    /** Sets the ARC4 encryption key index. */\n    virtual void setARC4EncryptionKeyIndex(unsigned int index);\n    /** Clears the ARC4 encryption key index. */\n    virtual void clearARC4EncryptionKeyIndex();\n\n    /** Returns the encryption type. */\n    DMREncryptionType dmrEncryptionType() const override;\n    /** Sets the encryption type. */\n    void setDMREncryptionType(DMREncryptionType type) override;\n    /** Returns @c true if a DMR encryption key is set. */\n    bool hasDMREncryptionKeyIndex() const override;\n    /** Returns the DMR encryption key index (+1), 0=Off. */\n    unsigned dmrEncryptionKeyIndex() const override;\n    /** Sets the DMR encryption key index (+1), 0=Off. */\n    void setDMREncryptionKeyIndex(unsigned idx) override;\n    /** Clears the DMR encryption key index. */\n    void clearDMREncryptionKeyIndex() override;\n\n    bool adaptiveTDMA() const override;\n    void enableAdaptiveTDMA(bool enable) override;\n\n    /** Constructs a Channel object from this element. */\n    Channel *toChannelObj(Context &ctx) const override;\n    /** Links a previously created channel object. */\n    bool linkChannelObj(Channel *c, Context &ctx) const override;\n    /** Encodes the given channel object. */\n    bool fromChannelObj(const Channel *c, Context &ctx) override;\n\n  protected:\n    /** Internal used offsets within the channel element. */\n    struct Offset : public D868UVCodeplug::ChannelElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int pttIDSetting()         { return 0x0019; }\n      static constexpr unsigned int aesKeyIndex()          { return 0x0022; }\n      static constexpr Bit roaming()                       { return {0x0034, 2}; }\n      static constexpr Bit dataACK()                       { return {0x0034, 3}; }\n      static constexpr Bit autoScan()                      { return {0x0034, 4}; }\n      static constexpr unsigned int fmAPRSPTTSetting()     { return 0x0036; }\n      static constexpr unsigned int dmrAPRSPTTSetting()    { return 0x0037; }\n      static constexpr unsigned int dmrAPRSSystemIndex()   { return 0x0038; }\n      static constexpr unsigned int frequenyCorrection()   { return 0x0039; }\n      static constexpr unsigned int dmrEncryptionKey()     { return 0x003a; }\n      static constexpr Bit muteFMAPRS()                    { return {0x003b, 3}; }\n      static constexpr Bit talkerAlias()                   { return {0x003b, 4}; }\n      static constexpr Bit advancedEncryptionType()        { return {0x003b, 5}; }\n      static constexpr unsigned int fmAPRSFrequencyIndex() { return 0x003c; }\n      static constexpr unsigned int arc4KeyIndex()         { return 0x003d; }\n      // Removed\n      static constexpr Bit adaptiveTDMA()                  { return {0, 0}; }\n      /// @endcond\n    };\n  };\n\n\n  /** Starting from AT-D878UV, all AnyTone devices encode a channel settings extension element\n   * at an offset 0x2000 to the original channel. Also, the size of the extension element is\n   * identical to the channel element itself. This is weird. Anyway. This class encodes/decodes\n   * these settings.\n   *\n   * This implementation is compatible with firmware version 3.06 */\n  class ChannelExtensionElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelExtensionElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Default constructor. */\n    ChannelExtensionElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return ChannelElement::size(); }\n\n    /** Resets the channel extension. */\n    void clear();\n\n    /** Returns the BOT 5-tone ID index. */\n    virtual unsigned int bot5ToneIDIndex() const;\n    /** Sets the BOT 5-tone ID index. */\n    virtual void setBOT5ToneIDIndex(unsigned int idx);\n\n    /** Returns the EOT 5-tone ID index. */\n    virtual unsigned int eot5ToneIDIndex() const;\n    /** Sets the EOT 5-tone ID index. */\n    virtual void setEOT5ToneIDIndex(unsigned int idx);\n\n    /** Returns the transmit color code. */\n    virtual unsigned int txColorCode() const;\n    /** Sets the transmit color code. */\n    virtual void setTXColorCode(unsigned int cc);\n\n    /** Constructs a Channel object from this element. */\n    virtual bool updateChannelObj(Channel *c, Context &ctx) const;\n    /** Links a previously created channel object. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx) const;\n    /** Encodes the given channel object. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx);\n\n  protected:\n    /** Some internal used offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bot5ToneIDIndex() { return 0x0000; }\n      static constexpr unsigned int eot5ToneIDIndex() { return 0x0001; }\n      static constexpr unsigned int txColorCode()     { return 0x0003; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the general config of the radio within the D878UV binary codeplug.\n   *\n   * This class implements only the differences to the generic\n   * @c AnytonCodeplug::GeneralSettingsElement.\n   *\n   * Binary encoding of the general settings (size 0x00f0 bytes):\n   * @verbinclude d878uv_generalsettings.txt */\n  class GeneralSettingsElement: public D868UVCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific key functions. */\n    struct KeyFunction {\n    public:\n      /** Encodes key function. */\n      static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone);\n      /** Decodes key function. */\n      static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code);\n\n    protected:\n      /** Device specific key functions. */\n      typedef enum {\n        Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04,\n        Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09,\n        Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f,\n        Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14,\n        HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19,\n        WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d,\n        PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21,\n        ChannelType = 0x22, Ranging = 0x23, Roaming = 0x24, ChannelRanging = 0x25,\n        MaxVolume = 0x26, Slot = 0x27, APRSType = 0x28, Zone = 0x29, RoamingSet = 0x2a,\n        APRSSet = 0x2b, Mute=0x2c, CtcssDcsSet=0x2d, TBSTSend = 0x2e, Bluetooth = 0x2f,\n        GPS = 0x30, ChannelName = 0x31, CDTScan = 0x32, APRSSend = 0x33, APRSInfo = 0x34,\n        GPSRoaming = 0x35, DIMShut = 0x36, Squelch = 0x38\n      } KeyFunctionCode;\n    };\n\n    /** Device specific time zones. */\n    struct TimeZone {\n    public:\n      /** Encodes time zone. */\n      static uint8_t encode(const QTimeZone& zone);\n      /** Decodes time zone. */\n      static QTimeZone decode(uint8_t code);\n\n    protected:\n      /** Vector of possible time-zones. */\n      static QVector<QTimeZone> _timeZones;\n    };\n\n  protected:\n    /** Possible VFO frequency steps. */\n    enum FreqStep {\n      FREQ_STEP_2_5kHz = 0,             ///< 2.5kHz\n      FREQ_STEP_5kHz = 1,               ///< 5kHz\n      FREQ_STEP_6_25kHz = 2,            ///< 6.25kHz\n      FREQ_STEP_8_33kHz = 3,            ///< 8.33kHz\n      FREQ_STEP_10kHz = 4,              ///< 10kHz\n      FREQ_STEP_12_5kHz = 5,            ///< 12.5kHz\n      FREQ_STEP_20kHz = 6,              ///< 20kHz\n      FREQ_STEP_25kHz = 7,              ///< 25kHz\n      FREQ_STEP_50kHz = 8               ///< 50kHz\n    };\n\n    /** DTMF signalling durations. */\n    enum DTMFDuration {\n      DTMF_DUR_50ms = 0, DTMF_DUR_100ms = 1, DTMF_DUR_200ms = 2, DTMF_DUR_300ms = 3, DTMF_DUR_500ms = 4\n    };\n\n    /** TBST (open repeater) frequencies. */\n    enum class TBSTFrequency {\n      Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3\n    };\n\n    /** All possible STE (squelch tail eliminate) frequencies. */\n    enum class STEFrequency {\n      Off = 0, Hz55_2  = 1, Hz259_2 = 2\n    };\n\n    /** Possible background images. */\n    enum class BackgroundImage {\n      Default=0, Custom1=1, Custom2=2\n    };\n\n    /** Encoding of possible backlight durations. */\n    enum class BacklightDuration {\n      Infinite = 0, _5s = 1, _10s = 2, _15s = 3, _20s = 4, _25s = 5, _30s = 6, _1min=7, _2min=8,\n      _3min = 9, _4min = 10, _5min = 11, _15min = 12, _30min = 13, _45min = 14, _1h = 15\n    };\n\n    /** Possible SMS formats. */\n    enum class SMSFormat {\n      Motorola = 0, Hytera = 1, DMR = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00f0; }\n\n    /** Resets the general settings. */\n    void clear() override;\n\n    /** Returns the transmit timeout in seconds. */\n    virtual Interval transmitTimeout() const;\n    /** Sets the transmit timeout in seconds. */\n    virtual void setTransmitTimeout(const Interval &tot);\n\n    /** Returns the UI language. */\n    virtual AnytoneDisplaySettingsExtension::Language language() const;\n    /** Sets the UI language. */\n    virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang);\n\n    QTimeZone gpsTimeZone() const override;\n    void setGPSTimeZone(const QTimeZone &zone) override;\n\n    /** Returns the VFO frequency step in kHz. */\n    virtual Frequency vfoFrequencyStep() const;\n    /** Sets the VFO frequency step in kHz. */\n    virtual void setVFOFrequencyStep(Frequency kHz);\n\n    AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const override;\n    void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const override;\n    void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const override;\n    void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const override;\n    void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const override;\n    void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const override;\n    void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const override;\n    void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const override;\n    void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const override;\n    void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const override;\n    void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n\n    /** Returns the STE (squelch tail eliminate) type. */\n    virtual AnytoneSettingsExtension::STEType steType() const;\n    /** Sets the STE (squelch tail eliminate) type. */\n    virtual void setSTEType(AnytoneSettingsExtension::STEType type);\n    /** Returns the STE (squelch tail eliminate) frequency setting in Hz.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual double steFrequency() const;\n    /** Sets the STE (squelch tail eliminate) frequency setting.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual void setSTEFrequency(double freq);\n\n    /** Returns the group call hang time in seconds. */\n    virtual Interval groupCallHangTime() const;\n    /** Sets the group call hang time in seconds. */\n    virtual void setGroupCallHangTime(Interval sec);\n    /** Returns the private call hang time in seconds. */\n    virtual Interval privateCallHangTime() const;\n    /** Sets the private call hang time in seconds. */\n    virtual void setPrivateCallHangTime(Interval sec);\n\n    /** Returns the pre-wave time in ms. */\n    virtual Interval preWaveDelay() const;\n    /** Sets the pre-wave time in ms. */\n    virtual void setPreWaveDelay(Interval ms);\n    /** Returns the wake head-period in ms. */\n    virtual Interval wakeHeadPeriod() const;\n    /** Sets the wake head-period in ms. */\n    virtual void setWakeHeadPeriod(Interval ms);\n\n    /** Returns the wide-FM (broadcast) channel index. */\n    virtual unsigned wfmChannelIndex() const;\n    /** Sets the wide-FM (broadcast) channel index. */\n    virtual void setWFMChannelIndex(unsigned idx);\n    /** Returns @c true if the WFM RX is in VFO mode. */\n    virtual bool wfmVFOEnabled() const;\n    /** Enables/disables VFO mode for WFM RX. */\n    virtual void enableWFMVFO(bool enable);\n\n    Interval backlightDuration() const override;\n    void setBacklightDuration(Interval intv) override;\n\n    /** Returns the DTMF tone duration in ms. */\n    virtual unsigned dtmfToneDuration() const;\n    /** Sets the DTMF tone duration in ms. */\n    virtual void setDTMFToneDuration(unsigned ms);\n\n    /** Returns @c true if \"man down\" is enabled. */\n    virtual bool manDown() const;\n    /** Enables/disables \"man down\". */\n    virtual void enableManDown(bool enable);\n\n    /** Returns @c true if WFM monitor is enabled. */\n    virtual bool wfmMonitor() const;\n    /** Enables/disables WFM monitor. */\n    virtual void enableWFMMonitor(bool enable);\n\n    /** Returns the TBST frequency. */\n    virtual Frequency tbstFrequency() const;\n    /** Sets the TBST frequency. */\n    virtual void setTBSTFrequency(Frequency freq);\n\n    /** Returns @c true if the \"pro mode\" is enabled. */\n    virtual bool proMode() const;\n    /** Enables/disables the \"pro mode\". */\n    virtual void enableProMode(bool enable);\n\n    /** Returns @c true if the own ID is filtered in call lists. */\n    virtual bool filterOwnID() const;\n    /** Enables/disables filter of own ID in call lists. */\n    virtual void enableFilterOwnID(bool enable);\n    /** Returns @c true remote stun/kill is enabled. */\n    virtual bool remoteStunKill() const;\n    /** Enables/disables remote stun/kill. */\n    virtual void enableRemoteStunKill(bool enable);\n    /** Returns @c true remote monitor is enabled. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables remote monitor. */\n    virtual void enableRemoteMonitor(bool enable);\n\n    /** Returns the monitor slot match. */\n    virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const;\n    /** Sets the monitor slot match. */\n    virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match);\n    /** Returns @c true if the monitor matches color code. */\n    virtual bool monitorColorCodeMatch() const;\n    /** Enables/disables monitor color code match. */\n    virtual void enableMonitorColorCodeMatch(bool enable);\n    /** Returns @c true if the monitor matches ID. */\n    virtual bool monitorIDMatch() const;\n    /** Enables/disables monitor ID match. */\n    virtual void enableMonitorIDMatch(bool enable);\n    /** Returns @c true if the monitor holds the time slot. */\n    virtual bool monitorTimeSlotHold() const;\n    /** Enables/disables monitor time slot hold. */\n    virtual void enableMonitorTimeSlotHold(bool enable);\n\n    /** Returns the \"man down\" delay in seconds. */\n    virtual Interval manDownDelay() const;\n    /** Sets the \"man down\" delay in seconds. */\n    virtual void setManDownDelay(Interval sec);\n    /** Returns the analog call hold in seconds. */\n    virtual unsigned fmCallHold() const;\n    /** Sets the analog call hold in seconds. */\n    virtual void setFMCallHold(unsigned sec);\n\n    /** Returns @c true if the GPS range reporting is enabled. */\n    virtual bool gpsMessageEnabled() const;\n    /** Enables/disables GPS range reporting. */\n    virtual void enableGPSMessage(bool enable);\n\n    /** Returns @c true if the call channel is maintained. */\n    virtual bool maintainCallChannel() const;\n    /** Enables/disables maintaining the call channel. */\n    virtual void enableMaintainCallChannel(bool enable);\n\n    /** Returns the priority Zone A index. */\n    virtual unsigned priorityZoneAIndex() const;\n    /** Sets the priority zone A index. */\n    virtual void setPriorityZoneAIndex(unsigned idx);\n    /** Returns the priority Zone B index. */\n    virtual unsigned priorityZoneBIndex() const;\n    /** Sets the priority zone B index. */\n    virtual void setPriorityZoneBIndex(unsigned idx);\n\n    /** Returns @c true if bluetooth is enabled. */\n    virtual bool bluetooth() const;\n    /** Enables/disables bluetooth. */\n    virtual void enableBluetooth(bool enable);\n\n    /** Returns @c true if the internal mic is additionally active when BT is active. */\n    virtual bool btAndInternalMic() const;\n    /** Enables/disables the internal mic when BT is active. */\n    virtual void enableBTAndInternalMic(bool enable);\n\n    /** Returns @c true if the internal speaker is additionally active when BT is active. */\n    virtual bool btAndInternalSpeaker() const;\n    /** Enables/disables the internal speaker when BT is active. */\n    virtual void enableBTAndInternalSpeaker(bool enable);\n\n    /** Returns @c true if the plug-in record tone is enabled. */\n    virtual bool pluginRecTone() const;\n    /** Enables/disables the plug-in record tone. */\n    virtual void enablePluginRecTone(bool enable);\n\n    /** Returns the GPS ranging interval in seconds. */\n    virtual Interval gpsUpdatePeriod() const override;\n    /** Sets the GPS ranging interval in seconds. */\n    virtual void setGPSUpdatePeriod(Interval sec) override;\n\n    /** Returns the bluetooth microphone gain [1,10]. */\n    virtual unsigned int btMicGain() const;\n    /** Sets the bluetooth microphone gain [1,10]. */\n    virtual void setBTMicGain(unsigned int gain);\n    /** Returns the bluetooth speaker gain [1,10]. */\n    virtual unsigned int btSpeakerGain() const;\n    /** Sets the bluetooth speaker gain [1,10]. */\n    virtual void setBTSpeakerGain(unsigned int gain);\n\n    /** Returns @c true if the channel number is displayed. */\n    virtual bool displayChannelNumber() const;\n    /** Enables/disables display of channel number. */\n    virtual void enableDisplayChannelNumber(bool enable);\n\n    bool showCurrentContact() const override;\n    void enableShowCurrentContact(bool enable) override;\n\n    /** Returns the auto roaming period in minutes. */\n    virtual Interval autoRoamPeriod() const;\n    /** Sets the auto roaming period in minutes. */\n    virtual void setAutoRoamPeriod(Interval min);\n\n    bool keyToneLevelAdjustable() const override;\n    Level keyToneLevel() const override;\n    void setKeyToneLevel(Level level) override;\n    void setKeyToneLevelAdjustable() override;\n\n    AnytoneDisplaySettingsExtension::Color callDisplayColor() const override;\n    void setCallDisplayColor(AnytoneDisplaySettingsExtension::Color color) override;\n\n    bool gpsUnitsImperial() const override;\n    void enableGPSUnitsImperial(bool enable) override;\n\n    bool knobLock() const override;\n    void enableKnobLock(bool enable) override;\n    bool keypadLock() const override;\n    void enableKeypadLock(bool enable) override;\n    bool sidekeysLock() const override;\n    void enableSidekeysLock(bool enable) override;\n    bool keyLockForced() const override;\n    void enableKeyLockForced(bool enable) override;\n\n    /** Returns the auto-roam delay in seconds. */\n    virtual Interval autoRoamDelay() const;\n    /** Sets the auto-roam delay in seconds. */\n    virtual void setAutoRoamDelay(Interval sec);\n\n    /** Returns the standby text color. */\n    virtual AnytoneDisplaySettingsExtension::Color standbyTextColor() const;\n    /** Sets the standby text color. */\n    virtual void setStandbyTextColor(AnytoneDisplaySettingsExtension::Color color);\n    /** Returns the standby background image. */\n    virtual BackgroundImage standbyBackgroundImage() const;\n    /** Sets the standby background image. */\n    virtual void setStandbyBackgroundImage(D878UVCodeplug::GeneralSettingsElement::BackgroundImage img);\n\n    bool showLastHeard() const override;\n    void enableShowLastHeard(bool enable) override;\n\n    /** Returns the SMS format. */\n    virtual SMSExtension::Format smsFormat() const;\n    /** Sets the SMS format. */\n    virtual void setSMSFormat(SMSExtension::Format fmt);\n\n    /** Returns the minimum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual Frequency autoRepeaterMinFrequencyVHF() const override;\n    /** Sets the minimum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual void setAutoRepeaterMinFrequencyVHF(Frequency Hz) override;\n    /** Returns the maximum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual Frequency autoRepeaterMaxFrequencyVHF() const override;\n    /** Sets the maximum frequency in Hz for the auto-repeater range in VHF band. */\n    virtual void setAutoRepeaterMaxFrequencyVHF(Frequency Hz) override;\n\n    /** Returns the minimum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual Frequency autoRepeaterMinFrequencyUHF() const override;\n    /** Sets the minimum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual void setAutoRepeaterMinFrequencyUHF(Frequency Hz) override;\n    /** Returns the maximum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual Frequency autoRepeaterMaxFrequencyUHF() const override;\n    /** Sets the maximum frequency in Hz for the auto-repeater range in UHF band. */\n    virtual void setAutoRepeaterMaxFrequencyUHF(Frequency Hz) override;\n\n    /** Returns the auto-repeater direction for VFO B. */\n    virtual AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const override;\n    /** Sets the auto-repeater direction for VFO B. */\n    virtual void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) override;\n\n    /** If enabled, the FM ID is sent together with selected contact. */\n    virtual bool fmSendIDAndContact() const;\n    /** Enables/disables sending contact with FM ID. */\n    virtual void enableFMSendIDAndContact(bool enable);\n\n    bool defaultChannel() const override;\n    void enableDefaultChannel(bool enable) override;\n    unsigned defaultZoneIndexA() const override;\n    void setDefaultZoneIndexA(unsigned idx) override;\n    unsigned defaultZoneIndexB() const override;\n    void setDefaultZoneIndexB(unsigned idx) override;\n    bool defaultChannelAIsVFO() const override;\n    unsigned defaultChannelAIndex() const override;\n    void setDefaultChannelAIndex(unsigned idx) override;\n    void setDefaultChannelAToVFO() override;\n    bool defaultChannelBIsVFO() const override;\n    unsigned defaultChannelBIndex() const override;\n    void setDefaultChannelBIndex(unsigned idx) override;\n    void setDefaultChannelBToVFO() override;\n\n    /** Returns the default roaming zone index. */\n    virtual unsigned defaultRoamingZoneIndex() const;\n    /** Sets the default roaming zone index. */\n    virtual void setDefaultRoamingZoneIndex(unsigned idx);\n\n    /** Returns @c true if repeater range check is enabled. */\n    virtual bool repeaterRangeCheck() const;\n    /** Enables/disables repeater range check. */\n    virtual void enableRepeaterRangeCheck(bool enable);\n    /** Returns the repeater range check period in seconds. */\n    virtual Interval repeaterRangeCheckInterval() const;\n    /** Sets the repeater range check interval in seconds. */\n    virtual void setRepeaterRangeCheckInterval(Interval sec);\n    /** Returns the number of repeater range checks. */\n    virtual unsigned repeaterRangeCheckCount() const;\n    /** Sets the number of repeater range checks. */\n    virtual void setRepeaterRangeCheckCount(unsigned n);\n\n    /** Returns the roaming start condition. */\n    virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const;\n    /** Sets the roaming start condition. */\n    virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond);\n\n    /** Returns the backlight duration during TX in seconds. */\n    virtual Interval txBacklightDuration() const;\n    /** Sets the backlight duration during TX in seconds. */\n    virtual void setTXBacklightDuration(Interval sec);\n\n    /** Returns @c true if the \"separate display\" is enabled. */\n    virtual bool separateDisplay() const;\n    /** Enables/disables \"separate display. */\n    virtual void enableSeparateDisplay(bool enable);\n\n    bool keepLastCaller() const override;\n    void enableKeepLastCaller(bool enable) override;\n\n    /** Returns the channel name color. */\n    virtual AnytoneDisplaySettingsExtension::Color channelNameColor() const;\n    /** Sets the channel name color. */\n    virtual void setChannelNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns @c true if repeater check notification is enabled. */\n    virtual bool repeaterCheckNotification() const;\n    /** Enables/disables repeater check notification. */\n    virtual void enableRepeaterCheckNotification(bool enable);\n\n    /** Returns the backlight duration during RX in seconds. */\n    Interval rxBacklightDuration() const;\n    /** Sets the backlight duration during RX in seconds. */\n    void setRXBacklightDuration(Interval sec);\n\n    /** Returns @c true if roaming is enabled. */\n    virtual bool roaming() const;\n    /** Enables/disables repeater check notification. */\n    virtual void enableRoaming(bool enable);\n\n    /** Returns the mute delay in minutes. */\n    virtual Interval muteDelay() const;\n    /** Sets the mute delay in minutes. */\n    virtual void setMuteDelay(Interval min);\n\n    /** Returns the number of repeater check notifications. */\n    virtual unsigned repeaterCheckNumNotifications() const;\n    /** Sets the number of repeater check notifications. */\n    virtual void setRepeaterCheckNumNotifications(unsigned num);\n\n    /** Returns @c true if boot GPS check is enabled. */\n    virtual bool bootGPSCheck() const;\n    /** Enables/disables boot GPS check. */\n    virtual void enableBootGPSCheck(bool enable);\n    /** Returns @c true if boot reset is enabled. */\n    virtual bool bootReset() const;\n    /** Enables/disables boot reset. */\n    virtual void enableBootReset(bool enable);\n\n    /** Returns @c true, if the bluetooth hold time is enabled. */\n    virtual bool btHoldTimeEnabled() const;\n    /** Returns @c true, if the bluetooth hold time is infinite. */\n    virtual bool btHoldTimeInfinite() const;\n    /** Returns the bluetooth hold time. */\n    virtual Interval btHoldTime() const;\n    /** Sets the Bluetooth hold time (1-120s). */\n    virtual void setBTHoldTime(Interval interval);\n    /** Sets the Bluetooth hold time to infinite. */\n    virtual void setBTHoldTimeInfinite();\n    /** Sets the Bluetooth hold time to infinite. */\n    virtual void disableBTHoldTime();\n\n    /** Returns the bluetooth RX delay in ms. */\n    virtual Interval btRXDelay() const;\n    /** Sets the bluetooth RX delay in ms. */\n    virtual void setBTRXDelay(Interval delay);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) override;\n    bool updateConfig(Context &ctx, const ErrorStack &err) override;\n    bool linkSettings(RadioSettings *settings, Context &ctx, const ErrorStack &err) override;\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: D868UVCodeplug::GeneralSettingsElement::Limit {\n      // Valid repeater-out-of-range notification counts.\n      static constexpr Range<unsigned int> repeaterOORNotificationCount() { return {1, 10}; }\n    };\n\n  protected:\n    /** Some internal used offsets within the element. */\n    struct Offset: public D868UVCodeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int transmitTimeout()     { return 0x0004; }\n      static constexpr unsigned int language()            { return 0x0005; }\n      static constexpr unsigned int vfoFrequencyStep()    { return 0x0008; }\n      static constexpr unsigned int steType()             { return 0x0017; }\n      static constexpr unsigned int steFrequency()        { return 0x0018; }\n      static constexpr unsigned int groupCallHangTime()   { return 0x0019; }\n      static constexpr unsigned int privateCallHangTime() { return 0x001a; }\n      static constexpr unsigned int preWaveDelay()        { return 0x001b; }\n      static constexpr unsigned int wakeHeadPeriod()      { return 0x001c; }\n      static constexpr unsigned int wfmChannelIndex()     { return 0x001d; }\n      static constexpr unsigned int wfmVFOEnabled()       { return 0x001e; }\n      static constexpr unsigned int dtmfToneDuration()    { return 0x0023; }\n      static constexpr unsigned int manDown()             { return 0x0024; }\n      static constexpr unsigned int wfmMonitor()          { return 0x002b; }\n      static constexpr unsigned int tbstFrequency()       { return 0x002e; }\n      static constexpr unsigned int proMode()             { return 0x0034; }\n      static constexpr unsigned int filterOwnID()         { return 0x0038; }\n      static constexpr unsigned int remoteStunKill()      { return 0x003c; }\n      static constexpr unsigned int remoteMonitor()       { return 0x003e; }\n      static constexpr unsigned int monSlotMatch()        { return 0x0049; }\n      static constexpr unsigned int monColorCodeMatch()   { return 0x004a; }\n      static constexpr unsigned int monIDMatch()          { return 0x004b; }\n      static constexpr unsigned int monTimeSlotHold()     { return 0x004c; }\n      static constexpr unsigned int manDownDelay()        { return 0x004f; }\n      static constexpr unsigned int fmCallHold()          { return 0x0050; }\n      static constexpr unsigned int enableGPSMessage()    { return 0x0053; }\n      static constexpr unsigned int maintainCallChannel() { return 0x006e; }\n      static constexpr unsigned int priorityZoneA()       { return 0x006f; }\n      static constexpr unsigned int priorityZoneB()       { return 0x0070; }\n      static constexpr unsigned int bluetooth()           { return 0x00b1; }\n      static constexpr unsigned int btAndInternalMic()    { return 0x00b2; }\n      static constexpr unsigned int btAndInternalSpeaker(){ return 0x00b3; }\n      static constexpr unsigned int pluginRecTone()       { return 0x00b4; }\n      static constexpr unsigned int gpsRangingInterval()  { return 0x00b5; }\n      static constexpr unsigned int btMicGain()           { return 0x00b6; }\n      static constexpr unsigned int btSpeakerGain()       { return 0x00b7; }\n      static constexpr unsigned int showChannelNumber()   { return 0x00b8; }\n      static constexpr unsigned int showCurrentContact()  { return 0x00b9; }\n      static constexpr unsigned int autoRoamPeriod()      { return 0x00ba; }\n      static constexpr unsigned int keyToneLevel()        { return 0x00bb; }\n      static constexpr unsigned int callColor()           { return 0x00bc; }\n      static constexpr unsigned int gpsUnits()            { return 0x00bd; }\n      static constexpr unsigned int knobLock()            { return 0x00be; }\n      static constexpr unsigned int keypadLock()          { return 0x00be; }\n      static constexpr unsigned int sideKeyLock()         { return 0x00be; }\n      static constexpr unsigned int forceKeyLock()        { return 0x00be; }\n      static constexpr unsigned int autoRoamDelay()       { return 0x00bf; }\n      static constexpr unsigned int standbyTextColor()    { return 0x00c0; }\n      static constexpr unsigned int standbyBackground()   { return 0x00c1; }\n      static constexpr unsigned int showLastHeard()       { return 0x00c2; }\n      static constexpr unsigned int smsFormat()           { return 0x00c3; }\n      static constexpr unsigned int autoRepMinVHF()       { return 0x00c4; }\n      static constexpr unsigned int autoRepMaxVHF()       { return 0x00c8; }\n      static constexpr unsigned int autoRepMinUHF()       { return 0x00cc; }\n      static constexpr unsigned int autoRepMaxUHF()       { return 0x00d0; }\n      static constexpr unsigned int autoRepeaterDirB()    { return 0x00d4; }\n      static constexpr unsigned int fmSendIDAndContact()  { return 0x00d5; }\n      static constexpr unsigned int defaultChannels()     { return 0x00d7; }\n      static constexpr unsigned int defaultZoneA()        { return 0x00d8; }\n      static constexpr unsigned int defaultZoneB()        { return 0x00d9; }\n      static constexpr unsigned int defaultChannelA()     { return 0x00da; }\n      static constexpr unsigned int defaultChannelB()     { return 0x00db; }\n      static constexpr unsigned int defaultRoamingZone()  { return 0x00dc; }\n      static constexpr unsigned int repRangeCheck()       { return 0x00dd; }\n      static constexpr unsigned int rangeCheckInterval()  { return 0x00de; }\n      static constexpr unsigned int rangeCheckCount()     { return 0x00df; }\n      static constexpr unsigned int roamStartCondition()  { return 0x00e0; }\n      static constexpr unsigned int txBacklightDuration() { return 0x00e1; }\n      static constexpr unsigned int displaySeparator()    { return 0x00e2; }\n      static constexpr unsigned int keepLastCaller()      { return 0x00e3; }\n      static constexpr unsigned int channelNameColor()    { return 0x00e4; }\n      static constexpr unsigned int repCheckNotify()      { return 0x00e5; }\n      static constexpr unsigned int rxBacklightDuration() { return 0x00e6; }\n      static constexpr unsigned int roaming()             { return 0x00e7; }\n      static constexpr unsigned int muteDelay()           { return 0x00e9; }\n      static constexpr unsigned int repCheckNumNotify()   { return 0x00ea; }\n      static constexpr unsigned int bootGPSCheck()        { return 0x00eb; }\n      static constexpr unsigned int bootReset()           { return 0x00ec; }\n      static constexpr unsigned int btHoldTime()          { return 0x00ed; }\n      static constexpr unsigned int btRXDelay()           { return 0x00ee; }\n      /// @endcond\n    };\n  };\n\n  /** General settings extension element for the D878UV.\n   *\n   * Memory representation of the encoded settings element (size 0x200 bytes):\n   * @verbinclude d878uv_generalsettingsextension.txt */\n  class ExtendedSettingsElement: public AnytoneCodeplug::ExtendedSettingsElement\n  {\n  protected:\n    /** Encoding of possible GNSSs. Could someone explain those AnyTone engineers the concept of\n     * flags? */\n    enum class GNSS {\n      GPS = 0, Beidou = 1, GPS_Beidou = 2, Glonass = 3, GPS_Glonass = 4, Beidou_Glonass = 5,\n      GPS_Beidou_Glonass = 6\n    };\n\n    /** Talker alias encoding. */\n    enum class TalkerAliasEncoding {\n      ISO8 = 0, ISO7 = 1, Unicode = 2,\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    ExtendedSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ExtendedSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00000200; }\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns @c true if the talker alias is sent. */\n    virtual bool sendTalkerAlias() const;\n    /** Enables/disables sending the talker alias. */\n    virtual void enableSendTalkerAlias(bool enable);\n\n    /** Returns the talker alias source. */\n    virtual AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const;\n    /** Sets the talker alias source. */\n    virtual void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode);\n\n    /** Returns the talker alias encoding. */\n    virtual DMRSettings::TalkerAliasEncoding talkerAliasEncoding() const;\n    /** Sets the talker alias encoding. */\n    virtual void setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding encoding);\n\n    /** Returns @c true if the BT PTT latch is enabled. */\n    virtual bool bluetoothPTTLatch() const;\n    /** Enables/disables bluetooth PTT latch. */\n    virtual void enableBluetoothPTTLatch(bool enable);\n\n    /** Returns @c true if the bluetooth PTT sleep delay is disabled (infinite). */\n    virtual bool infiniteBluetoothPTTSleepDelay() const;\n    /** Returns the bluetooth PTT sleep delay in minutes, 0=off. */\n    virtual Interval bluetoothPTTSleepDelay() const;\n    /** Sets the bluetooth PTT sleep delay in minutes. */\n    virtual void setBluetoothPTTSleepDelay(Interval delay);\n    /** Sets the bluetooth PTT sleep delay to infinite/disabled. */\n    virtual void setInfiniteBluetoothPTTSleepDelay();\n\n    /** Returns @c true if the auto repeater UHF 2 offset index is set. */\n    virtual bool hasAutoRepeaterUHF2OffsetIndex() const;\n    /** Returns the index of the UHF 2 offset frequency. */\n    virtual unsigned autoRepeaterUHF2OffsetIndex() const;\n    /** Sets the index of the UHF 2 offset frequency. */\n    virtual void setAutoRepeaterUHF2OffsetIndex(unsigned idx);\n    /** Clears the auto repeater UHF 2 offset frequency index. */\n    virtual void clearAutoRepeaterUHF2OffsetIndex();\n\n    /** Returns @c true if the auto repeater VHF 2 offset index is set. */\n    virtual bool hasAutoRepeaterVHF2OffsetIndex() const;\n    /** Returns the index of the VHF 2 offset frequency. */\n    virtual unsigned autoRepeaterVHF2OffsetIndex() const;\n    /** Sets the index of the VHF 2 offset frequency. */\n    virtual void setAutoRepeaterVHF2OffsetIndex(unsigned idx);\n    /** Clears the auto repeater VHF 2 offset frequency index. */\n    virtual void clearAutoRepeaterVHF2OffsetIndex();\n\n    /** Returns the minimum frequency in Hz for the auto-repeater VHF 2 band. */\n    virtual Frequency autoRepeaterVHF2MinFrequency() const;\n    /** Sets the minimum frequency in Hz for the auto-repeater VHF 2 band. */\n    virtual void setAutoRepeaterVHF2MinFrequency(Frequency hz);\n    /** Returns the maximum frequency in Hz for the auto-repeater VHF 2 band. */\n    virtual Frequency autoRepeaterVHF2MaxFrequency() const;\n    /** Sets the maximum frequency in Hz for the auto-repeater VHF 2 band. */\n    virtual void setAutoRepeaterVHF2MaxFrequency(Frequency hz);\n    /** Returns the minimum frequency in Hz for the auto-repeater UHF 2 band. */\n    virtual Frequency autoRepeaterUHF2MinFrequency() const;\n    /** Sets the minimum frequency in Hz for the auto-repeater UHF 2 band. */\n    virtual void setAutoRepeaterUHF2MinFrequency(Frequency hz);\n    /** Returns the maximum frequency in Hz for the auto-repeater UHF 2 band. */\n    virtual Frequency autoRepeaterUHF2MaxFrequency() const;\n    /** Sets the maximum frequency in Hz for the auto-repeater UHF 2 band. */\n    virtual void setAutoRepeaterUHF2MaxFrequency(Frequency hz);\n\n    /** Returns the GPS mode. */\n    virtual GNSSSettings::Systems gnss() const;\n    /** Sets the GPS mode. */\n    virtual void setGNSS(GNSSSettings::Systems mode);\n\n    /** Returns the STE (squelch tail elimination) duration. */\n    virtual Interval steDuration() const;\n    /** Sets the STE (squelch tail elimination) duration. */\n    virtual void setSTEDuration(Interval dur);\n\n    /** Returns @c true if the manual dialed group call hang time is infinite. */\n    virtual bool infiniteManDialGroupCallHangTime() const;\n    /** Returns the manual dial group call hang time. */\n    virtual Interval manDialGroupCallHangTime() const;\n    /** Sets the manual dial group call hang time. */\n    virtual void setManDialGroupCallHangTime(Interval dur);\n    /** Sets the manual dial group call hang time to infinite. */\n    virtual void setManDialGroupCallHangTimeInfinite();\n\n    /** Returns @c true if the manual dialed private call hang time is infinite. */\n    virtual bool infiniteManDialPrivateCallHangTime() const;\n    /** Returns the manual dial private call hang time. */\n    virtual Interval manDialPrivateCallHangTime() const;\n    /** Sets the manual dial private call hang time. */\n    virtual void setManDialPrivateCallHangTime(Interval dur);\n    /** Sets the manual dial private call hang time to infinite. */\n    virtual void setManDialPrivateCallHangTimeInfinite();\n\n    AnytoneDisplaySettingsExtension::Color channelBNameColor() const;\n    void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns the encryption mode. */\n    virtual AnytoneDMRSettingsExtension::EncryptionType encryption() const;\n    /** Sets the encryption mode. */\n    virtual void setEncryption(AnytoneDMRSettingsExtension::EncryptionType mode);\n\n    /** Returns @c true if the transmit timeout notification is enabled. */\n    virtual bool totNotification() const;\n    /** Enables/disables transmit timeout notification. */\n    virtual void enableTOTNotification(bool enable);\n\n    /** Returns @c true if the ATPC (Adaptiv Transmission Power Control) is enabled. */\n    virtual bool atpc() const;\n    /** Enables/disables the ATPC (Adaptiv Transmission Power Control). */\n    virtual void enableATPC(bool enable);\n\n    AnytoneDisplaySettingsExtension::Color zoneANameColor() const;\n    void setZoneANameColor(AnytoneDisplaySettingsExtension::Color color);\n    AnytoneDisplaySettingsExtension::Color zoneBNameColor() const;\n    void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns @c true if the auto-shutdown timer is reset on a call. */\n    virtual bool resetAutoShutdownOnCall() const;\n    /** Enables/disables reset on call of the auto-shutdown timer. */\n    virtual void enableResetAutoShutdownOnCall(bool enable);\n\n    /** Returns @c true if the color code is shown. */\n    virtual bool showColorCode() const;\n    /** Enables/disables display of color code. */\n    virtual void enableShowColorCode(bool enable);\n    /** Returns @c true if the time slot is shown. */\n    virtual bool showTimeSlot() const;\n    /** Enables/disables display of time slot. */\n    virtual void enableShowTimeSlot(bool enable);\n    /** Returns @c true if the channel type is shown. */\n    virtual bool showChannelType() const;\n    /** Enables/disables display of channel type. */\n    virtual void enableShowChannelType(bool enable);\n\n    /** Returns @c true if the FM idle channel tone is enabled. */\n    virtual bool fmIdleTone() const;\n    /** Enables/disables FM idle channel tone. */\n    virtual void enableFMIdleTone(bool enable);\n\n    /** Returns the date format. */\n    virtual AnytoneDisplaySettingsExtension::DateFormat dateFormat() const;\n    /** Sets the date format. */\n    virtual void setDateFormat(AnytoneDisplaySettingsExtension::DateFormat format);\n\n    /** Returns the FM Mic gain [1,10]. */\n    virtual Level fmMicGain() const;\n    /** Sets the analog mic gain [1,10]. */\n    virtual void setFMMicGain(Level gain);\n\n    /** Returns @c true if the GPS roaming is enabled. */\n    virtual bool gpsRoaming() const;\n    /** Enables/disables GPS roaming. */\n    virtual void enableGPSRoaming(bool enable);\n\n    /** Returns the call-end tone melody. */\n    virtual void callEndToneMelody(Melody &melody) const;\n    /** Sets the call-end tone melody. */\n    virtual void setCallEndToneMelody(const Melody &melody);\n    /** Returns the all-call tone melody. */\n    virtual void allCallToneMelody(Melody &melody) const;\n    /** Sets the all-call tone melody. */\n    virtual void setAllCallToneMelody(const Melody &melody);\n\n    /** Encodes the settings from the config. */\n    virtual bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err);\n    /** Update config from settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err);\n    /** Link config from settings extension. */\n    virtual bool linkConfig(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: AnytoneCodeplug::ExtendedSettingsElement::Limit {\n      static constexpr unsigned int maxBluetoothPTTSleepDelay() { return 4; }    ///< Maximum delay in minutes.\n      static constexpr Range<unsigned int> micGain() { return {0,4}; }\n    };\n\n  protected:\n    /** Internal used offset within the element. */\n    struct Offset: public AnytoneCodeplug::ExtendedSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int sendTalkerAlias()              { return 0x0000; }\n      static constexpr unsigned int talkerAliasDisplay()           { return 0x001e; }\n      static constexpr unsigned int talkerAliasEncoding()          { return 0x001f; }\n      static constexpr unsigned int btPTTLatch()                   { return 0x0020; }\n      static constexpr unsigned int autoRepeaterUHF2OffsetIndex()  { return 0x0022; }\n      static constexpr unsigned int autoRepeaterVHF2OffsetIndex()  { return 0x0023; }\n      static constexpr unsigned int autoRepeaterVHF2MinFrequency() { return 0x0024; }\n      static constexpr unsigned int autoRepeaterVHF2MaxFrequency() { return 0x0028; }\n      static constexpr unsigned int autoRepeaterUHF2MinFrequency() { return 0x002c; }\n      static constexpr unsigned int autoRepeaterUHF2MaxFrequency() { return 0x0030; }\n      static constexpr unsigned int btPTTSleepDelay()              { return 0x0034; }\n      static constexpr unsigned int gpsMode()                      { return 0x0035; }\n      static constexpr unsigned int steDuration()                  { return 0x0036; }\n      static constexpr unsigned int manGrpCallHangTime()           { return 0x0037; }\n      static constexpr unsigned int manPrivCallHangTime()          { return 0x0038; }\n      static constexpr unsigned int channelBNameColor()            { return 0x0039; }\n      static constexpr unsigned int encryptionType()               { return 0x003a; }\n      static constexpr unsigned int totNotification()              { return 0x003b; }\n      static constexpr unsigned int atpc()                         { return 0x003c; }\n      static constexpr unsigned int zoneANameColor()               { return 0x003d; }\n      static constexpr unsigned int zoneBNameColor()               { return 0x003e; }\n      static constexpr unsigned int autoShutdownMode()             { return 0x003f; }\n      static constexpr Bit displayColorCode()                      { return {0x0040, 2}; }\n      static constexpr Bit displayTimeSlot()                       { return {0x0040, 1}; }\n      static constexpr Bit displayChannelType()                    { return {0x0040, 0}; }\n      static constexpr unsigned int fmIdleTone()                   { return 0x0041; }\n      static constexpr unsigned int dateFormat()                   { return 0x0042; }\n      static constexpr unsigned int analogMicGain()                { return 0x0043; }\n      static constexpr unsigned int gpsRoaming()                   { return 0x0044; }\n      static constexpr unsigned int callEndTones()                 { return 0x0046; }\n      static constexpr unsigned int callEndDurations()             { return 0x0050; }\n      static constexpr unsigned int allCallTones()                 { return 0x005a; }\n      static constexpr unsigned int allCallDurations()             { return 0x0064; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the APRS settings within the binary D878UV codeplug.\n   *\n   * Memory layout of APRS settings (size 0x00f0 bytes):\n   * @verbinclude d878uv_aprssetting.txt\n   */\n  class APRSSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    APRSSettingsElement(uint8_t *ptr, unsigned size);\n\n    /** Possible settings for the FM APRS subtone type. */\n    enum class SignalingType {\n      Off=0, CTCSS=1, DCS=2\n    };\n\n  public:\n    /** Constructor. */\n    explicit APRSSettingsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Resets the settings. */\n    void clear();\n    bool isValid() const;\n\n    /** Returns the TX delay in ms. */\n    virtual Interval fmTXDelay() const;\n    /** Sets the TX delay in ms. */\n    virtual void setFMTXDelay(Interval ms);\n\n    /** Returns the sub tone settings. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the sub tone settings. */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns the manual TX interval in seconds. */\n    virtual Interval manualTXInterval() const;\n    /** Sets the manual TX interval in seconds. */\n    virtual void setManualTXInterval(Interval sec);\n\n    /** Returns @c true if the auto transmit is enabled. */\n    virtual bool autoTX() const;\n    /** Returns the auto TX interval in seconds. */\n    virtual Interval autoTXInterval() const;\n    /** Sets the auto TX interval in seconds. */\n    virtual void setAutoTXInterval(Interval sec);\n    /** Disables auto tx. */\n    virtual void disableAutoTX();\n\n    /** Returns @c true if a fixed location is sent. */\n    virtual bool fixedLocationEnabled() const;\n    /** Returns the fixed location send. */\n    virtual QGeoCoordinate fixedLocation() const;\n    /** Sets the fixed location to send. */\n    virtual void setFixedLocation(const QGeoCoordinate& loc);\n    /** Disables sending a fixed location. */\n    virtual void enableFixedLocation(bool enable);\n\n    /** Returns the destination call. */\n    virtual QString destination() const;\n    /** Returns the destination SSID. */\n    virtual unsigned destinationSSID() const;\n    /** Sets the destination call & SSID. */\n    virtual void setDestination(const QString &call, unsigned ssid);\n    /** Returns the source call. */\n    virtual QString source() const;\n    /** Returns the source SSID. */\n    virtual unsigned sourceSSID() const;\n    /** Sets the source call & SSID. */\n    virtual void setSource(const QString &call, unsigned ssid);\n\n    /** Returns the path string. */\n    virtual QString path() const;\n    /** Sets the path string. */\n    virtual void setPath(const QString &path);\n\n    /** Returns the APRS icon. */\n    virtual FMAPRSSystem::Icon icon() const;\n    /** Sets the APRS icon. */\n    virtual void setIcon(FMAPRSSystem::Icon icon);\n\n    /** Returns the transmit power. */\n    virtual Channel::Power power() const;\n    /** Sets the transmit power. */\n    virtual void setPower(Channel::Power power);\n\n    /** Returns the pre-wave delay in ms. */\n    virtual Interval fmPreWaveDelay() const;\n    /** Sets the pre-wave delay in ms. */\n    virtual void setFMPreWaveDelay(Interval ms);\n\n    /** Returns @c true if the channel points to the current/selected channel. */\n    virtual bool dmrChannelIsSelected(unsigned n) const;\n    /** Returns the digital channel index for the n-th system. */\n    virtual unsigned dmrChannelIndex(unsigned n) const;\n    /** Sets the digital channel index for the n-th system. */\n    virtual void setDMRChannelIndex(unsigned n, unsigned idx);\n    /** Sets the channel to the current/selected channel. */\n    virtual void setDMRChannelSelected(unsigned n);\n\n    /** Returns the destination contact for the n-th system. */\n    virtual unsigned dmrDestination(unsigned n) const;\n    /** Sets the destination contact for the n-th system. */\n    virtual void setDMRDestination(unsigned n, unsigned idx);\n\n    /** Returns the call type for the n-th system. */\n    virtual DMRContact::Type dmrCallType(unsigned n) const;\n    /** Sets the call type for the n-th system. */\n    virtual void setDMRCallType(unsigned n, DMRContact::Type type);\n\n    /** Returns @c true if the n-th system overrides the channel time-slot. */\n    virtual bool dmrTimeSlotOverride(unsigned n);\n    /** Returns the time slot if overridden (only valid if @c timeSlot returns true). */\n    virtual DMRChannel::TimeSlot dmrTimeSlot(unsigned n) const;\n    /** Overrides the time slot of the n-th selected channel. */\n    virtual void setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts);\n    /** Clears the time-slot override. */\n    virtual void clearDMRTimeSlotOverride(unsigned n);\n\n    /** Returns @c true if the roaming is enabled. */\n    virtual bool dmrRoaming() const;\n    /** Enables/disables roaming. */\n    virtual void enableDMRRoaming(bool enable);\n\n    /** Returns the the repeater activation delay in ms. */\n    virtual Interval dmrPreWaveDelay() const;\n    /** Sets the repeater activation delay in ms. */\n    virtual void setDMRPreWaveDelay(Interval ms);\n\n    /** Returns @c true if a received APRS message is shown indefinitely. */\n    virtual bool infiniteDisplayTime() const;\n    /** Returns the time, a received APRS message is shown. */\n    virtual Interval displayTime() const;\n    /** Sets the time, a received APRS is shown. */\n    virtual void setDisplayTime(Interval dur);\n    /** Sets the APRS display time to infinite. */\n    virtual void setDisplayTimeInifinite();\n\n    /** Returns the FM APRS channel width. */\n    virtual AnytoneFMAPRSSettingsExtension::Bandwidth fmChannelWidth() const;\n    /** Sets the FM APRS channel width. */\n    virtual void setFMChannelWidth(AnytoneFMAPRSSettingsExtension::Bandwidth width);\n\n    /** Returns @c true if the CRC check on received FM APRS messages is disabled. */\n    virtual bool fmPassAll() const;\n    /** Enables/disables \"pass all\", that is the CRC check on FM APRS messages is disabled. */\n    virtual void enableFMPassAll(bool enable);\n\n    /** Returns @c true if the n-th of 8 FM APRS frequencies is set. */\n    virtual bool fmFrequencySet(unsigned int n) const;\n    /** Returns the n-th of 8 FM APRS frequencies. */\n    virtual Frequency fmFrequency(unsigned int n) const;\n    /** Sets the n-th of 8 FM APRS frequencies. */\n    virtual void setFMFrequency(unsigned int n, Frequency f);\n    /** Clears the n-th of 8 FM APRS frequencies. */\n    virtual void clearFMFrequency(unsigned int n);\n\n    /** Returns @c true if the report position flag is set. */\n    virtual bool reportPosition() const;\n    /** Enables/disables report position flag. */\n    virtual void enableReportPosition(bool enable);\n    /** Returns @c true if the report Mic-E flag is set. */\n    virtual bool reportMicE() const;\n    /** Enables/disables report Mic-E flag. */\n    virtual void enableReportMicE(bool enable);\n    /** Returns @c true if the report object flag is set. */\n    virtual bool reportObject() const;\n    /** Enables/disables report object flag. */\n    virtual void enableReportObject(bool enable);\n    /** Returns @c true if the report item flag is set. */\n    virtual bool reportItem() const;\n    /** Enables/disables report item flag. */\n    virtual void enableReportItem(bool enable);\n    /** Returns @c true if the report message flag is set. */\n    virtual bool reportMessage() const;\n    /** Enables/disables report message flag. */\n    virtual void enableReportMessage(bool enable);\n    /** Returns @c true if the report weather flag is set. */\n    virtual bool reportWeather() const;\n    /** Enables/disables report weather flag. */\n    virtual void enableReportWeather(bool enable);\n    /** Returns @c true if the report NMEA flag is set. */\n    virtual bool reportNMEA() const;\n    /** Enables/disables report NMEA flag. */\n    virtual void enableReportNMEA(bool enable);\n    /** Returns @c true if the report status flag is set. */\n    virtual bool reportStatus() const;\n    /** Enables/disables report status flag. */\n    virtual void enableReportStatus(bool enable);\n    /** Returns @c true if the report other flag is set. */\n    virtual bool reportOther() const;\n    /** Enables/disables report other flag. */\n    virtual void enableReportOther(bool enable);\n\n    /** Encodes global APRS settings. */\n    virtual bool fromConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Updates global APRS settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n\n    /** Configures this APRS system from the given generic config. */\n    virtual bool fromFMAPRSSystem(const FMAPRSSystem *sys, Context &ctx,\n                                  const ErrorStack &err=ErrorStack());\n    /** Constructs a generic APRS system configuration from this APRS system. */\n    virtual FMAPRSSystem *toFMAPRSSystem(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the transmit channel within the generic APRS system based on the transmit frequency\n     * defined within this APRS system. */\n    virtual bool linkFMAPRSSystem(FMAPRSSystem *sys, Context &ctx);\n\n    /** Constructs all GPS system from the generic configuration. */\n    virtual bool fromDMRAPRSSystems(Context &ctx);\n    /** Encodes the given GPS system. */\n    virtual bool fromDMRAPRSSystemObj(unsigned int idx, DMRAPRSSystem *sys, Context &ctx);\n    /** Constructs a generic GPS system from the idx-th encoded GPS system. */\n    virtual DMRAPRSSystem *toDMRAPRSSystemObj(int idx) const;\n    /** Links the specified generic GPS system. */\n    virtual bool linkDMRAPRSSystem(int idx, DMRAPRSSystem *sys, Context &ctx) const;\n\n  public:\n    /** Some static limits for this element. */\n    struct Limit {\n      /// Maximum length of call signs.\n      static constexpr unsigned int callLength()                           { return 0x0006; }\n      /// Maximum length of the repeater path string.\n      static constexpr unsigned int pathLength()                           { return 0x0020; }\n      /// Maximum number of DMR APRS systems.\n      static constexpr unsigned int dmrSystems()                           { return 0x0008; }\n      /// Maximum number of FM APRS frequencies.\n      static constexpr unsigned int fmFrequencies()                        { return 0x0008; }\n    };\n\n  protected:\n    /** Internal used offsets within the codeplug element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int fmTXDelay()                            { return 0x0005; }\n      static constexpr unsigned int fmSigType()                            { return 0x0006; }\n      static constexpr unsigned int fmCTCSS()                              { return 0x0007; }\n      static constexpr unsigned int fmDCS()                                { return 0x0008; }\n      static constexpr unsigned int manualTXInterval()                     { return 0x000a; }\n      static constexpr unsigned int autoTXInterval()                       { return 0x000b; }\n      static constexpr unsigned int fmTXMonitor()                          { return 0x000c; }\n      static constexpr unsigned int fixedLocation()                        { return 0x000d; }\n      static constexpr unsigned int fixedLatDeg()                          { return 0x000e; }\n      static constexpr unsigned int fixedLatMin()                          { return 0x000f; }\n      static constexpr unsigned int fixedLatSec()                          { return 0x0010; }\n      static constexpr unsigned int fixedLatSouth()                        { return 0x0011; }\n      static constexpr unsigned int fixedLonDeg()                          { return 0x0012; }\n      static constexpr unsigned int fixedLonMin()                          { return 0x0013; }\n      static constexpr unsigned int fixedLonSec()                          { return 0x0014; }\n      static constexpr unsigned int fixedLonWest()                         { return 0x0015; }\n      static constexpr unsigned int destinationCall()                      { return 0x0016; }\n      static constexpr unsigned int destinationSSID()                      { return 0x001c; }\n      static constexpr unsigned int sourceCall()                           { return 0x001d; }\n      static constexpr unsigned int sourceSSID()                           { return 0x0023; }\n      static constexpr unsigned int path()                                 { return 0x0024; }\n      static constexpr unsigned int symbolTable()                          { return 0x0039; }\n      static constexpr unsigned int symbol()                               { return 0x003a; }\n      static constexpr unsigned int fmPower()                              { return 0x003b; }\n      static constexpr unsigned int fmPrewaveDelay()                       { return 0x003c; }\n      static constexpr unsigned int dmrChannelIndices()                    { return 0x0040; }\n      static constexpr unsigned int betweenDMRChannelIndices()             { return 0x0002; }\n      static constexpr unsigned int dmrDestinations()                      { return 0x0050; }\n      static constexpr unsigned int betweenDMRDestinations()               { return 0x0004; }\n      static constexpr unsigned int dmrCallTypes()                         { return 0x0070; }\n      static constexpr unsigned int betweenDMRCallTypes()                  { return 0x0001; }\n      static constexpr unsigned int roamingSupport()                       { return 0x0078; }\n      static constexpr unsigned int dmrTimeSlots()                         { return 0x0079; }\n      static constexpr unsigned int betweenDMRTimeSlots()                  { return 0x0001; }\n      static constexpr unsigned int dmrPrewaveDelay()                      { return 0x0081; }\n      static constexpr unsigned int displayInterval()                      { return 0x0082; }\n      static constexpr unsigned int fixedHeight()                          { return 0x00a6; }\n      static constexpr unsigned int reportPosition()                       { return 0x00a8; }\n      static constexpr unsigned int reportMicE()                           { return 0x00a8; }\n      static constexpr unsigned int reportObject()                         { return 0x00a8; }\n      static constexpr unsigned int reportItem()                           { return 0x00a8; }\n      static constexpr unsigned int reportMessage()                        { return 0x00a8; }\n      static constexpr unsigned int reportWeather()                        { return 0x00a8; }\n      static constexpr unsigned int reportNMEA()                           { return 0x00a8; }\n      static constexpr unsigned int reportStatus()                         { return 0x00a8; }\n      static constexpr unsigned int reportOther()                          { return 0x00a9; }\n      static constexpr unsigned int fmWidth()                              { return 0x00aa; }\n      static constexpr unsigned int passAll()                              { return 0x00ab; }\n      static constexpr unsigned int fmFrequencies()                        { return 0x00ac; }\n      static constexpr unsigned int betweenFMFrequencies()                 { return 0x0004; }\n\n      /// @endcond\n    };\n  };\n\n  /** Represents an (analog/FM) APRS message. */\n  class AnalogAPRSMessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AnalogAPRSMessageElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AnalogAPRSMessageElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    void clear();\n\n    /** Returns the message. */\n    virtual QString message() const;\n    /** Sets the message. */\n    virtual void setMessage(const QString &msg);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int length() { return 60; }    ///< Maximum message length.\n    };\n  };\n\n  /** Represents an analog APRS RX entry.\n   *\n   * Memory layout of analog APRS RX entry (size 0x0008 bytes):\n   * @verbinclude d878uv_aprsrxentry.txt */\n  class AnalogAPRSRXEntryElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AnalogAPRSRXEntryElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    AnalogAPRSRXEntryElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0008; }\n\n    /** Resets the entry. */\n    void clear();\n    /** Returns @c true if the APRS RX entry is valid. */\n    bool isValid() const;\n\n    /** Returns the call sign. */\n    virtual QString call() const;\n    /** Returns the SSID. */\n    virtual unsigned ssid() const;\n    /** Sets the call, SSID and enables the entry. */\n    virtual void setCall(const QString &call, unsigned ssid);\n  };\n\n  /** Implements the binary representation of a roaming channel within the codeplug.\n   *\n   * Memory layout of roaming channel (size 0x0020 bytes):\n   * @verbinclude d878uv_roamingchannel.txt */\n  class RoamingChannelElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    RoamingChannelElement(uint8_t *ptr, unsigned size);\n\n  protected:\n    /** Special values for the color code. */\n    enum ColorCodeValue {\n      Disabled = 16\n    };\n\n    /** Encoded values for the time slot. */\n    enum TimeSlotValue {\n      TS1 = 0,\n      TS2 = 1\n    };\n\n  public:\n    /** Constructor. */\n    RoamingChannelElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the roaming channel. */\n    void clear();\n\n    /** Returns the RX frequency in Hz. */\n    virtual unsigned rxFrequency() const;\n    /** Sets the RX frequency in Hz. */\n    virtual void setRXFrequency(unsigned hz);\n    /** Returns the TX frequency in Hz. */\n    virtual unsigned txFrequency() const;\n    /** Sets the TX frequency in Hz. */\n    virtual void setTXFrequency(unsigned hz);\n\n    /** Returns @c true if the color code is set. */\n    virtual bool hasColorCode() const;\n    /** Returns the color code. */\n    virtual unsigned colorCode() const;\n    /** Sets the color code. */\n    virtual void setColorCode(unsigned cc);\n    /** Disables the color code for the roaming channel. */\n    virtual void disableColorCode();\n\n    /** Returns the time slot. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns the name of the channel. */\n    virtual QString name() const;\n    /** Sets the name of the channel. */\n    virtual void setName(const QString &name);\n\n    /** Constructs a roaming channel from the given digital channel. */\n    virtual bool fromChannel(const RoamingChannel *ch);\n    /** Constructs a @c RoamingChannel instance for this roaming channel. */\n    virtual RoamingChannel *toChannel(Context &ctx);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int nameLength() { return 16; }       ///< Maximum name length.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int rxFrequency() { return 0x0000; }\n      static constexpr unsigned int txFrequency() { return 0x0004; }\n      static constexpr unsigned int colorCode()   { return 0x0008; }\n      static constexpr unsigned int timeSlot()    { return 0x0009; }\n      static constexpr unsigned int name()        { return 0x000a; }\n      /// @endcond\n    };\n  };\n\n\n  /** Represents the bitmap, indicating which roaming channel is valid. */\n  class RoamingChannelBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    RoamingChannelBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    RoamingChannelBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n  /** Represents a roaming zone within the binary codeplug.\n   *\n   * Memory layout of roaming zone (0x80byte):\n   * @verbinclude d878uv_roamingzone.txt */\n  class RoamingZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    RoamingZoneElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    RoamingZoneElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0080; }\n\n    /** Clears the roaming zone. */\n    void clear();\n\n    /** Returns @c true if the n-th member is set. */\n    virtual bool hasMember(unsigned n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned member(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMember(unsigned n, unsigned idx);\n    /** Clears the n-th member. */\n    virtual void clearMember(unsigned n);\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Assembles a binary representation of the given RoamingZone instance.*/\n    virtual bool fromRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack &err=ErrorStack());\n    /** Constructs a @c RoamingZone instance from this configuration. */\n    virtual RoamingZone *toRoamingZone(Context& ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the given RoamingZone. */\n    virtual bool linkRoamingZone(RoamingZone *zone, Context& ctx, const ErrorStack& err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int nameLength() { return 16; }          ///< Maximum name length.\n      static constexpr unsigned int numMembers() { return 64; }          ///< Maximum number of roaming channel in zone.\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members()        { return 0x0000; }\n      static constexpr unsigned int betweenMembers() { return 0x0001; }\n      static constexpr unsigned int name()           { return 0x0040; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap, indicating which roaming zone is valid. */\n  class RoamingZoneBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    RoamingZoneBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    RoamingZoneBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n  };\n\n\n  /** Represents an AES encryption key.\n   *\n   * Binary representation of a variable size AES key. The key size is between 4 and 256 bits. */\n  class AESEncryptionKeyElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AESEncryptionKeyElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    AESEncryptionKeyElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    /** Resets the key. */\n    void clear();\n\n    /** Returns @c true if the key is set. */\n    bool isValid() const;\n\n    /** Returns the key index. */\n    virtual unsigned index() const;\n    /** Sets the key index. */\n    virtual void setIndex(unsigned idx);\n\n    /** Returns the actual key. */\n    virtual QByteArray key() const;\n    /** Sets the key. */\n    virtual void setKey(const QByteArray &key);\n\n  public:\n    /** Some limits of the key element. */\n    struct Limit: public Element::Limit {\n      /// The maximum index.\n      static constexpr unsigned int maxIndex() { return 254; }\n      /// The maximum key length in bytes.\n      static constexpr unsigned int keySize() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int key()   { return 0x0001; }\n      static constexpr unsigned int size()  { return 0x0022; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap, indicating which AES key is valid. */\n  class AESEncryptionKeyBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    AESEncryptionKeyBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AESEncryptionKeyBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n\n  /** Represents an ARC4 encryption key.\n   *\n   * Encodes a 8bit ID and 40bit key. A smaller key might be encoded right-aligned. */\n  class ARC4EncryptionKeyElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ARC4EncryptionKeyElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ARC4EncryptionKeyElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the key index. */\n    virtual unsigned index() const;\n    /** Sets the key index. */\n    virtual void setIndex(unsigned idx);\n\n    /** Returns the actual key. */\n    virtual QByteArray key() const;\n    /** Sets the key. */\n    virtual void setKey(const QByteArray &key);\n\n  public:\n    /** Some limits of the key element. */\n    struct Limit: public Element::Limit {\n      /// The maximum index.\n      static constexpr unsigned int maxIndex() { return 254; }\n      /// The maximum key length in bytes.\n      static constexpr unsigned int keySize() { return 5; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int key()   { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the bitmap, indicating which ARC4 key is valid. */\n  class ARC4EncryptionKeyBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ARC4EncryptionKeyBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ARC4EncryptionKeyBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n\n  /** Encodes the bitmap, indicating which zone is hidden. */\n  class HiddenZoneBitmapElement: public BitmapElement\n  {\n  protected:\n    /** Hidden constructor. */\n    HiddenZoneBitmapElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    HiddenZoneBitmapElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n  };\n\n  /** Encodes some information about the radio and firmware.\n   *\n   * Binary encoding of the info, size 0x0100 bytes:\n   * @verbinclude d878uv_radioinfo.txt */\n  class RadioInfoElement: public Element\n  {\n  public:\n    /** Possible frequency ranges for the AT-D878UV. */\n    enum FrequencyRange {\n      RX_400_480_136_174_TX_400_480_136_174 = 0,\n      RX_400_480_136_174_TX_400_480_136_174_STEP_12_5kHz = 1,\n      RX_430_440_136_174_TX_430_440_136_174 = 2,\n      RX_400_480_136_174_TX_430_440_144_146 = 3,\n      RX_440_480_136_174_TX_440_480_136_174 = 4,\n      RX_440_480_144_146_TX_440_480_144_146 = 5,\n      RX_446_447_136_174_TX_446_447_136_174 = 6,\n      RX_400_480_136_174_TX_420_450_136_174 = 7,\n      RX_400_470_136_174_TX_400_470_136_174 = 8,\n      RX_430_432_144_146_TX_430_432_144_146 = 9,\n      RX_400_480_136_174_TX_430_450_144_148 = 10,\n      RX_400_520_136_174_TX_400_520_136_174 = 11,\n      RX_400_490_136_174_TX_400_490_136_174 = 12,\n      RX_400_480_136_174_TX_403_470_136_174 = 13,\n      RX_400_520_220_225_136_174_TX_400_520_220_225_136_174 = 14,\n      RX_420_520_144_148_TX_420_520_144_148 = 15,\n      RX_430_440_144_147_TX_430_440_144_147 = 16,\n      RX_430_440_136_174_TX_136_174 = 17\n    };\n\n  protected:\n    /** Hidden constructor. */\n    RadioInfoElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit RadioInfoElement(uint8_t *ptr);\n\n    /** Resets the info. */\n    void clear();\n\n    /** Returns @c true if full test is enabled.\n     * @warning Do not enable, may brick device! */\n    virtual bool fullTest() const;\n\n    /** Returns the frequency range. */\n    virtual FrequencyRange frequencyRange() const;\n    /** Sets the frequency range. */\n    virtual void setFrequencyRange(FrequencyRange range);\n\n    /** Returns @c true if \"international\" is enabled. */\n    virtual bool international() const;\n    /** Enables/disables \"international\". */\n    virtual void enableInternational(bool enable);\n\n    /** Returns @c true if band select is enabled. */\n    virtual bool bandSelect() const;\n    /** Enables/disables band select. */\n    virtual void enableBandSelect(bool enable);\n\n    /** Returns the band-select password. */\n    virtual QString bandSelectPassword() const;\n    /** Sets the band-select password. */\n    virtual void setBandSelectPassword(const QString &passwd);\n\n    /** Returns the radio type. */\n    virtual QString radioType() const;\n\n    /** Returns the program password. */\n    virtual QString programPassword() const;\n    /** Sets the program password. */\n    virtual void setProgramPassword(const QString &passwd);\n\n    /** Returns the area code. */\n    virtual QString areaCode() const;\n    /** Returns the serial number. */\n    virtual QString serialNumber() const;\n    /** Returns the production date. */\n    virtual QString productionDate() const;\n    /** Returns the manufacturer code. */\n    virtual QString manufacturerCode() const;\n    /** Returns the maintained date. */\n    virtual QString maintainedDate() const;\n    /** Returns the dealer code. */\n    virtual QString dealerCode() const;\n    /** Returns the stock date. */\n    virtual QString stockDate() const;\n    /** Returns the sell date. */\n    virtual QString sellDate() const;\n    /** Returns the seller. */\n    virtual QString seller() const;\n    /** Returns the maintainer note. */\n    virtual QString maintainerNote() const;\n  };\n\nprotected:\n  /** Hidden constructor. */\n  explicit D878UVCodeplug(const QString &label, QObject *parent = nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit D878UVCodeplug(QObject *parent = nullptr);\n\n  Config *preprocess(Config *config, const ErrorStack &err) const;\n\nprotected:\n  bool allocateBitmaps();\n  void setBitmaps(Context &ctx);\n  void allocateForDecoding();\n  void allocateUpdated();\n  void allocateForEncoding();\n\n  bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  virtual void allocateZones();\n  virtual bool encodeZone(int i, Zone *zone, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  virtual bool decodeZone(int i, Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateGPSSystems();\n  bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to store all roaming channels and zones. */\n  virtual void allocateRoaming();\n  /** Encodes the roaming channels and zones. */\n  virtual bool encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Creates roaming channels and zones from codeplug. */\n  virtual bool createRoaming(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links roaming channels and zones. */\n  virtual bool linkRoaming(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to encode/decode AES keys. */\n  virtual void allocateAESKeys();\n  /** Encode all AES keys. */\n  virtual bool encodeAESKeys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decode AES keys from the codeplug. */\n  virtual bool createAESKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to encode/decode ARC4 keys. */\n  virtual void allocateARC4Keys();\n  /** Encode all ARC4 keys. */\n  virtual bool encodeARC4Keys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decode ARC4 keys from the codeplug. */\n  virtual bool createARC4Keys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n\npublic:\n  /** Some limits. */\n  struct Limit: public D868UVCodeplug::Limit {\n    static constexpr unsigned int analogAPRSRXEntries() { return 32; }   ///< Maximum number of analog APRS RX entries.\n    static constexpr unsigned int roamingChannels()     { return 250; }  ///< Maximum number of roaming channels.\n    static constexpr unsigned int roamingZones()        { return 64; }   ///< Maximum number of roaming zones.\n    static constexpr unsigned int aesKeys()             { return 255; }  ///< Maximum number of AES keys.\n    static constexpr unsigned int arc4Keys()            { return 255; }  ///< Maximum number of ARC4 keys.\n  };\n\nprotected:\n  /** Internal offsets within the codeplug. */\n  struct Offset: public D868UVCodeplug::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int toChannelExtension()          { return 0x00002000; }\n    static constexpr unsigned int settingsExtension()           { return 0x02501400; }\n    static constexpr unsigned int aprsSettings()                { return 0x02501000; }\n    static constexpr unsigned int analogAPRSMessage()           { return 0x02501200; }\n    static constexpr unsigned int analogAPRSRXEntries()         { return 0x02501800; }\n    //static constexpr unsigned int fmAPRSFrequencyNames()        { return 0x02502000; }\n    static constexpr unsigned int hiddenZoneBitmap()            { return 0x024c1360; }\n    static constexpr unsigned int roamingChannelBitmap()        { return 0x01042000; }\n    static constexpr unsigned int roamingChannels()             { return 0x01040000; }\n    static constexpr unsigned int roamingZoneBitmap()           { return 0x01042080; }\n    static constexpr unsigned int roamingZones()                { return 0x01043000; }\n    static constexpr unsigned int aesKeys()                     { return 0x024C4000; }\n    static constexpr unsigned int aesKeyBitmap()                { return 0x024C8000; }\n    static constexpr unsigned int primaryId()                   { return 0x02582000; }\n    static constexpr unsigned int arc4Keys()                    { return 0x025C0C00; }\n    static constexpr unsigned int arc4KeyBitmap()               { return 0x025C1C00; }\n    /// @endcond\n  };\n};\n\n#endif // D878UVCODEPLUG_HH\n"
  },
  {
    "path": "lib/d878uv_filereader.cc",
    "content": "#include \"d878uv_filereader.hh\"\n\nD878UVFileReader::D878UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message)\n  :D868UVFileReader(config, data, size, message)\n{\n  // pass...\n}\n\n\n"
  },
  {
    "path": "lib/d878uv_filereader.hh",
    "content": "#ifndef D878UVFILEREADER_HH\n#define D878UVFILEREADER_HH\n\n#include \"d868uv_filereader.hh\"\n\n/** Implements a file read for the AnyTone D878UV manufacturer CPS file. */\nclass D878UVFileReader: public D868UVFileReader\n{\npublic:\n  /** Constructor. */\n  D878UVFileReader(Config *config, const uint8_t *data, size_t size, QString &message);\n};\n\n#endif // D878UVFILEREADER_HH\n"
  },
  {
    "path": "lib/d878uv_limits.cc",
    "content": "#include \"d878uv_limits.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include \"anytone_satelliteconfig.hh\"\n\nD878UVLimits::D878UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                           const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                           const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V101\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 200000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n            {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n            {\"password\", new RadioLimitPin(D878UVCodeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 128, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n            { \"number\", new RadioLimitString(1, 14, RadioLimitString::DTMF) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/d878uv_limits.hh",
    "content": "#ifndef D878UVLIMITS_HH\n#define D878UVLIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the AnyTone AT-D878UV.\n * @ingroup d878uv */\nclass D878UVLimits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  D878UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n               const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n               const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D878UVLIMITS_HH\n"
  },
  {
    "path": "lib/dfu_libusb.cc",
    "content": "#include \"dfu_libusb.hh\"\n#include <unistd.h>\n#include \"logger.hh\"\n#include \"utils.hh\"\n\n\n// USB request types.\n#define REQUEST_TYPE_TO_HOST    0xA1\n#define REQUEST_TYPE_TO_DEVICE  0x21\n\nenum {\n    REQUEST_DETACH      = 0,\n    REQUEST_DNLOAD      = 1,\n    REQUEST_UPLOAD      = 2,\n    REQUEST_GETSTATUS   = 3,\n    REQUEST_CLRSTATUS   = 4,\n    REQUEST_GETSTATE    = 5,\n    REQUEST_ABORT       = 6,\n};\n\nenum {\n    appIDLE                 = 0,\n    appDETACH               = 1,\n    dfuIDLE                 = 2,\n    dfuDNLOAD_SYNC          = 3,\n    dfuDNBUSY               = 4,\n    dfuDNLOAD_IDLE          = 5,\n    dfuMANIFEST_SYNC        = 6,\n    dfuMANIFEST             = 7,\n    dfuMANIFEST_WAIT_RESET  = 8,\n    dfuUPLOAD_IDLE          = 9,\n    dfuERROR                = 10,\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUDevice::Descriptor\n * ********************************************************************************************* */\nDFUDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device)\n  : USBDeviceDescriptor(info, USBDeviceHandle(bus, device))\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUDevice\n * ********************************************************************************************* */\nDFUDevice::DFUDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : QObject(parent), _ctx(nullptr), _dev(nullptr)\n{\n  if (USBDeviceInfo::Class::DFU != descr.interfaceClass()) {\n    errMsg(err) << \"Cannot connect to DFU device using a non DFU descriptor: \"\n                << descr.description() << \".\";\n    return;\n  }\n\n  int error = libusb_init(&_ctx);\n  if (error < 0) {\n    errMsg(err) << \"Libusb init failed (\" << error << \"): \"\n                << libusb_strerror((enum libusb_error) error) << \".\";\n    return;\n  }\n\n  int num=0;\n  libusb_device **lst;\n  libusb_device *dev=nullptr;\n  if (0 > (num = libusb_get_device_list(_ctx, &lst))) {\n    errMsg(err) << \"Cannot obtain list of USB devices.\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  logDebug() << \"Try to detect USB DFU interface \" << descr.description() << \".\";\n  USBDeviceHandle addr = descr.device().value<USBDeviceHandle>();\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    if (addr.bus != libusb_get_bus_number(lst[i]))\n      continue;\n    if (addr.device != libusb_get_device_address(lst[i]))\n      continue;\n    libusb_device_descriptor usb_descr;\n    if (0 > libusb_get_device_descriptor(lst[i],&usb_descr))\n      continue;\n    if (descr.vendorId() != usb_descr.idVendor)\n      continue;\n    if (descr.productId() != usb_descr.idProduct)\n      continue;\n    logDebug() << \"Matching device found at bus \" << addr.bus << \", device \" << addr.device\n               << \" with vendor ID \" << QString::number(usb_descr.idVendor, 16)\n               << \" and product ID \" << QString::number(usb_descr.idProduct, 16) << \".\";\n    libusb_ref_device(lst[i]); dev = lst[i];\n  }\n  // Unref all devices and free list, matching device was referenced earlier\n  libusb_free_device_list(lst, 1);\n\n  if (nullptr == dev) {\n    errMsg(err) << \"No matching device found: \" << descr.description() << \".\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  if (0 > (error = libusb_open(dev, &_dev))) {\n    errMsg(err) << \"Cannot open device \" << descr.description()\n                << \": \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_unref_device(dev);\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n\n  if (libusb_kernel_driver_active(_dev, 0) && libusb_detach_kernel_driver(_dev, 0)) {\n    errMsg(err) << \"Cannot detach kernel driver for device \" << descr.description()\n                << \". Interface claim will likely fail.\";\n  }\n\n  if (0 > (error = libusb_claim_interface(_dev, 0))) {\n    errMsg(err) << \"Failed to claim USB interface \"  << descr.description()\n                << \": \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_close(_dev);\n    _dev = nullptr;\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  logDebug() << \"Connected to DFU device \" << descr.description() << \".\";\n}\n\nDFUDevice::~DFUDevice() {\n  close();\n}\n\nQList<USBDeviceDescriptor>\nDFUDevice::detect(uint16_t vid, uint16_t pid)\n{\n  QList<USBDeviceDescriptor> res;\n\n  int error, num;\n  libusb_context *ctx;\n  if (0 > (error = libusb_init(&ctx))) {\n    logError() << \"Libusb init failed (\" << error << \"): \"\n               << libusb_strerror((enum libusb_error) error) << \".\";\n    return res;\n  }\n\n  libusb_device **lst;\n  if (0 == (num = libusb_get_device_list(ctx, &lst))) {\n    logDebug() << \"No USB devices found at all.\";\n    // unref devices and free list\n    libusb_free_device_list(lst, 1);\n    return res;\n  }\n\n  logDebug() << \"Search for DFU devices matching VID:PID \"\n             << QString::number(vid, 16) << \":\" << QString::number(pid, 16) << \".\";\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    libusb_device_descriptor descr;\n    libusb_get_device_descriptor(lst[i], &descr);\n    if ((vid == descr.idVendor) && (pid == descr.idProduct)) {\n      logDebug() << \"Found device on bus=\" << libusb_get_bus_number(lst[i])\n                 << \", device=\" << libusb_get_device_address(lst[i])\n                 << \" with \" << QString::number(vid, 16) << \":\" << QString::number(pid, 16) << \".\";\n      res.append(DFUDevice::Descriptor(\n                   USBDeviceInfo(USBDeviceInfo::Class::DFU, vid, pid),\n                   libusb_get_bus_number(lst[i]),\n                   libusb_get_device_address(lst[i])));\n    }\n  }\n\n  libusb_free_device_list(lst, 1);\n  return res;\n}\n\nbool\nDFUDevice::isOpen() const {\n  return nullptr != _dev;\n}\n\nvoid\nDFUDevice::close() {\n  if (nullptr != _dev) {\n    libusb_release_interface(_dev, 0);\n    libusb_close(_dev);\n  }\n  if (nullptr != _ctx)\n    libusb_exit(_ctx);\n  _ctx = nullptr;\n  _dev = nullptr;\n}\n\n\nint\nDFUDevice::download(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err) {\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_DNLOAD, block, 0, data, len, 0);\n\n  if (error < 0) {\n    errMsg(err) << \"Cannot write to device: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n\n  return get_status();\n}\n\nint\nDFUDevice::upload(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err) {\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_HOST, REQUEST_UPLOAD, block, 0, data, len, 0);\n\n  if (error < 0) {\n    errMsg(err) << \"Cannot read block: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n\n  return get_status();\n}\n\nint\nDFUDevice::detach(int timeout, const ErrorStack &err)\n{\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_DETACH, timeout, 0, nullptr, 0, 0);\n  if (0 > error) {\n    errMsg(err) << \"Cannot detach device: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n  return 0;\n}\n\nint\nDFUDevice::get_status(const ErrorStack &err)\n{\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_HOST, REQUEST_GETSTATUS, 0, 0, (unsigned char*)&_status, 6, 0);\n  if (0 > error) {\n    errMsg(err) << \"Cannot get status: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n  return 0;\n}\n\nint\nDFUDevice::clear_status(const ErrorStack &err)\n{\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_CLRSTATUS, 0, 0, NULL, 0, 0);\n  if (0 > error) {\n    errMsg(err) << \"Cannot clear status: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n  return 0;\n}\n\nint\nDFUDevice::get_state(int &pstate, const ErrorStack &err)\n{\n  unsigned char state;\n\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_HOST, REQUEST_GETSTATE, 0, 0, &state, 1, 0);\n  pstate = state;\n  if (error < 0) {\n    errMsg(err) << \"Cannot get state: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n  return 0;\n}\n\nint\nDFUDevice::abort(const ErrorStack &err)\n{\n  int error = libusb_control_transfer(\n        _dev, REQUEST_TYPE_TO_DEVICE, REQUEST_ABORT, 0, 0, NULL, 0, 0);\n  if (error < 0) {\n    errMsg(err) << \"Cannot abort: \" << libusb_strerror((enum libusb_error) error) << \".\";\n    return error;\n  }\n  return 0;\n}\n\n\nint\nDFUDevice::wait_idle(const ErrorStack &err)\n{\n  int state, error;\n\n  for (;;) {\n    if (0 > (error = get_state(state, err)))\n      return 1;\n\n    switch (state) {\n      case dfuIDLE:\n        return 0;\n\n      case appIDLE:\n        error = detach(1000, err);\n        break;\n\n      case dfuERROR:\n        error = clear_status(err);\n        break;\n\n      case appDETACH:\n      case dfuDNBUSY:\n      case dfuMANIFEST_WAIT_RESET:\n        usleep(100000);\n        continue;\n\n      default:\n        error = abort(err);\n        break;\n    }\n\n    if (error < 0)\n      return 1;\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUSEDevice\n * ********************************************************************************************* */\nDFUSEDevice::DFUSEDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, uint16_t blocksize, QObject *parent)\n  : DFUDevice(descr, err, parent), _blocksize(blocksize)\n{\n  // pass...\n}\n\nvoid\nDFUSEDevice::close() {\n  leaveDFU();\n  DFUDevice::close();\n}\n\nuint16_t\nDFUSEDevice::blocksize() const {\n  return _blocksize;\n}\n\nbool\nDFUSEDevice::setAddress(uint32_t address, const ErrorStack &err) {\n  uint8_t cmd[5] ={\n    0x21, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24)\n  };\n\n  if (int error = download(0, cmd, 5, err)) {\n    errMsg(err) << \"Cannot set address to \" << QString::number(address, 16) << \".\";\n    return error;\n  }\n\n  if (wait_idle(err)) {\n    errMsg(err) << \"Set address command failed.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUSEDevice::readBlock(unsigned block, uint8_t *data, const ErrorStack &err) {\n  return 0 == upload(block+2, data, _blocksize, err);\n}\n\nbool\nDFUSEDevice::writeBlock(unsigned block, const uint8_t *data, const ErrorStack &err) {\n  if (download(block+2, (uint8_t *)data, _blocksize, err)) {\n    return false;\n  }\n\n  if (wait_idle(err)) {\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDFUSEDevice::erasePage(uint32_t address, const ErrorStack &err) {\n  uint8_t cmd[5] ={\n    0x41, (uint8_t)address, (uint8_t)(address >> 8), (uint8_t)(address >> 16), (uint8_t)(address >> 24)\n  };\n\n  if (int error = download(0, cmd, 5, err)) {\n    errMsg(err) << \"Cannot erase page at address \" << QString::number(address, 16) << \".\";\n    return error;\n  }\n\n  if (wait_idle(err)) {\n    errMsg(err) << \"Erase page command failed.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUSEDevice::eraseAll(const ErrorStack &err) {\n  uint8_t cmd[1] ={0x41};\n\n  if (int error = download(0, cmd, 1, err)) {\n    errMsg(err) << \"Cannot erase entire memory.\";\n    return error;\n  }\n\n  if (wait_idle(err)) {\n    errMsg(err) << \"Erase memory command failed.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUSEDevice::releaseReadLock(const ErrorStack &err) {\n  uint8_t cmd[1] ={0x92};\n\n  if (int error = download(0, cmd, 1, err)) {\n    errMsg(err) << \"Cannot unlock memory.\";\n    return error;\n  }\n\n  if (wait_idle(err)) {\n    errMsg(err) << \"Unlock memory command failed.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUSEDevice::leaveDFU(const ErrorStack &err) {\n  if (int error = download(0, nullptr, 0)) {\n    errMsg(err) << \"Cannot leave DFU mode.\";\n    return error;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dfu_libusb.hh",
    "content": "#ifndef DFU_LIBUSB_HH\n#define DFU_LIBUSB_HH\n\n#include <QObject>\n#include <libusb.h>\n#include \"errorstack.hh\"\n#include \"radiointerface.hh\"\n\n/** This class implements DFU protocol to access radios.\n *\n * Many manufactures use the standardized DFU protocol to program codeplugs and update the\n * firmware of their radios. This class implements this protocol, see\n * https://www.usb.org/sites/default/files/DFU_1.1.pdf for details.\n *\n * @ingroup rif */\nclass DFUDevice: public QObject\n{\n\tQ_OBJECT\n\nprivate:\n  /** Status message from device. */\n  struct __attribute__((packed)) status_t {\n\t\tunsigned  status       : 8;\n\t\tunsigned  poll_timeout : 24;\n\t\tunsigned  state        : 8;\n\t\tunsigned  string_index : 8;\n  };\n\npublic:\n  /** Specialization to address a DFU device uniquely. */\n  class Descriptor: public USBDeviceDescriptor\n  {\n  public:\n    /** Constructor from interface info, bus number and device address. */\n    Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device);\n  };\n\npublic:\n  /** Opens a connection to the USB-DFU device at vendor @c vid and product @c pid. */\n  DFUDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n\tvirtual ~DFUDevice();\n\n  /** Returns @c true if the DFU device interface is open. */\n  bool isOpen() const;\n  /** Closes the DFU interface. */\n  void close();\n\n  /** Downloads some data to the device. */\n  int download(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err=ErrorStack());\n  /** Uploads some data from the device. */\n  int upload(unsigned block, uint8_t *data, unsigned len, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Finds all DFU interfaces with the specified VID/PID combination. */\n  static QList<USBDeviceDescriptor> detect(uint16_t vid, uint16_t pid);\n\nprotected:\n  /** Internal used function to detach the device. */\n  int detach(int timeout, const ErrorStack &err=ErrorStack());\n  /** Internal used function to read the current status. */\n  int get_status(const ErrorStack &err=ErrorStack());\n  /** Internal used function to clear the status. */\n  int clear_status(const ErrorStack &err=ErrorStack());\n  /** Internal used function to read the state. */\n  int get_state(int &pstate, const ErrorStack &err=ErrorStack());\n  /** Internal used function to abort the current operation. */\n  int abort(const ErrorStack &err=ErrorStack());\n  /** Internal used function to busy-wait for a response from the device. */\n  int wait_idle(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** USB context. */\n\tlibusb_context *_ctx;\n  /** USB device object. */\n\tlibusb_device_handle *_dev;\n  /** Device status. */\n\tstatus_t _status;\n};\n\n\n/** Implements the ST MCU extensions for the DFU protocol, aka DfuSe.\n * This class implements the extensions to the DFU protocaol used by ST for their MCUs. This is\n * also known as DfuSe.\n *\n * @ingroup rif */\nclass DFUSEDevice: public DFUDevice\n{\npublic:\n  /** Constructor, also connects to the specified VID/PID device found first. The @c blocksize\n   * specifies the blocksize for every read and write operation. */\n  DFUSEDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), uint16_t blocksize=32, QObject *parent=nullptr);\n\n  /** Closes the connection. */\n  void close();\n\n  /** Returns the blocksize in bytes. */\n  uint16_t blocksize() const;\n\n  /** Sets the read/write reference address. By default this is @c 0x08000000 (flash program\n   * memory address on ST devices). */\n  bool setAddress(uint32_t address, const ErrorStack &err=ErrorStack());\n  /** Reads a block of data from the device. The address is computed as base address +\n   * block*blocksize, where the base address is set using the @c setAddress method. */\n  bool readBlock(unsigned block, uint8_t *data, const ErrorStack &err=ErrorStack());\n  /** Writes a block of data to the device. The address is computed as base address +\n   * block*blocksize, where the base address is set using the @c setAddress method. */\n  bool writeBlock(unsigned block, const uint8_t *data, const ErrorStack &err=ErrorStack());\n  /** Erases an entire page of memory at the specified address. A page is usually 0x10000 bytes\n   * large. */\n  bool erasePage(uint32_t address, const ErrorStack &err=ErrorStack());\n  /** Erases the entire memory. Not reconmended. */\n  bool eraseAll(const ErrorStack &err=ErrorStack());\n  /** Releases the read lock. This usually also erases the entire memory. Not reconmended. */\n  bool releaseReadLock(const ErrorStack &err=ErrorStack());\n  /** Leaves the DFU mode, may boot into the application code. */\n  bool leaveDFU(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the block size in bytes. */\n  uint16_t _blocksize;\n};\n\n\n#endif // DFU_LIBUSB_HH\n"
  },
  {
    "path": "lib/dfufile.cc",
    "content": "#include \"dfufile.hh\"\n#include <QFile>\n#include <QtEndian>\n\n#include \"crc32.hh\"\n#include \"logger.hh\"\n\n\ntypedef struct __attribute((packed)) {\n  uint8_t signature[5];      ///< File signature = \"DfuSe\"\n  uint8_t version;           ///< File version = 0x01\n  uint32_t image_size;       ///< Total file size in big-endian\n  uint8_t n_targets;         ///< Number of images.\n} file_prefix_t;\n\ntypedef struct __attribute((packed)) {\n  uint16_t device_id;        ///< Device id in little endian\n  uint16_t product_id;       ///< Product id in little endian\n  uint16_t vendor_id;        ///< Vendor id in little endian\n  uint8_t DFUlo;             ///< Fixed 0x1A\n  uint8_t DFUhi;             ///< Fixed 0x01\n  uint8_t signature[3];      ///< Fixed \"UFD\", {0x44, 0x46, 0x55}\n  uint8_t size;              ///< Suffix size, fixed 16\n  uint32_t crc;              ///< CRC over compltete file excluding the CRC in little endian.\n} file_suffix_t;\n\ntypedef struct __attribute((packed)) {\n  uint8_t signature[6];      ///< Target signature, fixed \"Target\"\n  uint8_t alternate_setting; ///< Alternate setting for image.\n  uint32_t is_named;         ///< Bool, if target is named;\n  uint8_t name[255];         ///< Target name (0-padded?).\n  uint32_t size;             ///< Size of complete image excl. prefix in big endian.\n  uint32_t n_elements;       ///< Number of elements in image in big endian.\n} image_prefix_t;\n\ntypedef struct __attribute((packed)) {\n  uint32_t address;          ///< Target address of element in big endian.\n  uint32_t size;             ///< Element size in big endian;\n} element_prefix_t;\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUFile\n * ********************************************************************************************* */\nDFUFile::DFUFile(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nuint32_t\nDFUFile::size() const {\n  uint32_t size = sizeof(file_prefix_t);\n  foreach (const Image &i, _images) {\n    size += i.size();\n  }\n  return size+sizeof(file_suffix_t);\n}\n\nuint32_t\nDFUFile::memSize() const {\n  uint32_t size = 0;\n  foreach (const Image &i, _images) {\n    size += i.memSize();\n  }\n  return size;\n}\n\nint\nDFUFile::numImages() const {\n  return _images.size();\n}\n\nconst DFUFile::Image &\nDFUFile::image(int i) const {\n  return _images[i];\n}\n\nDFUFile::Image &\nDFUFile::image(int i) {\n  return _images[i];\n}\n\nvoid\nDFUFile::addImage(const QString &name, uint8_t altSettings) {\n  _images.append(Image(name, altSettings));\n}\n\nvoid\nDFUFile::addImage(const Image &img) {\n  _images.append(img);\n}\n\nvoid\nDFUFile::remImage(int i) {\n  _images.remove(i);\n}\n\nbool\nDFUFile::isAligned(unsigned blocksize) const {\n  for (int i=0; i<_images.size(); i++)\n    if (! _images.at(i).isAligned(blocksize))\n      return false;\n  return true;\n}\n\nbool\nDFUFile::read(const QString &filename, const ErrorStack &err) {\n  QFile file(filename);\n\n  if (! file.open(QIODevice::ReadOnly)) {\n    errMsg(err) << \"Cannot read DFU file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  if (! read(file, err)) {\n    file.close();\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDFUFile::read(QFile &file, const ErrorStack &err)\n{\n  CRC32 crc;\n\n  _images.clear();\n\n  file_prefix_t prefix;\n  if (sizeof(file_prefix_t) != file.read((char *)&prefix, sizeof(file_prefix_t))) {\n    errMsg(err) << \"Cannot read suffix: \" << file.errorString() << \".\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  // update crc\n  crc.update((const uint8_t *)&prefix, sizeof(file_prefix_t));\n\n  if (memcmp(prefix.signature, \"DfuSe\", 5)) {\n    errMsg(err) << \"Invalid DFU file signature. Not a DFU file?\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  uint32_t filesize = qFromLittleEndian(prefix.image_size);\n  uint8_t  n_images = prefix.n_targets;\n\n  for (uint8_t i=0; i<n_images; i++) {\n    Image img; QString errorMessage;\n    if (! img.read(file, crc, errorMessage)) {\n      errMsg(err) << errorMessage;\n      return false;\n    }\n    _images.append(img);\n  }\n\n  file_suffix_t suffix;\n  if (sizeof(file_suffix_t) != file.read((char *)&suffix, sizeof(suffix))) {\n    errMsg(err) << \"Cannot read suffix: \" << file.errorString() << \".\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  // Update CRC with suffix excl. CRC itself\n  crc.update((const uint8_t *) &suffix, sizeof(file_suffix_t)-4);\n\n  if (filesize != (size()-sizeof(file_suffix_t))) {\n    errMsg(err) << \"Filesize \" << (size()-sizeof(file_suffix_t))\n                << \" does not match declared content \" << filesize << \".\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  if (memcmp(suffix.signature, \"UFD\", 3)) {\n    errMsg(err) << \"Invalid suffix signature.\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  if (crc.get() != suffix.crc) {\n    errMsg(err) << \"Invalid checksum got \" << QString::number(unsigned(suffix.crc),16)\n                << \" expected \" << QString::number(unsigned(crc.get())) << \".\";\n    errMsg(err) << \"Cannot read DFU file '\" << file.fileName() << \"'.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUFile::write(const QString &filename, const ErrorStack &err) {\n  QFile file(filename);\n  if (! file.open(QIODevice::WriteOnly)) {\n    errMsg(err) << \"Cannot create DFU file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  bool res = write(file, err);\n  file.close();\n\n  return res;\n}\n\nbool\nDFUFile::write(QFile &file, const ErrorStack &err) {\n  file_prefix_t prefix;\n  memcpy(prefix.signature, \"DfuSe\", 5);\n  prefix.version = 0x01;\n  prefix.image_size = qToLittleEndian(uint32_t(size()-sizeof(file_suffix_t)));\n  prefix.n_targets = _images.size();\n\n  if (sizeof(file_prefix_t) != file.write((char *)&prefix, sizeof(file_prefix_t))) {\n    errMsg(err) << \"Cannot write DFU prefix to '\" << file.fileName()\n                << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  CRC32 crc;\n  crc.update((uint8_t *)&prefix, sizeof(file_prefix_t));\n\n  foreach (const Image &i, _images) {\n    QString errorMessage;\n    if (! i.write(file, crc, errorMessage)) {\n      errMsg(err) << errorMessage;\n      return false;\n    }\n  }\n\n  file_suffix_t suffix;\n  suffix.device_id = qToLittleEndian((uint16_t)0xffff);\n  suffix.product_id = qToLittleEndian((uint16_t)0xffff);\n  suffix.vendor_id = qToLittleEndian((uint16_t)0xffff);\n  suffix.DFUlo = 0x1a;\n  suffix.DFUhi = 0x01;\n  memcpy(suffix.signature, \"UFD\", 3);\n  suffix.size = 16;\n\n  crc.update((uint8_t *) &suffix, sizeof(file_suffix_t)-4);\n  suffix.crc = qToLittleEndian(crc.get());\n\n  if (sizeof(file_suffix_t) != file.write((char *)&suffix, sizeof(file_suffix_t))) {\n    errMsg(err) << \"Cannot write DFU suffix to '\" << file.fileName()\n                << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDFUFile::isAllocated(uint32_t offset, uint32_t img) const {\n  if (int(img) >= _images.size())\n    return false;\n  return image(img).isAllocated(offset);\n}\n\nunsigned char *\nDFUFile::data(uint32_t offset, uint32_t img) {\n  if (int(img) >= _images.size())\n    return nullptr;\n  return image(img).data(offset);\n}\n\nconst unsigned char *\nDFUFile::data(uint32_t offset, uint32_t img) const {\n  if (int(img) >= _images.size())\n    return nullptr;\n  return image(img).data(offset);\n}\n\nvoid\nDFUFile::dump(QTextStream &stream) const {\n  stream << \"DFU file with \" << _images.size() << \" images:\\n\";\n  foreach (const Image &i, _images) {\n    i.dump(stream);\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUFile::Element\n * ********************************************************************************************* */\nDFUFile::Element::Element()\n  : _address(0), _data()\n{\n  // pass...\n}\n\nDFUFile::Element::Element(uint32_t addr, uint32_t size)\n  : _address(addr), _data(size, 0x00)\n{\n  // pass...\n}\n\nDFUFile::Element::Element(const Element &other)\n  : _address(other._address), _data(other._data)\n{\n  // pass...\n}\n\nDFUFile::Element &\nDFUFile::Element::operator=(const Element &other) {\n  _address = other._address;\n  _data = other._data;\n  return *this;\n}\n\nuint32_t\nDFUFile::Element::size() const {\n  return sizeof(element_prefix_t) + _data.size();\n}\n\nuint32_t\nDFUFile::Element::memSize() const {\n  return _data.size();\n}\n\nuint32_t\nDFUFile::Element::address() const {\n  return _address;\n}\n\nvoid\nDFUFile::Element::setAddress(uint32_t addr) {\n  _address = addr;\n}\n\nbool\nDFUFile::Element::isAligned(unsigned blocksize) const {\n  return (0 == (_address % blocksize)) && (0 == (_data.size() % blocksize));\n}\n\nconst QByteArray &\nDFUFile::Element::data() const {\n  return _data;\n}\n\nQByteArray &\nDFUFile::Element::data() {\n  return _data;\n}\n\nbool\nDFUFile::Element::read(QFile &file, CRC32 &crc, QString &errorMessage)\n{\n  // Read Element prefix:\n  element_prefix_t prefix;\n  if (sizeof(element_prefix_t) != file.read((char *)&prefix, sizeof(element_prefix_t))) {\n    errorMessage = tr(\"Cannot read DFU file '%1': Cannot read element prefix: %2\").arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  crc.update((const uint8_t *) &prefix, sizeof(element_prefix_t));\n\n  _address = qFromLittleEndian(prefix.address);\n  uint32_t size = qFromLittleEndian(prefix.size);\n\n  _data.clear();\n  _data = file.read(size);\n\n  if (size != uint32_t(_data.size())) {\n    errorMessage = tr(\"Cannot read DFU file '%1': Cannot read element data: %2\").arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  crc.update(_data);\n\n  return true;\n}\n\nbool\nDFUFile::Element::write(QFile &file, CRC32 &crc, QString &errorMessage) const {\n  element_prefix_t prefix;\n  prefix.address = qToLittleEndian(_address);\n  prefix.size = qToLittleEndian(uint32_t(_data.size()));\n\n  crc.update((uint8_t *) &prefix, sizeof(element_prefix_t));\n\n  if (sizeof(element_prefix_t) != file.write((const char *)&prefix, sizeof(element_prefix_t))) {\n    errorMessage = tr(\"Cannot write element prefix to file '%1': %2\")\n        .arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  crc.update(_data);\n\n  if (_data.size() != file.write(_data)) {\n    errorMessage = tr(\"Cannot write element data to file '%1': %2\")\n        .arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  return true;\n}\n\nvoid\nDFUFile::Element::dump(QTextStream &stream) const {\n  stream.setIntegerBase(16);\n  stream << \"  Element @ 0x\" << _address << \", size=0x\" << _data.size() << \"\\n\";\n  int nrow = _data.size()/16;\n  uint8_t last_line[16]; memset(last_line, 0, 16);\n  bool skipping = false;\n  for (int i=0; i<nrow; i++) {\n    if ((i>0) && (0==memcmp(last_line, _data.constData()+i*16, 16)) && skipping)\n      continue;\n    if ((i>0) && (0==memcmp(last_line, _data.constData()+i*16, 16))) {\n      skipping = true;\n      stream.setFieldAlignment(QTextStream::AlignRight);\n      stream << qSetFieldWidth(8) << \"*\" << qSetFieldWidth(1) << \"\\n\";\n      continue;\n    }\n    memcpy(last_line, _data.constData()+i*16, 16);\n    skipping = false;\n    stream << qSetFieldWidth(8) << (_address+i*16)\n           << qSetFieldWidth(1) << \"  \";\n    for (int j=(i*16); j<(i*16+8); j++) {\n      stream << QString(\"%1\").arg(uint8_t(_data.at(j)), 2, 16, QChar('0'))\n             << qSetFieldWidth(1) << \" \";\n    }\n    stream << \" \";\n    for (int j=(i*16+8); j<(i*16+16); j++) {\n      stream << QString(\"%1\").arg(uint8_t(_data.at(j)), 2, 16, QChar('0'))\n             << qSetFieldWidth(1) << \" \";\n    }\n    stream << \" |\";\n    for (int j=(i*16); j<(i*16+16); j++) {\n      char c = _data.at(j);\n      if ((c>=32) && (c<127))\n        stream << c;\n      else\n        stream << \".\";\n    }\n    stream << \"|\\n\";\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DFUFile::Image\n * ********************************************************************************************* */\nDFUFile::Image::Image()\n  : _alternate_settings(0), _name(), _elements(), _addressmap()\n{\n  // pass...\n}\n\nDFUFile::Image::Image(const QString &name, uint8_t altSettings)\n  : _alternate_settings(altSettings), _name(name), _elements(), _addressmap()\n{\n  // pass...\n}\n\nDFUFile::Image::Image(const Image &other)\n  : _alternate_settings(other._alternate_settings), _name(other._name), _elements(other._elements),\n    _addressmap(other._addressmap)\n{\n  // pass...\n}\n\nDFUFile::Image::~Image() {\n  // pass...\n}\n\nDFUFile::Image &\nDFUFile::Image::operator=(const Image &other) {\n  _alternate_settings = other._alternate_settings;\n  _name = other._name;\n  _elements = other._elements;\n  _addressmap = other._addressmap;\n  return *this;\n}\n\nuint32_t\nDFUFile::Image::size() const {\n  uint32_t size = sizeof(image_prefix_t);\n  foreach (const Element &e, _elements)\n    size += e.size();\n  return size;\n}\n\nuint32_t\nDFUFile::Image::memSize() const {\n  uint32_t size = 0;\n  foreach (const Element &e, _elements)\n    size += e.memSize();\n  return size;\n}\n\nuint8_t\nDFUFile::Image::alternateSettings() const {\n  return _alternate_settings;\n}\n\nvoid\nDFUFile::Image::setAlternateSettings(uint8_t s) {\n  _alternate_settings = s;\n}\n\nbool\nDFUFile::Image::isNamed() const {\n  return ! _name.isEmpty();\n}\n\nconst QString &\nDFUFile::Image::name() const {\n  return _name;\n}\n\nvoid\nDFUFile::Image::setName(const QString &name) {\n  _name = name;\n}\n\nint\nDFUFile::Image::numElements() const {\n  return _elements.size();\n}\n\nconst DFUFile::Element &\nDFUFile::Image::element(int i) const {\n  return _elements[i];\n}\n\nDFUFile::Element &\nDFUFile::Image::element(int i) {\n  return _elements[i];\n}\n\nvoid\nDFUFile::Image::addElement(uint32_t addr, uint32_t size, int index) {\n  if ((0 > index) || (_elements.size() <= index)) {\n    _elements.append(Element(addr, size));\n    _addressmap.add(addr, size);\n  } else {\n    _elements.insert(index, Element(addr, size));\n    _addressmap.add(addr, size, index);\n  }\n}\n\nvoid\nDFUFile::Image::addElement(const Element &element) {\n  _elements.append(element);\n  _addressmap.add(element.address(), element.size());\n}\n\nvoid\nDFUFile::Image::remElement(int i) {\n  _elements.remove(i);\n  _addressmap.rem(i);\n}\n\nbool\nDFUFile::Image::isAligned(unsigned blocksize) const {\n  for (int i=0; i<_elements.count(); i++)\n    if (! _elements.at(i).isAligned(blocksize))\n      return false;\n  return true;\n}\n\nDFUFile::Image::iterator\nDFUFile::Image::begin() {\n  return _elements.begin();\n}\n\nDFUFile::Image::iterator\nDFUFile::Image::end() {\n  return _elements.end();\n}\n\n\nbool\nDFUFile::Image::read(QFile &file, CRC32 &crc, QString &errorMessage)\n{\n  image_prefix_t prefix;\n  if (sizeof(image_prefix_t) != file.read((char *)&prefix, sizeof(image_prefix_t))) {\n    errorMessage = tr(\"Cannot read DFU file '%1': Cannot read image: %2\").arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  crc.update((const uint8_t *) &prefix, sizeof(image_prefix_t));\n\n  if (memcmp(prefix.signature, \"Target\", 6)) {\n    errorMessage = tr(\"Cannot read DFU file '%1': Invalid image signature value.\").arg(file.fileName());\n    return false;\n  }\n\n  _alternate_settings = prefix.alternate_setting;\n  if (0x01 ==qFromLittleEndian(prefix.is_named)) {\n    char tmp[256]; tmp[255]=0;\n    memcpy(tmp, prefix.name, 255);\n    _name = tmp;\n  }\n\n  uint32_t size = qFromLittleEndian(prefix.size);\n  uint32_t n_elements = qFromLittleEndian(prefix.n_elements);\n  for (uint32_t i=0; i<n_elements; i++) {\n    Element element;\n    if (! element.read(file, crc, errorMessage))\n      return false;\n    this->addElement(element);\n  }\n\n  // verify size:\n  if (size != (this->size()-sizeof(image_prefix_t))) {\n    errorMessage = tr(\"Cannot read DFU file '%1': Invalid image size %2b specified, expected %3b.\")\n        .arg(file.fileName()).arg(size).arg(this->size()-sizeof(image_prefix_t));\n    return false;\n  }\n  return true;\n}\n\nbool\nDFUFile::Image::write(QFile &file, CRC32 &crc, QString &errorMessage) const {\n  image_prefix_t prefix;\n  memcpy(prefix.signature, \"Target\", 6);\n  prefix.alternate_setting = _alternate_settings;\n  prefix.is_named = qToLittleEndian(uint32_t(_name.isEmpty() ? 0 : 1));\n  memset(prefix.name, 0, 255);\n  if (! _name.isEmpty())\n    memcpy(prefix.name, _name.toLocal8Bit().constData(), std::min(qsizetype(255), _name.size()));\n  prefix.size = qToLittleEndian(uint32_t(size()-sizeof(image_prefix_t)));\n  prefix.n_elements = qToLittleEndian(uint32_t(_elements.size()));\n\n  crc.update((uint8_t *)&prefix, sizeof(image_prefix_t));\n\n  if (sizeof(image_prefix_t) != file.write((char *)&prefix, sizeof(image_prefix_t))) {\n    errorMessage = tr(\"Cannot write image prefix to '%1': %2.\")\n        .arg(file.fileName()).arg(file.errorString());\n    return false;\n  }\n\n  foreach (const Element &e, _elements) {\n    if (! e.write(file, crc, errorMessage))\n      return false;\n  }\n\n  return true;\n}\n\nvoid\nDFUFile::Image::sort() {\n  std::stable_sort(_elements.begin(), _elements.end(),\n                   [](const Element &first, const Element &second) {\n                     return first.address()<second.address();\n                   });\n\n  // Rebuild address map\n  _addressmap.clear();\n  for (int i=0; i<_elements.size(); i++)\n    _addressmap.add(_elements[i].address(), _elements.size());\n}\n\nvoid\nDFUFile::Image::dump(QTextStream &stream) const {\n  stream << \" Image\";\n  if (_name.isEmpty())\n    stream << \", target not named\";\n  else\n    stream << \", target '\" << _name << \"'\";\n  stream << \", #elements=\" << _elements.size() << \":\\n\";\n  foreach (const Element &e, _elements) {\n    e.dump(stream);\n  }\n}\n\nbool\nDFUFile::Image::isAllocated(uint32_t offset) const {\n  if (_elements.isEmpty())\n    return false;\n  return 0 <= _addressmap.find(offset);\n}\n\nunsigned char *\nDFUFile::Image::data(uint32_t offset) {\n  int idx = _addressmap.find(offset);\n  if (0 > idx) {\n    logFatal() << \"Cannot resolve offset \" << QString::number(offset, 16) << \"h.\";\n    return nullptr;\n  }\n  return (unsigned char *)(element(idx).data().data()+\n                           (offset-element(idx).address()));\n}\n\nconst unsigned char *\nDFUFile::Image::data(uint32_t offset) const {\n  int idx = _addressmap.find(offset);\n  if (0 > idx) {\n    logFatal() << \"Cannot resolve offset \" << QString::number(offset, 16) << \"h.\";\n    return nullptr;\n  }\n  return (unsigned char *)(element(idx).data().data()+\n                           (offset-element(idx).address()));\n}\n"
  },
  {
    "path": "lib/dfufile.hh",
    "content": "#ifndef DFUFILE_HH\n#define DFUFILE_HH\n\n#include <QFile>\n#include <QVector>\n#include <QByteArray>\n#include <QString>\n#include <QTextStream>\n\n#include \"addressmap.hh\"\n#include \"errorstack.hh\"\n\nclass CRC32;\n\n/** A collection of images, each consisting of one or more memory sections.\n *\n * This class forms the base of all binary encoded codeplugs and call-sign DBs. To this end, it\n * represents the codeplug or call-sign DB memory as written and stored inside the radio. It also\n * implements the DFU file format (hence the name) and thus allows to store and load binary\n * codeplugs or call-sign DBs in files. Please note, that binary codeplugs and call-sign DBs are\n * highly vendor and device specific. Consequently, they should be be used to exchange codeplugs.\n *\n * DFU File consists of a file prefix followed by several images and a final file suffix. The file\n * prefix consists of a file signature of 5 bytes just consisting of the ASCII string \"DfuSe\",\n * followed by a single byte indicating the version number (V), here 0x01. The next field is a\n * uint32_t containing the file-size excluding the suffix in little endian. Finally, there is a\n * single byte (N) holding the number of images.\n *\n * @code\n * +---+---+---+---+---+---+---+---+---+---+---+\n * |      \"DfuSe\"      | V |   file size   | N |\n * +---+---+---+---+---+---+---+---+---+---+---+\n * @endcode\n *\n * The file suffix consists of the device, product and vendor IDs as uint16_t followed by a\n * fixed signature uin16_t 0x011a (little endian) followed by another fixed signature containing the\n * ASCII string \"UFD\". The next field (S) contains the size of the suffix, that is 16. Finally there\n * is a CRC32 field computed over the entire file excluding the CRC itself and stored in little\n * endian.\n *\n * @code\n * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n * |  dev  |  prod |  vend |  Sig  |    UFD    | S |     CRC32     |\n * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+\n * @endcode\n *\n * Each image section consists of a image prefix followed by several element sections. The image\n * prefix consists of a 6byte signature containing the ASCII string \"Target\" followed by a\n * single byte indicating the so-called \"alternate settings\" field, usually 0x01. The 32bit field\n * \"is named\" just indicates that the 255 byte name field is set (i.e., 0x01 in little endian).\n * The next field contains the 32 bit size of the image excluding the image prefix in little endian.\n * Finally the last 32bit field contains the number of elements, the image consists of in little\n * endian too.\n *\n * @code\n * +---+---+---+---+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+---+\n * |       \"Target\"        | A |    is named   | 255b name |      size     |   N Elements  |\n * +---+---+---+---+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+---+\n * @endcode\n *\n * Finally, each element of an image is prefixed by a header containing the target address and size\n * of the element in little endian.\n *\n * @code\n * +---+---+---+---+---+---+---+---+---+...+---+\n * |    address    |      size     | el. data  |\n * +---+---+---+---+---+---+---+---+---+...+---+\n * @endcode\n *\n * @ingroup util\n */\nclass DFUFile: public QObject\n{\n\tQ_OBJECT\n\npublic:\n  /** Represents a single element within a @c Image. */\n  class Element {\n  public:\n    /** Empty constructor. */\n    Element();\n    /** Constructs an element for the given address and of the given size. */\n    Element(uint32_t addr, uint32_t size);\n    /** Copy constructor. */\n    Element(const Element &other);\n    /** Copying assignment. */\n    Element &operator= (const Element &other);\n\n    /** Returns the address of the element. */\n    uint32_t address() const;\n    /** Sets the address of the element. */\n    void setAddress(uint32_t addr);\n    /** Returns the size of the element (including headers). */\n    uint32_t size() const;\n    /** Returns the memory size of the element. */\n    uint32_t memSize() const;\n    /** Checks if the element address and size is aligned with the given block size. */\n    bool isAligned(unsigned blocksize) const;\n    /** Returns a reference to the data. */\n    const QByteArray &data() const;\n    /** Returns a reference to the data. */\n    QByteArray &data();\n\n    /** Reads an element from the given file and updates the CRC. */\n    bool read(QFile &file, CRC32 &crc, QString &errorMessage);\n    /** Writes an element to the given file and updates the CRC. */\n    bool write(QFile &file, CRC32 &crc, QString &errorMessage) const;\n\n    /** Dumps a textual representation of the element. */\n    void dump(QTextStream &stream) const;\n\n  protected:\n    /** The address of the element. */\n    uint32_t _address;\n    /** The data of the element. */\n    QByteArray _data;\n  };\n\n  /** Represents a single image within a @c DFUFile. */\n  class Image\n  {\n  public:\n    /** Iterator type over elements. */\n    typedef QVector<Element>::iterator iterator;\n\n  public:\n    /** Default constructor.\n     * Constructs an empty image. */\n    Image();\n    /** Constructs an image with the given name and optional \"alternative settings\". */\n    Image(const QString &name, uint8_t altSettings=0);\n    /** Copy constructor. */\n    Image(const Image &other);\n    /** Destructor. */\n    virtual ~Image();\n    /** Copying assignment. */\n    Image &operator=(const Image &other);\n\n    /** Returns the alternate settings byte. */\n    uint8_t alternateSettings() const;\n    /** Sets the alternate settings byte. */\n    void setAlternateSettings(uint8_t s);\n\n    /** Returns @c true if the image is named. */\n    bool isNamed() const;\n    /** Returns the name of the image. */\n    const QString &name() const;\n    /** Sets the name of the image. */\n    void setName(const QString &name);\n    /** Returns the total size of the image (including headers). */\n    uint32_t size() const;\n    /** Returns the memory size stored in the image. */\n    uint32_t memSize() const;\n    /** Returns the number of elements of this image. */\n    int numElements() const;\n    /** Returns a reference to the i-th element of the image. */\n    const Element &element(int i) const;\n    /** Returns a reference to the i-th element of the image. */\n    Element &element(int i);\n    /** Adds an element to the image with the given address and size at the specified index.\n     * If the index is negative, the element gets appended. */\n    void addElement(uint32_t addr, uint32_t size, int index=-1);\n    /** Adds an element to the image. */\n    void addElement(const Element &element);\n    /** Removes the i-th element from this image. */\n    void remElement(int i);\n    /** Checks if all element addresses and sizes is aligned with the given block size. */\n    bool isAligned(unsigned blocksize) const;\n\n    /** Retruns a pointer to the first element. */\n    iterator begin();\n    /** Returns a pointer after the last element. */\n    iterator end();\n\n    /** Reads an image from the given file and updates the CRC. */\n    bool read(QFile &file, CRC32 &crc, QString &errorMessage);\n    /** Writes this image to the given file and updates the CRC. */\n    bool write(QFile &file, CRC32 &crc, QString &errorMessage) const;\n\n    /** Prints a textual representation of the image into the given stream. */\n    void dump(QTextStream &stream) const;\n\n    /** Returns @c true if the specified address is allocated. */\n    virtual bool isAllocated(uint32_t offset) const;\n\n    /** Returns a pointer to the encoded raw data at the specified offset. */\n    virtual unsigned char *data(uint32_t offset);\n    /** Returns a const pointer to the encoded raw data at the specified offset. */\n    virtual const unsigned char *data(uint32_t offset) const;\n\n    /** Sorts all elements with respect to their addresses. */\n    void sort();\n\n  protected:\n    /** Alternate settings byte. */\n    uint8_t  _alternate_settings;\n    /** Optional image name. */\n    QString _name;\n    /** The elements of the image. */\n    QVector<Element> _elements;\n    /** Maps an address range to element index. */\n    AddressMap _addressmap;\n  };\n\npublic:\n  /** Constructs an empty DFU file object. */\n\tDFUFile(QObject *parent=nullptr);\n\n  /** Returns the total size of the DFU file. */\n\tuint32_t size() const;\n  /** Returns the total memory size stored in the DFU file. */\n  uint32_t memSize() const;\n\n  /** Returns the number of images within the DFU file. */\n\tint numImages() const;\n  /** Returns a reference to the @c i-th image of the file. */\n\tconst Image &image(int i) const;\n  /** Returns a reference to the @c i-th image of the file. */\n\tImage &image(int i);\n  /** Adds a new image to the file. */\n\tvoid addImage(const QString &name, uint8_t altSettings=1);\n  /** Adds an image to the file. */\n\tvoid addImage(const Image &img);\n  /** Deletes the @c i-th image from the file. */\n\tvoid remImage(int i);\n\n  /** Checks if all image addresses and sizes is aligned with the given block size. */\n  bool isAligned(unsigned blocksize) const;\n\n  /** Reads the specified DFU file.\n   * @return @c false on error. */\n  bool read(const QString &filename, const ErrorStack &err=ErrorStack());\n  /** Reads the specified DFU file.\n   * @returns @c false on error. */\n  bool read(QFile &file, const ErrorStack &err=ErrorStack());\n\n  /** Writes to the specified file.\n   * @returns @c false on error. */\n  bool write(const QString &filename, const ErrorStack &err=ErrorStack());\n  /** Writes to the specified file.\n   * @returns @c false on error. */\n  bool write(QFile &file, const ErrorStack &err=ErrorStack());\n\n  /** Dumps a text representation of the DFU file structure to the specified text stream. */\n\tvoid dump(QTextStream &stream) const;\n\n  /** Returns @c true if the specified address (and image) is allocated. */\n  virtual bool isAllocated(uint32_t offset, uint32_t img=0) const;\n\n  /** Returns a pointer to the encoded raw data at the specified offset. */\n  virtual unsigned char *data(uint32_t offset, uint32_t img=0);\n  /** Returns a const pointer to the encoded raw data at the specified offset. */\n  virtual const unsigned char *data(uint32_t offset, uint32_t img=0) const;\n\nprotected:\n  /// The list of images.\n\tQVector<Image> _images;\n};\n\n#endif // DFUFILE_HH\n"
  },
  {
    "path": "lib/dm1701.cc",
    "content": "#include \"dm1701.hh\"\n#include \"dm1701_limits.hh\"\n\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n\nRadioLimits *DM1701::_limits = nullptr;\n\n\nDM1701::DM1701(TyTInterface *device, QObject *parent)\n  : TyTRadio(device, parent), _name(\"Baofeng DM-1701\"), _codeplug()\n{\n  // pass...\n}\n\nconst QString &\nDM1701::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nDM1701::limits() const {\n  if (nullptr == _limits)\n    _limits = new DM1701Limits();\n  return *_limits;\n}\n\nRadioInfo\nDM1701::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::DM1701, \"dm1701\", \"DM-1701\", \"Baofeng\", {TyTInterface::interfaceInfo()},\n        QList<RadioInfo>{\n          RadioInfo(RadioInfo::RT84, \"rt84\", \"RT84\", \"Retevis\", {TyTInterface::interfaceInfo()})\n        });\n}\n\nconst Codeplug &\nDM1701::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nDM1701::codeplug() {\n  return _codeplug;\n}\n\nconst CallsignDB *\nDM1701::callsignDB() const {\n  return &_callsigndb;\n}\n\nCallsignDB *\nDM1701::callsignDB() {\n  return &_callsigndb;\n}\n\n"
  },
  {
    "path": "lib/dm1701.hh",
    "content": "/** @defgroup dm1701 Baofeng DM-1701, Retevis RT84\n * Device specific classes for Baofeng DM-1701 and Retevis RT84.\n *\n * \\image html dm1701.png \"DM-1701\" width=200px\n * \\image latex dm1701.png \"DM-1701\" width=200px\n *\n * The Baofeng DM-1701 and the identical Retevis RT84. This implementation supports CPS version 1.5\n * and firmware version 2.3.\n *\n * Features:\n *   - VHF/UHF, 136-174 MHz and 400-480 MHz\n *   - Pout = 1, 3 and 5W\n *   - 3000 channels\n *   - 10000 contacts\n *   - 250 RX group lists with ?? contacts each\n *   - 250 zones, 64 channels each (A & B)\n *   - 250 scan lists, 31 channels each\n *   - ...\n * @ingroup tyt */\n#ifndef DM1701_HH\n#define DM1701_HH\n\n#include \"radio.hh\"\n#include \"tyt_radio.hh\"\n#include \"tyt_codeplug.hh\"\n#include \"tyt_callsigndb.hh\"\n#include \"dm1701_codeplug.hh\"\n#include \"dm1701_callsigndb.hh\"\n\n\n/** Implements an USB interface to the  Baofeng DM-1701 and Retevis RT84 VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * The  Baofeng DM-1701 and Retevis RT84 radios use a DFU-style communication protocol to read and write\n * codeplugs onto the radio (see @c DFUDevice). This class implements the communication details\n * using DFU protocol.\n *\n * @ingroup dm1701 */\nclass DM1701: public TyTRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit DM1701(TyTInterface *device=nullptr, QObject *parent=nullptr);\n\n  const QString &name() const;\n  const RadioLimits &limits() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprotected:\n  /** The device identifier. */\n  QString _name;\n  /** The actual binary codeplug representation. */\n  DM1701Codeplug _codeplug;\n  /** The callsign DB object. */\n  DM1701CallsignDB _callsigndb;\n\nprivate:\n  /** Holds the singleton instance of the radio limits. */\n  static RadioLimits *_limits;\n};\n\n#endif // DM1701_HH\n"
  },
  {
    "path": "lib/dm1701_callsigndb.cc",
    "content": "#include \"dm1701_callsigndb.hh\"\n\nDM1701CallsignDB::DM1701CallsignDB(QObject *parent)\n  : TyTCallsignDB(parent)\n{\n  image(0).setName(\"BTECH DM-1701 Callsign database.\");\n}\n\nDM1701CallsignDB::~DM1701CallsignDB() {\n  // pass...\n}\n\n\n"
  },
  {
    "path": "lib/dm1701_callsigndb.hh",
    "content": "#ifndef DM1701_CALLSIGNDB_HH\n#define DM1701_CALLSIGNDB_HH\n\n#include \"tyt_callsigndb.hh\"\n\n/** Device specific implementation of the call-sign DB for the BTECH DM1701 and Retevis RT84.\n *\n * In fact this callsign DB is identical to the generic @c TyTCallsignDB.\n *\n * @ingroup dm1701 */\nclass DM1701CallsignDB : public TyTCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DM1701CallsignDB(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~DM1701CallsignDB();\n};\n\n#endif // DM1701_CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/dm1701_codeplug.cc",
    "content": "#include \"dm1701_codeplug.hh\"\n#include \"logger.hh\"\n#include \"config.hh\"\n#include <QTimeZone>\n\n\n#define NUM_CHANNELS                3000\n#define ADDR_CHANNELS           0x110000\n#define CHANNEL_SIZE            0x000040\n\n#define NUM_CONTACTS               10000\n#define ADDR_CONTACTS           0x140000\n#define CONTACT_SIZE            0x000024\n\n#define NUM_ZONES                    250\n#define ADDR_ZONES              0x0149e0\n#define ZONE_SIZE               0x000040\n#define ADDR_ZONEEXTS           0x031000\n#define ZONEEXT_SIZE            0x0000e0\n\n#define NUM_GROUPLISTS               250\n#define ADDR_GROUPLISTS         0x00ec20\n#define GROUPLIST_SIZE          0x000060\n\n#define NUM_SCANLISTS                250\n#define ADDR_SCANLISTS          0x018860\n#define SCANLIST_SIZE           0x000068\n\n#define ADDR_TIMESTAMP          0x002000\n#define ADDR_SETTINGS           0x002040\n#define SETTINGS_SIZE           0x0000b0\n#define ADDR_BOOTSETTINGS       0x02f000\n#define ADDR_MENUSETTINGS       0x0020f0\n#define ADDR_BUTTONSETTINGS     0x002100\n#define BUTTONSETTINGS_SIZE     0x000014\n#define ADDR_PRIVACY_KEYS       0x0059c0\n\n#define NUM_GPSSYSTEMS                16\n#define ADDR_GPSSYSTEMS         0x03ec40\n#define GPSSYSTEM_SIZE          0x000010\n\n#define ADDR_EMERGENCY_SETTINGS 0x005a50\n#define NUM_EMERGENCY_SYSTEMS         32\n#define ADDR_EMERGENCY_SYSTEMS  0x005a60\n#define EMERGENCY_SYSTEM_SIZE   0x000028\n\n#define ADDR_VFO_CHANNEL_A      0x02ef00\n#define ADDR_VFO_CHANNEL_B      0x02ef40\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DM1701Codeplug::ChannelElement\n * ********************************************************************************************* */\nDM1701Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::ChannelElement::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nDM1701Codeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : TyTCodeplug::ChannelElement(ptr, CHANNEL_SIZE)\n{\n  // pass...\n}\n\nvoid\nDM1701Codeplug::ChannelElement::clear() {\n  TyTCodeplug::ChannelElement::clear();\n\n  enableTightSquelch(false);\n  enableReverseBurst(true);\n  setPower(Channel::Power::High);\n  setBit(0x0003, 6, true);\n  setUInt8(0x0005, 0xc3);\n  setUInt8(0x000f, 0xff);\n}\n\nbool\nDM1701Codeplug::ChannelElement::tightSquelchEnabled() const {\n  return !getBit(0x0000, 5);\n}\nvoid\nDM1701Codeplug::ChannelElement::enableTightSquelch(bool enable) {\n  setBit(0x0000, 5, !enable);\n}\n\nbool\nDM1701Codeplug::ChannelElement::reverseBurst() const {\n  return getBit(0x0004, 2);\n}\nvoid\nDM1701Codeplug::ChannelElement::enableReverseBurst(bool enable) {\n  setBit(0x0004, 2, enable);\n}\n\nChannel::Power\nDM1701Codeplug::ChannelElement::power() const {\n  if (getBit(0x0004, 5))\n    return Channel::Power::High;\n  return Channel::Power::Low;\n}\nvoid\nDM1701Codeplug::ChannelElement::setPower(Channel::Power pwr) {\n  switch (pwr) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n  case Channel::Power::Mid:\n    setBit(0x0004, 5, false);\n    break;\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setBit(0x0004, 5, true);\n  }\n}\n\nChannel *\nDM1701Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const {\n  Channel *ch = TyTCodeplug::ChannelElement::toChannelObj(err);\n  if (nullptr == ch)\n    return ch;\n\n  ch->setPower(power());\n  if (auto fmc = ch->as<FMChannel>())\n    fmc->extended()->enableReverseBurst(reverseBurst());\n\n  // Apply extension\n  if (ch->tytChannelExtension()) {\n    ch->tytChannelExtension()->enableTightSquelch(tightSquelchEnabled());\n  }\n\n  return ch;\n}\n\nvoid\nDM1701Codeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  TyTCodeplug::ChannelElement::fromChannelObj(c, ctx);\n\n  setPower(c->power());\n  if (auto fmc = c->as<FMChannel>())\n    enableReverseBurst(fmc->extended()->reverseBurst());\n\n  // apply extensions (extension will be created in TyTCodeplug::ChannelElement::fromChannelObj)\n  if (TyTChannelExtension *ex = c->tytChannelExtension()) {\n    enableTightSquelch(ex->tightSquelch());\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DM1701Codeplug::VFOChannelElement\n * ********************************************************************************************* */\nDM1701Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, size_t size)\n  : ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nDM1701Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr)\n  : ChannelElement(ptr, CHANNEL_SIZE)\n{\n  // pass...\n}\n\nDM1701Codeplug::VFOChannelElement::~VFOChannelElement() {\n  // pass...\n}\n\nQString\nDM1701Codeplug::VFOChannelElement::name() const {\n  return \"\";\n}\nvoid\nDM1701Codeplug::VFOChannelElement::setName(const QString &txt) {\n  Q_UNUSED(txt)\n  // pass...\n}\n\nunsigned\nDM1701Codeplug::VFOChannelElement::stepSize() const {\n  return (getUInt8(32)+1)*2500;\n}\nvoid\nDM1701Codeplug::VFOChannelElement::setStepSize(unsigned ss_Hz) {\n  ss_Hz = std::min(50000U, std::max(ss_Hz, 2500U));\n  setUInt8(32, ss_Hz/2500-1);\n  setUInt8(33, 0xff);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DM1701Codeplug::GeneralSettingsElement\n * ********************************************************************************************* */\nDM1701Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDM1701Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : TyTCodeplug::GeneralSettingsElement(ptr, SETTINGS_SIZE)\n{\n  // pass...\n}\n\nvoid\nDM1701Codeplug::GeneralSettingsElement::clear() {\n  TyTCodeplug::GeneralSettingsElement::clear();\n\n  enableChannelModeA(true);\n  enableChannelModeB(true);\n  enableChannelMode(true);\n  enableGroupCallMatch(true);\n  enablePrivateCallMatch(true);\n  setTimeZone(QTimeZone::systemTimeZone());\n  setChannelHangTime(3000);\n\n  memset(_data+0x91, 0xff, 0x1f);\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::channelModeA() const {\n  return getBit(0x43,3);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::enableChannelModeA(bool enable) {\n  setBit(0x43,3, enable);\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::channelModeB() const {\n  return getBit(0x43,7);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::enableChannelModeB(bool enable) {\n  setBit(0x43,7, enable);\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::channelMode() const {\n  return 0xff == getUInt8(0x57);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::enableChannelMode(bool enable) {\n  setUInt8(0x57, enable ? 0xff : 0x00);\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::groupCallMatch() const {\n  return getBit(0x6b, 0);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::enableGroupCallMatch(bool enable) {\n  setBit(0x6b, 0, enable);\n}\nbool\nDM1701Codeplug::GeneralSettingsElement::privateCallMatch() const {\n  return getBit(0x6b, 1);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::enablePrivateCallMatch(bool enable) {\n  setBit(0x6b, 1, enable);\n}\n\nQTimeZone\nDM1701Codeplug::GeneralSettingsElement::timeZone() const {\n  return QTimeZone((int(getUInt5(0x6b, 3))-12)*3600);\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::setTimeZone(const QTimeZone &zone) {\n  int idx = (zone.standardTimeOffset(QDateTime::currentDateTime())/3600)+12;\n  setUInt5(0x6b, 3, uint8_t(idx));\n}\n\nunsigned\nDM1701Codeplug::GeneralSettingsElement::channelHangTime() const {\n  return unsigned(getUInt8(0x90))*100;\n}\nvoid\nDM1701Codeplug::GeneralSettingsElement::setChannelHangTime(unsigned dur) {\n  setUInt8(0x90, dur/100);\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::fromConfig(const Config *config) {\n  if (! TyTCodeplug::GeneralSettingsElement::fromConfig(config))\n    return false;\n\n  setTimeZone(QTimeZone::systemTimeZone());\n\n  enablePrivateCallMatch(config->settings()->dmr()->privateCallMatchEnabled());\n  enableGroupCallMatch(config->settings()->dmr()->groupCallMatchEnabled());\n\n  // apply extension\n  if (TyTSettingsExtension *ex  = config->settings()->tytExtension()) {\n    enableChannelMode(ex->channelMode());\n    enableChannelModeA(ex->channelModeA());\n    enableChannelModeB(ex->channelModeB());\n    setChannelHangTime(ex->channelHangTime());\n  }\n\n  return true;\n}\n\nbool\nDM1701Codeplug::GeneralSettingsElement::updateConfig(Config *config) {\n  if (! TyTCodeplug::GeneralSettingsElement::updateConfig(config))\n    return false;\n\n  config->settings()->dmr()->enablePrivateCallMatch(privateCallMatch());\n  config->settings()->dmr()->enableGroupCallMatch(groupCallMatch());\n\n  // Update extension if set.\n  if (TyTSettingsExtension *ex = config->settings()->tytExtension()) {\n    ex->enableChannelMode(channelMode());\n    ex->enableChannelModeA(channelModeA());\n    ex->enableChannelModeB(channelModeB());\n    ex->setChannelHangTime(channelHangTime());\n  }\n\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of DM1701Codeplug::ButtonSettingsElement\n * ********************************************************************************************* */\nDM1701Codeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::ButtonSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDM1701Codeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr)\n  : TyTCodeplug::ButtonSettingsElement(ptr, BUTTONSETTINGS_SIZE)\n{\n  // pass...\n}\n\nvoid\nDM1701Codeplug::ButtonSettingsElement::clear() {\n  setSideButton3Short(ButtonAction::Disabled);\n  setSideButton3Long(ButtonAction::Disabled);\n  setProgButton1Short(ButtonAction::Disabled);\n  setProgButton1Long(ButtonAction::Disabled);\n  setProgButton2Short(ButtonAction::Disabled);\n  setProgButton2Long(ButtonAction::Disabled);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::sideButton3Short() const {\n  return ButtonAction(getUInt8(0x06));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setSideButton3Short(ButtonAction action) {\n  setUInt8(0x06, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::sideButton3Long() const {\n  return ButtonAction(getUInt8(0x07));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setSideButton3Long(ButtonAction action) {\n  setUInt8(0x07, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::progButton1Short() const {\n  return ButtonAction(getUInt8(0x08));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setProgButton1Short(ButtonAction action) {\n  setUInt8(0x08, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::progButton1Long() const {\n  return ButtonAction(getUInt8(0x09));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setProgButton1Long(ButtonAction action) {\n  setUInt8(0x09, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::progButton2Short() const {\n  return ButtonAction(getUInt8(0x0a));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setProgButton2Short(ButtonAction action) {\n  setUInt8(0x0a, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nDM1701Codeplug::ButtonSettingsElement::progButton2Long() const {\n  return ButtonAction(getUInt8(0x0b));\n}\nvoid\nDM1701Codeplug::ButtonSettingsElement::setProgButton2Long(ButtonAction action) {\n  setUInt8(0x0b, action);\n}\n\nbool\nDM1701Codeplug::ButtonSettingsElement::fromConfig(const Config *config) {\n  if (! TyTCodeplug::ButtonSettingsElement::fromConfig(config))\n    return false;\n\n  if (config->tytExtension()) {\n    TyTButtonSettings *ex = config->tytExtension()->buttonSettings();\n    setSideButton3Short(ex->sideButton3Short());\n    setSideButton3Long(ex->sideButton3Long());\n    setProgButton1Short(ex->progButton1Short());\n    setProgButton1Long(ex->progButton1Long());\n    setProgButton2Short(ex->progButton2Short());\n    setProgButton2Long(ex->progButton2Long());\n  }\n\n  return true;\n}\n\nbool\nDM1701Codeplug::ButtonSettingsElement::updateConfig(Config *config) {\n  if (! TyTCodeplug::ButtonSettingsElement::updateConfig(config))\n    return false;\n\n  if (config->tytExtension()) {\n    TyTButtonSettings *ex = config->tytExtension()->buttonSettings();\n    ex->setSideButton3Short(sideButton3Short());\n    ex->setSideButton3Long(sideButton3Long());\n    ex->setProgButton1Short(progButton1Short());\n    ex->setProgButton1Long(progButton1Long());\n    ex->setProgButton2Short(progButton2Short());\n    ex->setProgButton2Long(progButton2Long());\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::ZoneElement\n * ******************************************************************************************** */\nDM1701Codeplug::ZoneExtElement::ZoneExtElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nDM1701Codeplug::ZoneExtElement::ZoneExtElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, ZONEEXT_SIZE)\n{\n  // pass...\n}\n\nDM1701Codeplug::ZoneExtElement::~ZoneExtElement() {\n  // pass...\n}\n\nvoid\nDM1701Codeplug::ZoneExtElement::clear() {\n  memset(_data, 0x00, 0xe0);\n}\n\nbool\nDM1701Codeplug::ZoneExtElement::hasMemberIndexA(unsigned n) const {\n  return 0 != memberIndexA(n);\n}\nuint16_t\nDM1701Codeplug::ZoneExtElement::memberIndexA(unsigned n) const {\n  return getUInt16_le(0x00 + 2*n);\n}\n\nvoid\nDM1701Codeplug::ZoneExtElement::setMemberIndexA(unsigned n, uint16_t idx) {\n  setUInt16_le(0x00 + 2*n, idx);\n}\n\nbool\nDM1701Codeplug::ZoneExtElement::hasMemberIndexB(unsigned n) const {\n  return 0 != memberIndexB(n);\n}\nuint16_t\nDM1701Codeplug::ZoneExtElement::memberIndexB(unsigned n) const {\n  return getUInt16_le(0x60 + 2*n);\n}\n\nvoid\nDM1701Codeplug::ZoneExtElement::setMemberIndexB(unsigned n, uint16_t idx) {\n  setUInt16_le(0x60 + 2*n, idx);\n}\n\nbool\nDM1701Codeplug::ZoneExtElement::fromZoneObj(const Zone *zone, Context &ctx) {\n  // Store remaining channels from list A\n  for (int i=16; i<64; i++) {\n    if (i < zone->A()->count()) {\n      int idx = ctx.index(zone->A()->get(i));\n      setMemberIndexA(i-16, idx);\n    } else\n      setMemberIndexA(i-16, 0);\n  }\n  // Store channel from list B\n  for (int i=0; i<64; i++) {\n    if (i < zone->B()->count())\n      setMemberIndexB(i, ctx.index(zone->B()->get(i)));\n    else\n      setMemberIndexB(i, 0);\n  }\n\n  return true;\n}\n\nbool\nDM1701Codeplug::ZoneExtElement::linkZoneObj(Zone *zone, Context &ctx) {\n  for (int i=0; (i<48) && hasMemberIndexA(i); i++) {\n    if (! ctx.has<Channel>(memberIndexA(i))) {\n      logWarn() << \"Cannot link zone extension: Channel index \" << memberIndexA(i) << \" not defined.\";\n      continue;\n    }\n    zone->A()->add(ctx.get<Channel>(memberIndexA(i)));\n  }\n\n  for (int i=0; (i<64) && hasMemberIndexB(i); i++) {\n    if (! ctx.has<Channel>(memberIndexB(i))) {\n      logWarn() << \"Cannot link zone extension: Channel index \" << memberIndexB(i) << \" not defined.\";\n      continue;\n    }\n    zone->B()->add(ctx.get<Channel>(memberIndexB(i)));\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DM1701Codeplug\n * ********************************************************************************************* */\nDM1701Codeplug::DM1701Codeplug(QObject *parent)\n  : TyTCodeplug(parent)\n{\n  addImage(\"Baofeng DM-1701 Codeplug\");\n  image(0).addElement(0x002000, 0x3e000);\n  image(0).addElement(0x110000, 0x90000);\n\n  // Clear entire codeplug\n  clear();\n}\n\nDM1701Codeplug::~DM1701Codeplug() {\n  // pass...\n}\n\nvoid\nDM1701Codeplug::clearTimestamp() {\n  TimestampElement(data(ADDR_TIMESTAMP)).clear();\n}\n\nbool\nDM1701Codeplug::encodeTimestamp() {\n  TimestampElement ts(data(ADDR_TIMESTAMP));\n  ts.setTimestamp(QDateTime::currentDateTime());\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(ADDR_SETTINGS)).clear();\n}\n\nbool\nDM1701Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nDM1701Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(ctx.config());\n}\n\nvoid\nDM1701Codeplug::clearChannels() {\n  // Clear channels\n  for (int i=0; i<NUM_CHANNELS; i++)\n    ChannelElement(data(ADDR_CHANNELS+i*CHANNEL_SIZE)).clear();\n}\n\nbool\nDM1701Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Channels\n  for (unsigned int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (i < ctx.count<Channel>()) {\n      chan.fromChannelObj(ctx.get<Channel>(i+1), ctx);\n    } else {\n      chan.clear();\n    }\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (! chan.isValid())\n      continue;\n    if (Channel *obj = chan.toChannelObj()) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid channel at index %\" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (! chan.isValid())\n      continue;\n    if (! chan.linkChannelObj(ctx.get<Channel>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearContacts() {\n  // Clear contacts\n  for (int i=0; i<NUM_CONTACTS; i++)\n    ContactElement(data(ADDR_CONTACTS+i*CONTACT_SIZE)).clear();\n}\n\nbool\nDM1701Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode contacts\n  for (unsigned int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (i < ctx.count<DigitalContact>())\n      cont.fromContactObj(ctx.get<DMRContact>(i+1));\n    else\n      cont.clear();\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (! cont.isValid())\n      continue;\n    if (DMRContact *obj = cont.toContactObj()) {\n      ctx.config()->contacts()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid contact at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearZones() {\n  // Clear zones & zone extensions\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement(data(ADDR_ZONES+i*ZONE_SIZE)).clear();\n    ZoneExtElement(data(ADDR_ZONEEXTS+i*ZONEEXT_SIZE)).clear();\n  }\n}\n\nbool\nDM1701Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES + i*ZONE_SIZE));\n    ZoneExtElement ext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    zone.clear();\n    ext.clear();\n    if (i < ctx.config()->zones()->count()) {\n      zone.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n      if (ctx.config()->zones()->zone(i)->B()->count() || (16 < ctx.config()->zones()->zone(i)->A()->count()))\n        ext.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n    }\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (Zone *obj = zone.toZoneObj()) {\n      ctx.config()->zones()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nDM1701Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (! zone.linkZone(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n    ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    if (! zoneext.linkZoneObj(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearGroupLists() {\n  for (int i=0; i<NUM_GROUPLISTS; i++)\n    GroupListElement(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE)).clear();\n}\n\nbool\nDM1701Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (i < ctx.config()->rxGroupLists()->count())\n      glist.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx);\n    else\n      glist.clear();\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (RXGroupList *obj = glist.toGroupListObj(ctx)) {\n      ctx.config()->rxGroupLists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (! glist.linkGroupListObj(ctx.get<RXGroupList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearScanLists() {\n  // Clear scan lists\n  for (int i=0; i<NUM_SCANLISTS; i++)\n    ScanListElement(data(ADDR_SCANLISTS + i*SCANLIST_SIZE)).clear();\n}\n\nbool\nDM1701Codeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Scanlists\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (i < ctx.config()->scanlists()->count())\n      scan.fromScanListObj(ctx.config()->scanlists()->scanlist(i), ctx);\n    else\n      scan.clear();\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (ScanList *obj = scan.toScanListObj(ctx)) {\n      ctx.config()->scanlists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid scanlist at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nDM1701Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (! scan.linkScanListObj(ctx.get<ScanList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link scan list at index\" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nDM1701Codeplug::clearMenuSettings() {\n  MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear();\n}\n\nvoid\nDM1701Codeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear();\n}\n\nbool\nDM1701Codeplug::encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode settings\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nDM1701Codeplug::decodeButtonSetttings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(ctx.config());\n}\n\n\nvoid\nDM1701Codeplug::clearPrivacyKeys() {\n  EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear();\n}\n\nbool\nDM1701Codeplug::encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n  // First, reset keys\n  clearPrivacyKeys();\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  return keys.fromCommercialExt(ctx.config()->commercialExtension(), ctx);\n}\n\nbool\nDM1701Codeplug::decodePrivacyKeys(Context &ctx, const ErrorStack &err) {\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // Decode element\n  if (! keys.updateCommercialExt(ctx)) {\n    errMsg(err) << \"Cannot create encryption extension.\";\n    return false;\n  }\n  return true;\n}\n\n\nvoid\nDM1701Codeplug::clearTextMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\n\nbool\nDM1701Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err);\n}\n\nbool\nDM1701Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).decode(ctx, err);\n}\n\n\nvoid\nDM1701Codeplug::clearEmergencySystems() {\n  EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear();\n  for (int i=0; i<NUM_EMERGENCY_SYSTEMS; i++)\n    EmergencySystemElement(data(ADDR_EMERGENCY_SYSTEMS + i*EMERGENCY_SYSTEM_SIZE)).clear();\n}\n\nvoid\nDM1701Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_A)).clear();\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_B)).clear();\n}\n\n\nvoid\nDM1701Codeplug::clearPositioningSystems() {\n  // pass, nothing to do.\n}\n\nbool\nDM1701Codeplug::encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Pass, nothing to do\n  return true;\n}\n\nbool\nDM1701Codeplug::createPositioningSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  // Pass, nothing to do\n  return true;\n}\n\nbool\nDM1701Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  // Nothing to do\n  return true;\n}\n"
  },
  {
    "path": "lib/dm1701_codeplug.hh",
    "content": "#ifndef DM1701_CODEPLUG_HH\n#define DM1701_CODEPLUG_HH\n\n#include \"tyt_codeplug.hh\"\n\n/** Device specific implementation of the codeplug for the Baofeng DM-1701.\n *\n * The codeplug consists of two segments. The first segment starts at address @c 0x002000 and ends at\n * address @c 0x040000. The second segment starts at address @c 0x110000 and ends at @c 0x1a0000. The\n * segments must align with @c 0x400 (1024 bytes).\n *\n * @section dm1701cpl Codeplug structure within radio\n * The codeplug structure is reverse engineered almost completely and can be programmed from\n * scratch. That is, it is not necessary to update an existing codeplug on the radio.\n * <table>\n *  <tr><th>Start</th>    <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x002000-0x040000</th></tr>\n *  <tr><td>0x002000</td> <td>0x00200c</td> <td>0x0000c</td> <td>Timestamp see @c TyTCodeplug::TimestampElement.</td></tr>\n *  <tr><td>0x00200c</td> <td>0x002040</td> <td>0x00034</td> <td>Reserved, filled with 0xff. </td></tr>\n *  <tr><td>0x002040</td> <td>0x0020f0</td> <td>0x000b0</td> <td>General settings see @c DM1701Codeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x0020f0</td> <td>0x002100</td> <td>0x00010</td> <td>Menu settings, see @c TyTCodeplug::MenuSettingsElement</td></tr>\n *  <tr><td>0x002100</td> <td>0x002140</td> <td>0x00040</td> <td>Button config, see @c DM1701Codeplug::ButtonSettingsElement.</td></tr>\n *  <tr><td>0x002140</td> <td>0x002180</td> <td>0x00040</td> <td>Reserved, filled with 0xff.</td></tr>\n *  <tr><td>0x002180</td> <td>0x0059c0</td> <td>0x03840</td> <td>50 Text messages @ 0x120 bytes each.</td></tr>\n *  <tr><td>0x0059c0</td> <td>0x005a70</td> <td>0x000b0</td> <td>Privacy keys, see @c TyTCodeplug::EncryptionElement.</td></tr>\n *  <tr><td>0x005a70</td> <td>0x005a80</td> <td>0x00010</td> <td>Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement.</td></td>\n *  <tr><td>0x005a80</td> <td>0x005f80</td> <td>0x00500</td> <td>Emergency systems, see @c TyTCodeplug::EmergencySystemElement.</td></td>\n *  <tr><td>0x005f80</td> <td>0x00ec20</td> <td>0x08ca0</td> <td>Reserved, filled with 0xff.</td></td>\n *  <tr><td>0x00ec20</td> <td>0x0149e0</td> <td>0x05dc0</td> <td>250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement.</td></tr>\n *  <tr><td>0x0149e0</td> <td>0x018860</td> <td>0x03e80</td> <td>250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement.</td></tr>\n *  <tr><td>0x018860</td> <td>0x01edf0</td> <td>0x06590</td> <td>250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement.</td></tr>\n *  <tr><td>0x01edf0</td> <td>0x02ef00</td> <td>0x10110</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x02ef00</td> <td>0x02ef40</td> <td>0x00040</td> <td>VFO A channel, see @c DM1701Codeplug::VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef40</td> <td>0x02ef80</td> <td>0x00040</td> <td>VFO B channel, see @c DM1701Codeplug::VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef80</td> <td>0x031000</td> <td>0x02080</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x031000</td> <td>0x03eac0</td> <td>0x0dac0</td> <td>250 Zone-extensions @ 0xe0 bytes each, see @c DM1701Codeplug::ZoneExtElement.</td></tr>\n *  <tr><td>0x03eac0</td> <td>0x03ec40</td> <td>0x00180</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x03ec40</td> <td>0x03ed40</td> <td>0x00100</td> <td>16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement.</td></tr>\n *  <tr><td>0x03ed40</td> <td>0x040000</td> <td>0x012c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><th colspan=\"4\">Second segment 0x110000-0x1a0000</th></tr>\n *  <tr><td>0x110000</td> <td>0x13ee00</td> <td>0x2ee00</td> <td>3000 Channels @ 0x40 bytes each, see @c DM1701Codeplug::ChannelElement.</td></tr>\n *  <tr><td>0x13ee00</td> <td>0x140000</td> <td>0x01200</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x140000</td> <td>0x197e40</td> <td>0x57e40</td> <td>10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement.</td></tr>\n *  <tr><td>0x197e40</td> <td>0x1a0000</td> <td>0x081c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n * </table>\n *\n * @ingroup dm1701 */\nclass DM1701Codeplug : public TyTCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Extends the common @c TyTCodeplug::ChannelElement to implement the DM-1701 specific settings.\n   *\n   * Memory layout of the channel (size 0x0040 bytes):\n   * @verbinclude dm1701_channel.txt */\n  class ChannelElement: public TyTCodeplug::ChannelElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns @c true if the squelch is 'tight'. */\n    virtual bool tightSquelchEnabled() const;\n    /** Enables/disables tight squelch. */\n    virtual void enableTightSquelch(bool enable);\n\n    /** Returns @c true if the reversed burst is enabled. */\n    virtual bool reverseBurst() const;\n    /** Enables/disables reverse burst. */\n    virtual void enableReverseBurst(bool enable);\n\n    /** Returns the power of this channel. */\n    virtual Channel::Power power() const;\n    /** Sets the power of this channel. */\n    virtual void setPower(Channel::Power pwr);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    void fromChannelObj(const Channel *c, Context &ctx);\n  };\n\n  /** Extends the @c ChannelElement to implement the VFO channel settings for the DM-1701.\n   * This class is an extension of the normal @c ChannelElement that only implements the step-size\n   * feature and encodes it where the name used to be. Thus the memory layout and size is identical\n   * to the normal channel. */\n  class VFOChannelElement: public ChannelElement\n  {\n  protected:\n    /** Constructor from pointer to memory. */\n    VFOChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor from pointer to memory. */\n    VFOChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~VFOChannelElement();\n\n    QString name() const;\n    void setName(const QString &txt);\n\n    /** Returns the step-size for the VFO channel. */\n    virtual unsigned stepSize() const;\n    /** Sets the step-size for the VFO channel in Hz. */\n    virtual void setStepSize(unsigned ss_hz);\n  };\n\n  /** Extends the common @c TyTCodeplug::GeneralSettingsElement to implement the DM-1701 specific\n   * settings.\n   *\n   * Memory layout of the settings (size 0x00b0 bytes):\n   * @verbinclude dm1701_settings.txt */\n  class GeneralSettingsElement: public TyTCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns @c true, if VFO A is in channel mode. */\n    virtual bool channelModeA() const;\n    /** Enables/disables the channel mode for VFO A. */\n    virtual void enableChannelModeA(bool enable);\n    /** Returns @c true, if VFO B is in channel mode. */\n    virtual bool channelModeB() const;\n    /** Enables/disables the channel mode for VFO B. */\n    virtual void enableChannelModeB(bool enable);\n\n    /** Returns @c true, if the radio is in channel (and not VFO) mode. */\n    virtual bool channelMode() const;\n    /** Enable/disable channel mode. */\n    virtual void enableChannelMode(bool enable);\n\n    /** Returns @c true if group-call match is enabled. */\n    virtual bool groupCallMatch() const;\n    /** Enables/disables group-call match. */\n    virtual void enableGroupCallMatch(bool enable);\n    /** Returns @c true if private-call match is enabled. */\n    virtual bool privateCallMatch() const;\n    /** Enables/disables private-call match. */\n    virtual void enablePrivateCallMatch(bool enable);\n\n    /** Returns the time-zone. */\n    virtual QTimeZone timeZone() const;\n    /** Sets the time-zone. */\n    virtual void setTimeZone(const QTimeZone &zone);\n\n    /** Returns the channel hang time in ms. */\n    virtual unsigned channelHangTime() const;\n    /** Sets the channel hang time in ms. */\n    virtual void setChannelHangTime(unsigned dur);\n\n    /** Encodes the general settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from general settings. */\n    virtual bool updateConfig(Config *config);\n  };\n\n  /** Extens the common @c TyTCodeplug::ButtonSettingsElement to implement the DM-1701 specific\n   * settings.\n   *\n   * Memory layout of the setting (size 0x0014 bytes):\n   * @verbinclude dm1701_buttonsettings.txt */\n  class ButtonSettingsElement: public TyTCodeplug::ButtonSettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ButtonSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ButtonSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the action for a short press on side button 3. */\n    virtual ButtonAction sideButton3Short() const;\n    /** Sets the action for a short press on side button 3. */\n    virtual void setSideButton3Short(ButtonAction action);\n    /** Returns the action for a long press on side button 3. */\n    virtual ButtonAction sideButton3Long() const;\n    /** Sets the action for a long press on side button 3. */\n    virtual void setSideButton3Long(ButtonAction action);\n\n    /** Returns the action for a short press on programmable button 1. */\n    virtual ButtonAction progButton1Short() const;\n    /** Sets the action for a short press on programmable button 1. */\n    virtual void setProgButton1Short(ButtonAction action);\n    /** Returns the action for a long press on programmable button 1. */\n    virtual ButtonAction progButton1Long() const;\n    /** Sets the action for a long press on programmable button 1. */\n    virtual void setProgButton1Long(ButtonAction action);\n\n    /** Returns the action for a short press on programmable button 2. */\n    virtual ButtonAction progButton2Short() const;\n    /** Sets the action for a short press on programmable button 2. */\n    virtual void setProgButton2Short(ButtonAction action);\n    /** Returns the action for a long press on programmable button 2. */\n    virtual ButtonAction progButton2Long() const;\n    /** Sets the action for a long press on programmable button 2. */\n    virtual void setProgButton2Long(ButtonAction action);\n\n    /** Encodes the button settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from button settings. */\n    virtual bool updateConfig(Config *config);\n  };\n\n  /** Extended zone data for the DM-1701.\n   * The zone definition @c ZoneElement contains only a single set of 16 channels. For each zone\n   * definition, there is a zone extension which extends a zone to zwo sets of 64 channels each.\n   *\n   * Memory layout of encoded zone extension:\n   * @verbinclude dm1701_zoneext.txt */\n  class ZoneExtElement: public Codeplug::Element\n  {\n  protected:\n    /** Constructor. */\n    ZoneExtElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneExtElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ZoneExtElement();\n\n    void clear();\n\n    /** Returns @c true if the n-th member index for list A is set. */\n    virtual bool hasMemberIndexA(unsigned n) const;\n    /** Returns the n-th member index of the channel list for A. */\n    virtual uint16_t memberIndexA(unsigned n) const;\n    /** Sets the n-th member index of the channel list for A. */\n    virtual void setMemberIndexA(unsigned n, uint16_t idx);\n    /** Returns @c true if the n-th member index for list B is set. */\n    virtual bool hasMemberIndexB(unsigned n) const;\n    /** Returns the n-th member index of the channel list for B. */\n    virtual uint16_t memberIndexB(unsigned n) const;\n    /** Returns the n-th member index of the channel list for B. */\n    virtual void setMemberIndexB(unsigned n, uint16_t idx);\n\n    /** Encodes the given zone. */\n    virtual bool fromZoneObj(const Zone *zone, Context &ctx);\n    /** Links the given zone object.\n     * That is, extends channel list A and populates channel list B. */\n    virtual bool linkZoneObj(Zone *zone, Context &ctx);\n  };\n\npublic:\n  /** Constructor. */\n  explicit DM1701Codeplug(QObject *parent = nullptr);\n  /** Destructor. */\n  virtual ~DM1701Codeplug();\n\npublic:\n  void clearTimestamp();\n  bool encodeTimestamp();\n\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSetttings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPositioningSystems();\n  bool encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool createPositioningSystems(Context &ctx, const ErrorStack &err);\n  bool linkPositioningSystems(Context &ctx, const ErrorStack &err);\n\n  void clearPrivacyKeys();\n  bool encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool decodePrivacyKeys(Context &ctx, const ErrorStack &err);\n\n  void clearTextMessages();\n  bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err);\n  bool decodeTextMessages(Context &ctx, const ErrorStack &err);\n\n  void clearMenuSettings();\n  void clearEmergencySystems();\n  /** Resets VFO settings. */\n  virtual void clearVFOSettings();\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int messages() { return 0x002180; }\n    /// @endcond\n  };\n};\n\n#endif // DM1701_CODEPLUG_HH\n"
  },
  {
    "path": "lib/dm1701_filereader.cc",
    "content": "#include \"dm1701_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_FILE_ADDR   0x00002225\n#define SEGMENT0_TARGET_ADDR 0x00002000\n#define SEGMENT0_SIZE        0x0003e000\n#define SEGMENT1_FILE_ADDR   0x00040235\n#define SEGMENT1_TARGET_ADDR 0x00110000\n#define SEGMENT1_SIZE        0x00090000\n\nbool\nDM1701FileReader::read(const QString &filename, DM1701Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (852533 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 852533 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  if (! file.seek(SEGMENT1_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR);\n  n = SEGMENT1_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dm1701_filereader.hh",
    "content": "#ifndef DM1701_FILEREADER_HH\n#define DM1701_FILEREADER_HH\n\n#include \"dm1701_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is still pretty simple. The first part of the file consists of\n * a mal-formed DFU file. This contains a single image with a single element containing the\n * first section of the memory written to the device. The second section is then added as-is\n * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ.\n *\n * <table>\n *  <tr><th>File Start</th> <th>Memory Start</th> <th>Size</th></tr>\n *  <tr><td>0x002225</td>   <td>0x002000</td>     <td>0x03e000</td></tr>\n *  <tr><td>0x040235</td>   <td>0x110000</td>     <td>0x090000</td></tr>\n * </table>\n *\n * @ingroup dm1701 */\nclass DM1701FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err Error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, DM1701Codeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // DM1701_FILEREADER_HH\n"
  },
  {
    "path": "lib/dm1701_limits.cc",
    "content": "#include \"dm1701_limits.hh\"\n#include \"dm1701_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"scanlist.hh\"\n#include \"zone.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nDM1701Limits::DM1701Limits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 122197;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"introLine2\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::Mid),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(DM1701Codeplug::GeneralSettingsElement::Limit::bootPasswordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, 3000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef({ScanList::staticMetaObject})},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::Mid),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name\n          { \"A\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems. */\n  add(\"positioning\", new RadioLimitList({\n        { DMRAPRSSystem::staticMetaObject, 0, 16, new RadioLimitObject {\n          { \"name\", new RadioLimitStringIgnored() },\n          { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n          { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n          { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) );\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList {\n           {BasicEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::basicKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{4}\")}\n            }},\n           {AESEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{32}\")}\n            }} }\n        }\n      });\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/dm1701_limits.hh",
    "content": "#ifndef DM1701LIMITS_HH\n#define DM1701LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the configuration limits for the Baofeng DM-1701.\n * @ingroup dm1701 */\nclass DM1701Limits : public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DM1701Limits(QObject *parent=nullptr);\n};\n\n#endif // UV390LIMITS_HH\n"
  },
  {
    "path": "lib/dm32uv.cc",
    "content": "#include \"dm32uv.hh\"\n#include \"dm32uv_limits.hh\"\n#include \"dm32uv_interface.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of Baofeng DM32UV weird address mapping\n * ********************************************************************************************* */\nDM32UV::AddressMap::AddressMap()\n  : _toVirtual(), _toPhysical()\n{\n  // pass...\n}\n\nvoid\nDM32UV::AddressMap::map(uint32_t phys, uint32_t virt) {\n  phys >>= 12; virt >>= 12;\n  _toVirtual[phys] = virt;\n  _toPhysical[virt] = phys;\n}\n\nbool\nDM32UV::AddressMap::virtualIsMapped(uint32_t addr) const {\n  return _toPhysical.contains(addr>>12);\n}\n\nbool\nDM32UV::AddressMap::physicalIsMapped(uint32_t addr) const {\n  return _toVirtual.contains(addr>>12);\n}\n\nuint32_t\nDM32UV::AddressMap::toPhysical(uint32_t virt) const {\n  uint32_t offset = virt & 0xfff;\n  if (! _toPhysical.contains(virt>>12))\n    return std::numeric_limits<uint32_t>::max();\n  return (_toPhysical[virt>>12]<<12) | offset;\n}\n\nuint32_t\nDM32UV::AddressMap::toVirtual(uint32_t phys) const {\n  uint32_t offset = phys & 0xfff;\n  if (! _toVirtual.contains(phys>>12))\n    return std::numeric_limits<uint32_t>::max();\n  return (_toVirtual[phys>>12]<<12) | offset;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of Baofeng DM32UV\n * ********************************************************************************************* */\nRadioLimits *DM32UV::_limits = nullptr;\n\nDM32UV::DM32UV(DM32UVInterface *dev, QObject *parent)\n  : Radio{parent}, _dev(dev), _radioName(\"Baofeng DM-32UV\"), _codeplug()\n{\n  // pass...\n}\n\nRadioInfo\nDM32UV::defaultRadioInfo() {\n  return RadioInfo(\n    RadioInfo::DM32UV, \"dm32uv\", \"DM-32UV\", \"Baofeng\", {DM32UVInterface::interfaceInfo()});\n}\n\nconst QString &\nDM32UV::name() const {\n  return _radioName;\n}\n\nconst RadioLimits &\nDM32UV::limits() const {\n  if (nullptr == _limits)\n    _limits = new DM32UVLimits();\n  return *_limits;\n}\n\nconst Codeplug &\nDM32UV::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nDM32UV::codeplug() {\n  return _codeplug;\n}\n\n\nbool\nDM32UV::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n\n  start();\n\n  return true;\n}\n\n\nbool\nDM32UV::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (! (_config = config))\n    return false;\n  _codeplugFlags = flags;\n\n  _task = StatusUpload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  start();\n  return true;\n}\n\n\nbool\nDM32UV::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  _callsigns.encode(db, selection);\n\n  _task = StatusUploadCallsigns;\n  _errorStack = err;\n\n  if (selection.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  start();\n\n  return true;\n}\n\n\nbool\nDM32UV::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(flags);\n\n  errMsg(err) << \"Satellite config upload is not implemented yet.\";\n\n  return false;\n}\n\n\nvoid\nDM32UV::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    emit downloadStarted();\n\n    if (! download(_errorStack)) {\n      _dev->read_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit downloadFinished(this, &codeplug());\n    //_config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! upload()) {\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->write_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! uploadCallsigns()) {\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->write_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n\n    emit uploadComplete(this);\n  }\n}\n\n\nbool\nDM32UV::download(const ErrorStack &err) {\n  // First, we need to get the codeplug address map\n  DM32UV::AddressMap addressMap;\n  if (! _dev->getAddressMap(addressMap, err)) {\n    errMsg(err) << \"Cannot read codeplug from device: Cannot get address map.\";\n    return false;\n  }\n\n  // Allocate all mapped memory regions:\n  static Range<unsigned int> codeplugMemoryRange = {0x3000, 0x68000};\n  foreach (uint32_t virtualBlockAddress, addressMap.mappedVirtual()) {\n    if (codeplugMemoryRange.contains(virtualBlockAddress))\n      _codeplug.image(0).addElement(virtualBlockAddress, Offset::blockSize());\n  }\n\n  if (! _dev->read_start(0, 0, err)) {\n    errMsg(err) << \"Cannot start reading codeplug.\";\n    return false;\n  }\n\n  // Read all allocated memory regions (sorted by physical address).\n  uint32_t blockCount = 0;\n  foreach (uint32_t physicalBlockAddress, addressMap.mappedPhysical()) {\n    auto progress = (100*++blockCount)/addressMap.mappedPhysical().count();\n    emit downloadProgress(progress);\n    uint32_t virtualBlockAddress = addressMap.toVirtual(physicalBlockAddress);\n    if (! codeplugMemoryRange.contains(virtualBlockAddress))\n      continue;\n    if (! _dev->read(0, physicalBlockAddress, _codeplug.data(virtualBlockAddress), Offset::blockSize())) {\n      errMsg(err) << \"Cannot read codeplug block from \"\n                  << Qt::hex << physicalBlockAddress << \"h (virt. \"\n                  << Qt::hex << virtualBlockAddress << \"h).\";\n      return false;\n    }\n  }\n\n  if (! _dev->read_finish(err)) {\n    errMsg(err) << \"Cannot finish codeplug download.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UV::upload(const ErrorStack &err) {\n  // First, we need to get the codeplug address map\n  DM32UV::AddressMap addressMap;\n  if (! _dev->getAddressMap(addressMap, err)) {\n    errMsg(err) << \"Cannot read codeplug from device: Cannot get address map.\";\n    return false;\n  }\n\n  // Allocate all mapped memory regions:\n  static Range<unsigned int> codeplugMemoryRange = {0x3000, 0x68000};\n  foreach (uint32_t virtualBlockAddress, addressMap.mappedVirtual()) {\n    if (codeplugMemoryRange.contains(virtualBlockAddress))\n      _codeplug.image(0).addElement(virtualBlockAddress, Offset::blockSize());\n  }\n\n  if (! _dev->read_start(0, 0, err)) {\n    errMsg(err) << \"Cannot start reading codeplug.\";\n    return false;\n  }\n\n  // Read all allocated memory regions (sorted by physical address).\n  uint32_t blockCount = 0;\n  foreach (uint32_t physicalBlockAddress, addressMap.mappedPhysical()) {\n    emit uploadProgress((50*++blockCount)/addressMap.mappedPhysical().count());\n    uint32_t virtualBlockAddress = addressMap.toVirtual(physicalBlockAddress);\n    if (! codeplugMemoryRange.contains(virtualBlockAddress))\n      continue;\n    if (! _dev->read(0, physicalBlockAddress, _codeplug.data(virtualBlockAddress), Offset::blockSize())) {\n      errMsg(err) << \"Cannot read codeplug block from \"\n                  << Qt::hex << physicalBlockAddress << \"h (virt. \"\n                  << Qt::hex << virtualBlockAddress << \"h).\";\n      return false;\n    }\n  }\n\n  if (! _dev->read_finish(err)) {\n    errMsg(err) << \"Cannot finish codeplug read.\";\n    return false;\n  }\n\n  // Now, encode codeplug in physical address space, may override large portions and may also add\n  // new portions to the codeplug.\n  if (! _codeplug.encode(_config, _codeplugFlags, err)) {\n    errMsg(err) << \"Cannot encode codeplug from config.\";\n    return false;\n  }\n\n  if (! _dev->write_start(0,0,err)) {\n    errMsg(err) << \"Cannot start codeplug write´.\";\n    return false;\n  }\n\n  // Now mark all blocks with their virtual address\n  // and write them to the associated pyhsical address or to 0xff000 if not yet allocated.\n  for (unsigned int blk=0; blk<(unsigned int)_codeplug.image(0).numElements(); blk++) {\n    emit uploadProgress(50 + (50*blk)/_codeplug.image(0).numElements());\n    auto element = _codeplug.image(0).element(blk);\n    auto virtualBlockAddress = element.address();\n    element.data()[Offset::blockSize()-1] = (virtualBlockAddress>>12);\n    auto physicalBlockAddress = addressMap.virtualIsMapped(virtualBlockAddress)\n        ? addressMap.toPhysical(virtualBlockAddress) : 0xff<<12;\n    if (! _dev->write(0, physicalBlockAddress, _codeplug.data(virtualBlockAddress), Offset::blockSize())) {\n      errMsg(err) << \"Cannot write codeplug block to \"\n                  << Qt::hex << physicalBlockAddress << \"h (virt. \"\n                  << Qt::hex << virtualBlockAddress << \"h).\";\n      return false;\n    }\n  }\n\n  if (! _dev->write_finish(err)) {\n    errMsg(err) << \"Cannot finish codeplug write.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UV::uploadCallsigns(const ErrorStack &err) {\n  if (! _dev->write_start(0,0,err)) {\n    errMsg(err) << \"Cannot start codeplug write´.\";\n    return false;\n  }\n\n  // Now mark all blocks with their virtual address\n  // and write them to the associated pyhsical address or to 0xff000 if not yet allocated.\n  for (unsigned int blk=0; blk<(unsigned int)_callsigns.image(0).numElements(); blk++) {\n    emit uploadProgress(100*blk/_callsigns.image(0).numElements());\n    auto element = _callsigns.image(0).element(blk);\n    auto blockAddress = element.address();\n    if (! _dev->write(0, blockAddress, _callsigns.data(blockAddress), Offset::blockSize())) {\n      errMsg(err) << \"Cannot write codeplug block to \"\n                  << Qt::hex << blockAddress << \"h.\";\n      return false;\n    }\n  }\n\n  if (! _dev->write_finish(err)) {\n    errMsg(err) << \"Cannot finish codeplug write.\";\n    return false;\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/dm32uv.hh",
    "content": "/** @defgroup dm32uv Baofeng DM32UV\n * @ingroup dsc */\n#ifndef DM32UV_HH\n#define DM32UV_HH\n\n#include \"radio.hh\"\n#include \"dm32uv_codeplug.hh\"\n#include \"dm32uv_callsigndb.hh\"\n#include <QObject>\n#include <QHash>\n\n// Forward declaration\nclass DM32UVInterface;\n\n\n/** Represents a Baofeng DM32UV.\n * @ingroup dm32uv */\nclass DM32UV : public Radio\n{\n  Q_OBJECT\n\npublic:\n  /** Implements a memory map between physical and virtual codeplug addresses.\n   * The addresses, the codeplug is encoded in, are virtual. These codeplug blocks are then written\n   * to different physical addresses. */\n  class AddressMap {\n  public:\n    /** Default constructor. */\n    AddressMap();\n    /** Default copy constructor. */\n    AddressMap(const AddressMap &other) = default;\n\n    /** Adds an address mapping. */\n    void map(uint32_t phys, uint32_t virt);\n\n    /** Returns @c true, if the virtual address is mapped. */\n    bool virtualIsMapped(uint32_t virt) const;\n    /** Returns @c true, if the physical address is mapped. */\n    bool physicalIsMapped(uint32_t virt) const;\n\n    /** Maps the given physical address to a virtual codeplug address.\n      * If physical address is unknown -- that is, not mapped --\n      * @c std::numeric_limits<uint32_t>::max() gets returned. */\n    uint32_t toVirtual(uint32_t phys) const;\n    /** Maps the given virtual codeplug address to a physical memory address.\n     * If virtual address is unknown -- that is, not mapped --\n     * @c std::numeric_limits<uint32_t>::max() gets returned. */\n    uint32_t toPhysical(uint32_t virt) const;\n\n    /** Returns the map from physical to virtual addresses, sorted by physical addresses. */\n    inline QList<uint32_t> mappedPhysical() const {\n      QList<uint32_t> addrs;\n      for (auto prefix: _toVirtual.keys())\n        addrs.append(prefix<<12);\n      return addrs;\n    }\n\n    /** Returns the map from virtual to physical addresses, sorted by virtual addresses. */\n    inline QList<uint32_t> mappedVirtual() const {\n      QList<uint32_t> addrs;\n      for (auto prefix: _toPhysical.keys())\n        addrs.append(prefix<<12);\n      return addrs;\n    }\n\n  protected:\n    /** Holds only the prefix address mapping from physical to virtual addresses. That is\n     *  @c addr>>12. */\n    QMap<uint32_t, uint32_t> _toVirtual;\n    /** Holds only the prefix address mapping from virtual to physical addresses. That is\n     *  @c addr>>12. */\n    QMap<uint32_t, uint32_t> _toPhysical;\n  };\n\npublic:\n  /** Default constructor. */\n  explicit DM32UV(DM32UVInterface *device=nullptr, QObject *parent=nullptr);\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\n  const QString &name() const override;\n\n  const RadioLimits &limits() const override;\n\n  const Codeplug &codeplug() const override;\n  Codeplug &codeplug() override;\n\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack()) override;\n  bool startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) override;\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) override;\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) override;\n\n\npublic:\n  /** Returns a set of supported firmware versions. */\n  static inline QSet<QByteArray> supportedFirmwareVersions() { return {\"DM32.01.L01.048\"}; }\n\npublic:\n  /** Some constants. */\n  struct Offset {\n    /** Size for each codeplug block. */\n    static constexpr uint32_t blockSize() { return 0x1000; }\n  };\n\nprivate:\n  virtual bool download(const ErrorStack &err=ErrorStack());\n  virtual bool upload(const ErrorStack &err=ErrorStack());\n  virtual bool uploadCallsigns(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n  void run() override;\n\nprotected:\n  /** The interface to the radio. */\n  DM32UVInterface *_dev;\n  /** The name of the device. */\n  QString _radioName;\n  /** Holds the flags to control assembly and upload of code-plugs. */\n  Codeplug::Flags _codeplugFlags;\n  /** Encoded / read codeplug. */\n  DM32UVCodeplug _codeplug;\n  /** Encoded call-sign db. */\n  DM32UVCallsignDB _callsigns;\n  /** The generic configuration. */\n  Config *_config;\n  /** A weak reference to the user-database. */\n  UserDatabase *_userDB;\n\n\nprivate:\n  /** Holds the singleton instance of the radio limits. */\n  static RadioLimits *_limits;\n};\n\n\n#endif // DM32UV_HH\n"
  },
  {
    "path": "lib/dm32uv_callsigndb.cc",
    "content": "#include \"dm32uv_callsigndb.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of call sign DB header\n * ********************************************************************************************* */\nDM32UVCallsignDB::HeaderElement::HeaderElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nDM32UVCallsignDB::HeaderElement::clear() {\n  memset(_data, 0, size());\n}\n\nunsigned int\nDM32UVCallsignDB::HeaderElement::count() const {\n  return getUInt32_le(Offset::count());\n}\n\nvoid\nDM32UVCallsignDB::HeaderElement::setCount(unsigned int count) {\n  setUInt32_le(Offset::count(), count);\n}\n\n\n\n\n/* ********************************************************************************************* *\n * Implementation of call sign DB entry\n * ********************************************************************************************* */\nDM32UVCallsignDB::EntryElement::EntryElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::clear() {\n  memset(_data, 0xff, size());\n  setId(0);\n}\n\nbool\nDM32UVCallsignDB::EntryElement::isValid() const {\n  return 0 != id();\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::name() const {\n  return readASCII(Offset::name(), Limit::name(), 0x00);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::name(), 0x00);\n}\n\n\nunsigned int\nDM32UVCallsignDB::EntryElement::id() const {\n  return getUInt32_le(Offset::id());\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setId(unsigned int id) {\n  setUInt32_le(Offset::id(), id);\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::callsign() const {\n  return readASCII(Offset::callsign(), Limit::callsign(), 0xff);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setCallsign(const QString &cal) {\n  writeASCII(Offset::callsign(), cal, Limit::callsign(), 0xff);\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::city() const {\n  return readASCII(Offset::city(), Limit::city(), 0xff);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setCity(const QString &city) {\n  writeASCII(Offset::city(), city, Limit::city(), 0xff);\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::province() const {\n  return readASCII(Offset::province(), Limit::province(), 0xff);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setProvince(const QString &province) {\n  writeASCII(Offset::province(), province, Limit::province(), 0xff);\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::country() const {\n  return readASCII(Offset::country(), Limit::country(), 0xff);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setCountry(const QString &country) {\n  writeASCII(Offset::country(), country, Limit::country(), 0xff);\n}\n\n\nQString\nDM32UVCallsignDB::EntryElement::remark() const {\n  return readASCII(Offset::remark(), Limit::remark(), 0xff);\n}\n\nvoid\nDM32UVCallsignDB::EntryElement::setRemark(const QString &remark) {\n  writeASCII(Offset::remark(), remark, Limit::remark(), 0xff);\n}\n\n\nbool\nDM32UVCallsignDB::EntryElement::encode(const UserDatabase::User &user, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setName(user.name);\n  setCallsign(user.call);\n  setId(user.id);\n  setCity(user.city);\n  setCountry(user.country);\n  setRemark(user.comment);\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of call sign DB\n * ********************************************************************************************* */\nDM32UVCallsignDB::DM32UVCallsignDB(QObject *parent)\n  : CallsignDB{parent}\n{\n  addImage(\"Call-sign DB for Baofeng DM-32UV\");\n}\n\nbool\nDM32UVCallsignDB::encode(UserDatabase *db, const Flags &selection, const ErrorStack &err) {\n  // Limit number of entries.\n  unsigned int count = std::min(Limit::entries(), (unsigned int)db->count());\n  if (selection.hasCountLimit())\n    count = std::min(count, (unsigned int)selection.countLimit());\n\n  // Allocate first block\n  if (! isAllocated(Offset::count()))\n    image(0).addElement(Offset::count(), Offset::betweenBlocks());\n\n  // Store count\n  HeaderElement(data(Offset::count())).setCount(count);\n\n  // Encode first count entries.\n  for (unsigned int i=0; i<count; i++) {\n    unsigned int block = i/Limit::entriesPerBlock();\n    unsigned int withinBlock = i%Limit::entriesPerBlock();\n    unsigned int blockAddr = (0 == block) ? Offset::block0()\n                                          : Offset::block1() + (block-1)*Offset::betweenBlocks();\n    if (! isAllocated(blockAddr))\n      image(0).addElement(blockAddr, Offset::betweenBlocks());\n    if (! EntryElement(data(blockAddr + withinBlock*EntryElement::size())).encode(db->user(i))) {\n      errMsg(err) << \"Cannot encode DB entry at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dm32uv_callsigndb.hh",
    "content": "#ifndef DM32UV_CALLSIGNDB_HH\n#define DM32UV_CALLSIGNDB_HH\n\n#include \"callsigndb.hh\"\n#include \"codeplug.hh\"\n#include \"userdatabase.hh\"\n\n\n/** Implements the call-sign DB for the DM32UV. *\n * @ingroup dm32uv */\nclass DM32UVCallsignDB : public CallsignDB\n{\n  Q_OBJECT\n\npublic:\n  class HeaderElement: public Codeplug::Element\n  {\n  public:\n    /** Constructor. */\n    explicit HeaderElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    void clear() override;\n\n    /** Returns the number of entries. */\n    virtual unsigned int count() const;\n    /** Sets the number of entries. */\n    virtual void setCount(unsigned int count);\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Codeplug::Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n\n  class EntryElement: public Codeplug::Element\n  {\n  public:\n    /** Constructor. */\n    explicit EntryElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static unsigned int size() { return 0x005c; }\n\n    void clear() override;\n    bool isValid() const override;\n\n    /** Returns the name of the entry. */\n    virtual QString name() const;\n    /** Sets the name of the entry. */\n    virtual void setName(const QString &name);\n\n    /** Returns the DMR id of the entry. */\n    virtual unsigned int id() const;\n    /** Sets the DMR id of the entry. */\n    virtual void setId(unsigned int id);\n\n    /** Retruns the callsign field. */\n    virtual QString callsign() const;\n    /** Sets the callsign field. */\n    virtual void setCallsign(const QString &cal);\n\n    /** Retruns the city field. */\n    virtual QString city() const;\n    /** Sets the city field. */\n    virtual void setCity(const QString &city);\n\n    /** Retruns the province field. */\n    virtual QString province() const;\n    /** Sets the province field. */\n    virtual void setProvince(const QString &provice);\n\n    /** Retruns the country field. */\n    virtual QString country() const;\n    /** Sets the country field. */\n    virtual void setCountry(const QString &country);\n\n    /** Retruns the remark field. */\n    virtual QString remark() const;\n    /** Sets the remark field. */\n    virtual void setRemark(const QString &remark);\n\n    /** Encode entry. */\n    virtual bool encode(const UserDatabase::User &user, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the entry. */\n    struct Limit: Codeplug::Element::Limit {\n      /// Maximum name length.\n      static constexpr unsigned int name()     { return 16; }\n      static constexpr unsigned int callsign() { return 8; }\n      static constexpr unsigned int city()     { return 16; }\n      static constexpr unsigned int province() { return 16; }\n      static constexpr unsigned int country()  { return 16; }\n      static constexpr unsigned int remark()   { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Codeplug::Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()     { return 0x0000; }\n      static constexpr unsigned int id()       { return 0x0010; }\n      static constexpr unsigned int callsign() { return 0x0014; }\n      static constexpr unsigned int city()     { return 0x001c; }\n      static constexpr unsigned int province() { return 0x002c; }\n      static constexpr unsigned int country()  { return 0x003c; }\n      static constexpr unsigned int remark()   { return 0x004c; }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  explicit DM32UVCallsignDB(QObject *parent = nullptr);\n\n  bool encode(UserDatabase *db, const Flags &selection,const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits for the DB. */\n  struct Limit: Codeplug::Element::Limit {\n    /// Maximum number of db entries.\n    static constexpr unsigned int entries()         { return 50000; }\n    // Number of entires per block.\n    static constexpr unsigned int entriesPerBlock() { return 44; }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset: Codeplug::Element::Limit {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int count()           { return 0x278000; }\n    static constexpr unsigned int block0()          { return 0x278010; }\n    static constexpr unsigned int block1()          { return 0x279000; }\n    static constexpr unsigned int betweenBlocks()   { return 0x1000; }\n    /// @endcond\n  };\n};\n\n#endif // DM32UV_CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/dm32uv_codeplug.cc",
    "content": "#include \"dm32uv_codeplug.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include <QRegularExpression>\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDM32UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::clear() {\n  fill(0x00);\n}\n\nQString\nDM32UVCodeplug::ChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nFrequency\nDM32UVCodeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(getBCD8_le(Offset::rxFrequency())*10);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setRXFrequency(const Frequency &freq) {\n  setBCD8_le(Offset::rxFrequency(), freq.inHz()/10);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::validTXFrequency() const {\n  return std::numeric_limits<uint32_t>::max() != getUInt32_le(Offset::txFrequency());\n}\n\nFrequency\nDM32UVCodeplug::ChannelElement::txFrequency() const {\n  if (validTXFrequency())\n    return Frequency::fromHz(getBCD8_le(Offset::txFrequency())*10);\n  return Frequency();\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setTXFrequency(const Frequency &freq) {\n  setBCD8_le(Offset::txFrequency(), freq.inHz()/10);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::clearTXFrequency() {\n  setUInt32_le(Offset::txFrequency(), std::numeric_limits<uint32_t>::max());\n}\n\n\nDM32UVCodeplug::ChannelElement::ChannelType\nDM32UVCodeplug::ChannelElement::channelType() const {\n  return (ChannelType)getUInt2(Offset::channelType());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setChannelType(ChannelType type) {\n  setUInt2(Offset::channelType(), (unsigned int)type);\n}\n\n\nChannel::Power\nDM32UVCodeplug::ChannelElement::power() const {\n  switch ((Power)getUInt2(Offset::power())) {\n  case Power::Low: return Channel::Power::Low;\n  case Power::Medium: return Channel::Power::Mid;\n  case Power::High: return Channel::Power::High;\n  }\n  return Channel::Power::Low;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt2(Offset::power(), (unsigned int)Power::Low);\n    break;\n  case Channel::Power::Mid:\n    setUInt2(Offset::power(), (unsigned int)Power::Medium);\n    break;\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setUInt2(Offset::power(), (unsigned int)Power::High);\n    break;\n  }\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::loneWorkerEnabled() const {\n  return getBit(Offset::loneWorker());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableLoneWorker(bool enable) {\n  setBit(Offset::loneWorker(), enable);\n}\n\n\nFMChannel::Bandwidth\nDM32UVCodeplug::ChannelElement::bandwidth() const {\n  if (getBit(Offset::bandwidth())) {\n    return FMChannel::Bandwidth::Wide;\n  }\n  return FMChannel::Bandwidth::Narrow;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  switch (bw) {\n    case FMChannel::Bandwidth::Narrow: clearBit(Offset::bandwidth()); break;\n    case FMChannel::Bandwidth::Wide: setBit(Offset::bandwidth()); break;\n  }\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::validScanListIndex() const {\n  return 0 != getUInt5(Offset::scanListIndex());\n}\n\nunsigned int\nDM32UVCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt5(Offset::scanListIndex())-1;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setScanListIndex(unsigned int idx) {\n  setUInt5(Offset::scanListIndex(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::clearScanListIndex() {\n  setUInt5(Offset::scanListIndex(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::talkaroundEnabled() const {\n  return !getBit(Offset::preventTalkaround());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableTalkaround(bool enabled) {\n  setBit(Offset::preventTalkaround(), !enabled);\n}\n\n\nDM32UVCodeplug::ChannelElement::Admit\nDM32UVCodeplug::ChannelElement::admitCriterion() const {\n  return (DM32UVCodeplug::ChannelElement::Admit) getUInt2(Offset::admitCriterion());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setAdmitCriterion(Admit admit) {\n  setUInt2(Offset::admitCriterion(), (unsigned int)admit);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::rxDMRAPRSEnabled() const {\n  return getBit(Offset::rxDMRAPRS());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableRXDMRAPRS(bool enable) {\n  setBit(Offset::rxDMRAPRS(), enable);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::emergencyNotificationEnabled() const {\n  return getBit(Offset::emergencyNotification());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableEmergencyNotification(bool enable) {\n  setBit(Offset::emergencySystemIndex(), enable);\n}\n\nbool\nDM32UVCodeplug::ChannelElement::emergencyACKEnabled() const {\n  return getBit(Offset::emergencyACK());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableEmergencyACK(bool enable) {\n  setBit(Offset::emergencyACK(), enable);\n}\n\nbool\nDM32UVCodeplug::ChannelElement::validEmergencySystemIndex() const {\n  return 0 != getUInt3(Offset::emergencySystemIndex());\n}\n\nunsigned int\nDM32UVCodeplug::ChannelElement::emergencySystemIndex() const {\n  return getUInt3(Offset::emergencySystemIndex())-1;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setEmergencySystemIndex(unsigned int idx) {\n  setUInt3(Offset::emergencySystemIndex(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::clearEmergencySystemIndex() {\n  setUInt3(Offset::emergencySystemIndex(), 0);\n}\n\n\nLevel\nDM32UVCodeplug::ChannelElement::squelchLevel() const {\n  return Level::fromValue(getUInt4(Offset::squelchLevel()), Limit::squelchLevel());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setSquelchLevel(Level level) {\n  setUInt4(Offset::squelchLevel(), level.mapTo(Limit::squelchLevel()));\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::rxOnlyEnabled() const {\n  return getBit(Offset::rxOnly());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableRXOnly(bool enable) {\n  setBit(Offset::rxOnly(), enable);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::dmrAPRSEnabled() const {\n  return getBit(Offset::dmrAPRS());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableDMRAPRS(bool enable) {\n  return setBit(Offset::dmrAPRS(), enable);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::privateCallACKEnabled() const {\n  return getBit(Offset::privateCallACK());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enablePrivateCallACK(bool enable) {\n  return setBit(Offset::privateCallACK(), enable);\n}\n\nbool\nDM32UVCodeplug::ChannelElement::dataACKEnabled() const {\n  return getBit(Offset::dataACK());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableDataACK(bool enable) {\n  return setBit(Offset::dataACK(), enable);\n}\n\nbool\nDM32UVCodeplug::ChannelElement::dcdmEnabled() const {\n  return getBit(Offset::dcdm());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableDCDM(bool enable) {\n  return setBit(Offset::dcdm(), enable);\n}\n\n\nDMRChannel::TimeSlot\nDM32UVCodeplug::ChannelElement::timeslot() const {\n  return getBit(Offset::timeslot()) ? DMRChannel::TimeSlot::TS2 : DMRChannel::TimeSlot::TS1;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setTimeslot(DMRChannel::TimeSlot ts) {\n  setBit(Offset::timeslot(), DMRChannel::TimeSlot::TS2 == ts);\n}\n\nunsigned int\nDM32UVCodeplug::ChannelElement::colorCode() const {\n  return getUInt4(Offset::colorcode());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setColorCode(unsigned int cc) {\n  setUInt4(Offset::colorcode(), cc);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::encryptionEnabled() const {\n  return getBit(Offset::encryptionEnable());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableEncryption(bool enable) {\n  setBit(Offset::encryptionEnable(), enable);\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::validGroupListIndex() const {\n  return 0 != getUInt6(Offset::groupListIndex());\n}\n\nunsigned int\nDM32UVCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt6(Offset::groupListIndex())-1;\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setGroupListIndex(unsigned int idx) {\n  setUInt6(Offset::groupListIndex(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::clearGroupListIndex() {\n  setUInt6(Offset::groupListIndex(), 0);\n}\n\n\nunsigned int\nDM32UVCodeplug::ChannelElement::dmrAPRSChannelIndex() const {\n  return getUInt4(Offset::dmrAPRSChannelIndex());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setDMRAPRSChannelIndex(unsigned int idx) {\n  setUInt4(Offset::dmrAPRSChannelIndex(), idx);\n}\n\n\nSelectiveCall\nDM32UVCodeplug::ChannelElement::rxTone() const {\n  return decodeSelectiveCall(getUInt16_le(Offset::rxTone()));\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setRXTone(const SelectiveCall &tone) {\n  setUInt16_le(Offset::rxTone(), encodeSelectiveCall(tone));\n}\n\nSelectiveCall\nDM32UVCodeplug::ChannelElement::txTone() const {\n  return decodeSelectiveCall(getUInt16_le(Offset::txTone()));\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setTXTone(const SelectiveCall &tone) {\n  setUInt16_le(Offset::txTone(), encodeSelectiveCall(tone));\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::voxEnabled() const {\n  return getBit(Offset::vox());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::enableVOX(bool enable) {\n  return setBit(Offset::vox(), enable);\n}\n\n\nunsigned int\nDM32UVCodeplug::ChannelElement::dmrIdIndex() const {\n  return getUInt8(Offset::dmrIdIndex());\n}\n\nvoid\nDM32UVCodeplug::ChannelElement::setDMRIdIndex(unsigned int id) {\n  setUInt8(Offset::dmrIdIndex(), id);\n}\n\n\nChannel *\nDM32UVCodeplug::ChannelElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  Channel *ch = nullptr;\n\n  if ((ChannelType::FM == channelType()) || (ChannelType::FMFixed == channelType())) {\n    if ((rxFrequency().inMHz() >= 118) && (rxFrequency().inMHz() <= 137)) {\n      AMChannel *am = new AMChannel(); ch = am;\n      am->setTXFrequency(Frequency());\n      am->setSquelch(squelchLevel());\n    } else {\n      FMChannel *fm = new FMChannel(); ch = fm;\n      fm->setBandwidth(bandwidth());\n      switch (admitCriterion()) {\n      case Admit::Always: fm->setAdmit(FMChannel::Admit::Always); break;\n      case Admit::ChannelFree:\n      case Admit::ToneOrCCMatch: fm->setAdmit(FMChannel::Admit::Free); break;\n      case Admit::ToneMismatch: fm->setAdmit(FMChannel::Admit::Tone); break;\n      }\n      fm->setSquelch(squelchLevel());\n      fm->setRXTone(rxTone());\n      fm->setTXTone(txTone());\n      fm->extended()->enableTalkaround(talkaroundEnabled());\n    }\n  } else if ((ChannelType::DMR == channelType()) || (ChannelType::DMRFixed == channelType())) {\n    DMRChannel *dmr = new DMRChannel(); ch = dmr;\n    switch (admitCriterion()) {\n    case Admit::Always: dmr->setAdmit(DMRChannel::Admit::Always); break;\n    case Admit::ToneOrCCMatch: dmr->setAdmit(DMRChannel::Admit::ColorCode); break;\n    case Admit::ChannelFree: dmr->setAdmit(DMRChannel::Admit::Free); break;\n    default: dmr->setAdmit(DMRChannel::Admit::Always); break;\n    }\n    dmr->setTimeSlot(timeslot());\n    dmr->setColorCode(colorCode());\n    dmr->extended()->enableTalkaround(talkaroundEnabled());\n    dmr->extended()->enableDCDM(dcdmEnabled());\n    dmr->extended()->enableLoneWorker(loneWorkerEnabled());\n    dmr->extended()->enablePrivateCallConfirm(privateCallACKEnabled());\n    dmr->extended()->enableDataConfirm(dataACKEnabled());\n  } else {\n    errMsg(err) << \"Unknown channel type \" << (unsigned int)channelType() << \".\";\n    return nullptr;\n  }\n\n  ch->setName(name());\n  ch->setRXFrequency(rxFrequency());\n  ch->setTXFrequency(validTXFrequency() ? txFrequency() : Frequency());\n  ch->setPower(power());\n  ch->setRXOnly(rxOnlyEnabled());\n\n  if (voxEnabled())\n    ch->setVOXDefault();\n  else\n    ch->disableVOX();\n\n  return ch;\n}\n\nbool\nDM32UVCodeplug::ChannelElement::link(Channel *channel, Context &ctx, const ErrorStack &err) const {\n  // Link scan list\n  if (validScanListIndex()) {\n    if (! ctx.has<ScanList>(scanListIndex())) {\n      errMsg(err) << \"Cannot link channel: Scan list with index \" << scanListIndex()\n                  << \" not defined.\";\n      return false;\n    }\n    channel->setScanList(ctx.get<ScanList>(scanListIndex()));\n  }\n\n  if (channel->is<DMRChannel>()) {\n    auto dmr = channel->as<DMRChannel>();\n    // Link group list\n    if (validGroupListIndex()) {\n      if (! ctx.has<RXGroupList>(groupListIndex())) {\n        errMsg(err) << \"Cannot resolve group list index \" << groupListIndex() << \".\";\n        return false;\n      }\n      dmr->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n    }\n\n    // Link DMR ID\n    if (! ctx.has<DMRRadioID>(dmrIdIndex())) {\n      errMsg(err) << \"Cannot resolve radio id index \" << dmrIdIndex() << \".\";\n      return false;\n    }\n    if (0 == dmrIdIndex())\n      dmr->setRadioId(DefaultRadioID::get());\n    else\n      dmr->setRadioId(ctx.get<DMRRadioID>(dmrIdIndex()));\n\n    if (dmrAPRSEnabled() && ctx.has<DMRAPRSSystem>(dmrAPRSChannelIndex())) {\n      dmr->setAPRS(ctx.get<DMRAPRSSystem>(dmrAPRSChannelIndex()));\n    }\n  }    \n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::ChannelElement::encode(const Channel *channel, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  clear();\n  setName(channel->name());\n  setRXFrequency(channel->rxFrequency());\n  if (channel->txFrequency().isZero())\n    clearTXFrequency();\n  else\n    setTXFrequency(channel->txFrequency());\n  // AM channels are encoded as FM with rx frequency within the air band\n  setChannelType(channel->is<DMRChannel>() ? ChannelType::DMR : ChannelType::FM);\n  setPower(channel->power());\n  enableRXOnly(channel->rxOnly());\n  if (channel->defaultVOX())\n    enableVOX(ctx.config()->settings()->audio()->voxEnabled());\n  else\n    enableVOX(! channel->voxDisabled());\n\n  if (! channel->scanListRef()->isNull())\n    setScanListIndex(ctx.index(channel->scanList()));\n  else\n    clearScanListIndex();\n\n  if (channel->is<DMRChannel>()) {\n    auto dmr = channel->as<DMRChannel>();\n    setBandwidth(FMChannel::Bandwidth::Narrow);\n    switch (dmr->admit()) {\n      case DMRChannel::Admit::Always: setAdmitCriterion(Admit::Always); break;\n      case DMRChannel::Admit::Free: setAdmitCriterion(Admit::ChannelFree); break;\n      case DMRChannel::Admit::ColorCode: setAdmitCriterion(Admit::ToneOrCCMatch); break;\n    }\n    setSquelchLevel(ctx.config()->settings()->audio()->squelch());\n    setTimeslot(dmr->timeSlot());\n    setColorCode(dmr->colorCode());\n    if (dmr->commercialExtension()) {\n      enableEncryption(! dmr->commercialExtension()->encryptionKeyRef()->isNull());\n      if (! dmr->commercialExtension()->encryptionKeyRef()->isNull()) {\n        // reverse engineer encryption key index!\n      }\n    }\n    clearGroupListIndex();\n    if (! dmr->groupListRef()->isNull()) {\n      setGroupListIndex(ctx.index(dmr->groupList()));\n    }\n    if (dmr->radioIdRef()->is<DefaultRadioID>()) {\n      setDMRIdIndex(ctx.index(ctx.config()->settings()->defaultId()));\n    } else {\n      setDMRIdIndex(ctx.index(dmr->radioId()));\n    }\n    enableTalkaround(dmr->extended()->talkaround());\n    enableDCDM(dmr->extended()->dcdm());\n    enableLoneWorker(dmr->extended()->loneWorker());\n    enablePrivateCallACK(dmr->extended()->privateCallConfirm());\n    enableDataACK(dmr->extended()->dataConfirm());\n    if (!dmr->aprsRef()->isNull() && dmr->aprsRef()->is<DMRAPRSSystem>()) {\n      enableDMRAPRS(true);\n      setDMRAPRSChannelIndex(ctx.index(dmr->aprsRef()->as<DMRAPRSSystem>()));\n    } else {\n      enableDMRAPRS(false);\n    }\n  } else if (channel->is<FMChannel>()) {\n    auto fm = channel->as<FMChannel>();\n    setBandwidth(fm->bandwidth());\n    switch (fm->admit()) {\n    case FMChannel::Admit::Always: setAdmitCriterion(Admit::Always); break;\n    case FMChannel::Admit::Free: setAdmitCriterion(Admit::ChannelFree); break;\n    case FMChannel::Admit::Tone: setAdmitCriterion(Admit::ToneOrCCMatch); break;\n    }\n    setSquelchLevel(fm->defaultSquelch() ? ctx.config()->settings()->audio()->squelch() : fm->squelch());\n    setRXTone(fm->rxTone());\n    setTXTone(fm->txTone());\n    enableTalkaround(fm->extended()->talkaround());\n  } else if (channel->is<AMChannel>()) {\n    auto am = channel->as<AMChannel>();\n    clearTXFrequency();\n    setSquelchLevel(am->defaultSquelch() ? ctx.config()->settings()->audio()->squelch() : am->squelch());\n    enableRXOnly(true);\n  }\n\n  return true;\n}\n\n\nSelectiveCall\nDM32UVCodeplug::ChannelElement::decodeSelectiveCall(uint16_t code) {\n  if (0xffff == code)\n    return SelectiveCall();\n\n  uint8_t type = code >> 14;\n  code &= 0x3fff;\n\n  if (0 == type) {\n    return SelectiveCall(double((code>>8)&0xf)*10 + double((code>>4)&0xf)*1 + double(code & 0xf)/10);\n  } else if ((1 == type) || (2 == type)) {\n    return SelectiveCall(code, 2 == type);\n  }\n\n  return SelectiveCall();\n}\n\nuint16_t\nDM32UVCodeplug::ChannelElement::encodeSelectiveCall(const SelectiveCall &tone) {\n  if (! tone.isValid())\n    return 0xffff;\n  if (tone.isCTCSS())\n    return (((tone.mHz()/10000) % 10) << 8) | (((tone.mHz()/1000) % 10) << 4) | ((tone.mHz()/100) % 10);\n  if (tone.isDCS())\n    return ((tone.isInverted() ? 2 : 1) << 14) | tone.octalCode();\n  return 0xffff;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ChannelBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nunsigned int\nDM32UVCodeplug::ChannelBankElement::channelCount() const {\n  return getUInt16_le(Offset::channelCount());\n}\n\nvoid\nDM32UVCodeplug::ChannelBankElement::setChannelCount(unsigned int n) {\n  setUInt16_le(Offset::channelCount(), n);\n}\n\n\nunsigned int\nDM32UVCodeplug::ChannelBankElement::channelBank(unsigned int index) {\n  if (index < Limit::channelsInBlock0())\n    return 0;\n  index -= Limit::channelsInBlock0();\n  return 1 + index/Limit::channelsPerBlock();\n}\n\nunsigned int\nDM32UVCodeplug::ChannelBankElement::indexInBank(unsigned int index) {\n  if (index < Limit::channelsInBlock0())\n    return index;\n  index -= Limit::channelsInBlock0();\n  return index % Limit::channelsPerBlock();\n}\n\nunsigned int\nDM32UVCodeplug::ChannelBankElement::bankCount(unsigned int channelCount) {\n  if (channelCount <= Limit::channelsInBlock0())\n    return 1;\n  channelCount -= Limit::channelsInBlock0();\n  return 1 + channelCount / Limit::channelsPerBlock() +\n    ((0 != channelCount % Limit::channelsPerBlock()) ? 1 : 0);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ChannelExtensionElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nDM32UVCodeplug::ChannelExtensionElement::clear() {\n  setUInt16_le(0x0000, 0x0001);\n  clearContactIndex();\n}\n\n\nbool\nDM32UVCodeplug::ChannelExtensionElement::hasContactIndex() const {\n  unsigned int contactIndex = (((unsigned int)getUInt4(Offset::indexMSN()))<<8)\n      | (unsigned int) getUInt8(Offset::indexLSB());\n  return 0x0000 != contactIndex;\n}\n\nunsigned int\nDM32UVCodeplug::ChannelExtensionElement::contactIndex() const {\n  unsigned int contactIndex = (((unsigned int)getUInt4(Offset::indexMSN()))<<8)\n      | (unsigned int) getUInt8(Offset::indexLSB());\n  return contactIndex - 1;\n}\n\nvoid\nDM32UVCodeplug::ChannelExtensionElement::setContactIndex(unsigned int idx) {\n  idx = std::min(ContactBankElement::Limit::contacts(), idx+1);\n  uint8_t msn = (idx >> 8), lsb = (idx & 0xff);\n  setUInt4(Offset::indexMSN(), msn);\n  setUInt8(Offset::indexLSB(), lsb);\n}\n\nvoid\nDM32UVCodeplug::ChannelExtensionElement::clearContactIndex() {\n  setUInt4(Offset::indexMSN(), 0);\n  setUInt8(Offset::indexLSB(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ChannelExtensionElement::decode(Channel *ch, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ch); Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\nbool\nDM32UVCodeplug::ChannelExtensionElement::link(Channel *ch, Context &ctx, const ErrorStack &err) const {\n  if (!ch->is<DMRChannel>() || !hasContactIndex())\n    return true;\n\n  if (! ctx.has<DMRContact>(contactIndex())) {\n    errMsg(err) << \"Cannot resolve contact index \" << contactIndex() << \".\";\n    return false;\n  }\n\n  ch->as<DMRChannel>()->setContact(ctx.get<DMRContact>(contactIndex()));\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::ChannelExtensionElement::encode(const Channel *ch, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  clear();\n\n  if (! ch->is<DMRChannel>())\n    return true;\n\n  auto dch = ch->as<DMRChannel>();\n  if (dch->contactRef()->isNull())\n    clearContactIndex();\n  else\n    setContactIndex(ctx.index(dch->contact()));\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ContactElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ContactElement::ContactElement(uint8_t *data, size_t size)\n  : Element{data,size}\n{\n  // pass...\n}\n\nDM32UVCodeplug::ContactElement::ContactElement(uint8_t *data)\n  : Element{data, size()}\n{\n  // pass...\n}\n\n\nQString\nDM32UVCodeplug::ContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::ContactElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nDMRContact::Type\nDM32UVCodeplug::ContactElement::callType() const {\n  switch ((Type)getUInt8(Offset::callType())) {\n  case Type::Private: return DMRContact::Type::PrivateCall;\n  case Type::Group: return DMRContact::Type::GroupCall;\n  case Type::All: return DMRContact::Type::AllCall;\n  }\n  return DMRContact::Type::PrivateCall;\n}\n\nvoid\nDM32UVCodeplug::ContactElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::Type::PrivateCall:\n    setUInt8(Offset::callType(), (unsigned int)Type::Private);\n    break;\n  case DMRContact::Type::GroupCall:\n    setUInt8(Offset::callType(), (unsigned int)Type::Group);\n    break;\n  case DMRContact::Type::AllCall:\n    setUInt8(Offset::callType(), (unsigned int)Type::All);\n    break;\n  }\n}\n\nunsigned int\nDM32UVCodeplug::ContactElement::dmrId() const {\n  return getUInt24_le(Offset::dmrId());\n}\n\n\nvoid\nDM32UVCodeplug::ContactElement::setDMRId(unsigned int id) {\n  setUInt24_le(Offset::dmrId(), id);\n}\n\nDMRContact *\nDM32UVCodeplug::ContactElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new DMRContact(callType(), name(), dmrId());\n}\n\n\nbool\nDM32UVCodeplug::ContactElement::encode(const DMRContact *contact, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setCallType(contact->type());\n  setName(contact->name());\n  setDMRId(contact->number());\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ContactIndexElement::ContactBitmapElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ContactIndexElement::ContactBitmapElement::ContactBitmapElement(uint8_t *ptr)\n  : Codeplug::InvertedBitmapElement(ptr, size())\n{\n  // pass...\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ContactIndexElement::EntryElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ContactIndexElement::EntryElement::EntryElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass...\n}\n\nDM32UVCodeplug::ContactIndexElement::EntryElement::EntryElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::EntryElement::clear() {\n  setUInt16_le(0x0000, 0xffff);\n}\n\nbool\nDM32UVCodeplug::ContactIndexElement::EntryElement::isValid() const {\n  return 0xffff != getUInt16_le(0x0000);\n}\n\nDMRContact::Type\nDM32UVCodeplug::ContactIndexElement::EntryElement::callType() const {\n  switch ((Type)(getUInt16_le(0x0000) >> 12)) {\n  case Type::Private: return DMRContact::Type::PrivateCall;\n  case Type::Group: return DMRContact::Type::GroupCall;\n  case Type::All: return DMRContact::Type::AllCall;\n  }\n  return DMRContact::Type::PrivateCall;\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::EntryElement::setCallType(DMRContact::Type type) {\n  uint16_t value = (getUInt16_le(0x0000) & 0x0fff);\n  switch (type) {\n  case DMRContact::Type::PrivateCall: value |= ((unsigned int)Type::Private)<<12; break;\n  case DMRContact::Type::GroupCall: value |= ((unsigned int)Type::Group)<<12; break;\n  case DMRContact::Type::AllCall: value |= ((unsigned int)Type::All)<<12; break;\n  }\n  setUInt16_le(0x0000, value);\n}\n\n\nunsigned int\nDM32UVCodeplug::ContactIndexElement::EntryElement::index() const {\n  return (getUInt16_le(0x0000) & 0x0fff)-1;\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::EntryElement::setIndex(unsigned int idx) {\n  setUInt16_le(0x0000, (getUInt16_le(0x0000) & 0xf000) | ((idx+1) & 0x0fff));\n}\n\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ContactIndexElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ContactIndexElement::ContactIndexElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass...\n}\n\nDM32UVCodeplug::ContactIndexElement::ContactIndexElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nunsigned int\nDM32UVCodeplug::ContactIndexElement::contactCount() const {\n  return getUInt16_le(Offset::contactCount());\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::setContactCount(unsigned int n) {\n  setUInt16_le(Offset::contactCount(), n);\n}\n\nunsigned int\nDM32UVCodeplug::ContactIndexElement::groupCallCount() const {\n  return getUInt16_le(Offset::groupCount());\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::setGroupCallCount(unsigned int n) {\n  setUInt16_le(Offset::groupCount(), n);\n}\n\nunsigned int\nDM32UVCodeplug::ContactIndexElement::privateCallCount() const {\n  return getUInt16_le(Offset::privateCount());\n}\n\nvoid\nDM32UVCodeplug::ContactIndexElement::setPrivateCallCount(unsigned int n) {\n  setUInt16_le(Offset::privateCount(), n);\n}\n\n\nDM32UVCodeplug::ContactIndexElement::ContactBitmapElement\nDM32UVCodeplug::ContactIndexElement::bitmap() const {\n  return ContactBitmapElement(_data + Offset::bitmap());\n}\n\nDM32UVCodeplug::ContactIndexElement::EntryElement\nDM32UVCodeplug::ContactIndexElement::indexEntry(unsigned int n) {\n  return EntryElement(_data + Offset::index() + n*EntryElement::size());\n}\n\nDM32UVCodeplug::ContactIndexElement::EntryElement\nDM32UVCodeplug::ContactIndexElement::sortedIndexEntry(unsigned int n) {\n  return EntryElement(_data + Offset::sorted() + n*EntryElement::size());\n}\n\n\nbool\nDM32UVCodeplug::ContactIndexElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setContactCount(ctx.count<DigitalContact>());\n  unsigned int privatCallCount = 0, groupCallCount = 0;\n\n  unsigned int cc = contactCount();\n  QVector<unsigned int> indices; indices.reserve(cc);\n  for (unsigned int idx=0; idx<contactCount(); idx++) {\n    if (DMRContact::Type::PrivateCall == ctx.get<DMRContact>(idx)->type())\n      privatCallCount++;\n    else if (DMRContact::Type::GroupCall == ctx.get<DMRContact>(idx)->type())\n      groupCallCount++;\n    indexEntry(idx).setCallType(ctx.get<DMRContact>(idx)->type());\n    indexEntry(idx).setIndex(idx);\n    indices.append(idx);\n  }\n\n  setPrivateCallCount(privatCallCount);\n  setGroupCallCount(groupCallCount);\n  bitmap().enableFirst(ctx.count<DigitalContact>());\n\n  // Sort indices w.r.t. associated DMR id:\n  std::sort(indices.begin(), indices.end(), [&ctx](unsigned ia, unsigned ib)->bool {\n    return ctx.get<DMRContact>(ia)->number() < ctx.get<DMRContact>(ib)->number();\n  });\n  for (unsigned int sidx=0; sidx<contactCount(); sidx++) {\n    sortedIndexEntry(sidx).setCallType(ctx.get<DMRContact>(indices[sidx])->type());\n    sortedIndexEntry(sidx).setIndex(indices[sidx]);\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GroupListElement\n * ******************************************************************************************** */\nDM32UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass...\n}\n\nDM32UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nQString\nDM32UVCodeplug::GroupListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::GroupListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nbool\nDM32UVCodeplug::GroupListElement::validId(unsigned int n) {\n  return 0 != getUInt24_le(Offset::ids() + n*Offset::betweenIds());\n}\n\nunsigned int\nDM32UVCodeplug::GroupListElement::id(unsigned int n) {\n  return getUInt24_le(Offset::ids() + n*Offset::betweenIds());\n}\n\nvoid\nDM32UVCodeplug::GroupListElement::setId(unsigned int n, unsigned int idx) {\n  setUInt24_le(Offset::ids() + n*Offset::betweenIds(), idx);\n}\n\nvoid\nDM32UVCodeplug::GroupListElement::clearId(unsigned int n) {\n  setUInt24_le(Offset::ids() + n*Offset::betweenIds(), 0);\n}\n\n\nRXGroupList *\nDM32UVCodeplug::GroupListElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(ctx);\n  return new RXGroupList(name());\n}\n\nbool\nDM32UVCodeplug::GroupListElement::link(RXGroupList *gl, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (unsigned int i=0; i<Limit::contacts(); i++) {\n    if (! validId(i))\n      continue;\n    DMRContact* contact = nullptr;\n    for (unsigned int j=0; j<ctx.count<DigitalContact>(); j++) {\n      if (id(i) == ctx.get<DMRContact>(j)->number()) {\n        contact = ctx.get<DMRContact>(j);\n        break;\n      }\n    }\n    if (nullptr == contact) {\n      contact = new DMRContact(DMRContact::GroupCall, \"Group Call\", id(i));\n      ctx.config()->contacts()->add(contact);\n      logInfo() << \"Cannot find DMR contact with ID \" << id(i) << \", create one.\";\n    } else if (DMRContact::Type::GroupCall != contact->type()) {\n      logWarn() << \"Cannot add non-group-call contact to group list.\";\n      continue;\n    }\n    gl->addContact(contact);\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::GroupListElement::encode(const RXGroupList *gl, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  setName(gl->name());\n  for (unsigned int i=0; i<std::min((unsigned int)gl->count(), Limit::contacts()); i++) {\n    setId(i, gl->contacts()->get(i)->as<DMRContact>()->number());\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GroupListBankElement::GroupListBitmapElement\n * ******************************************************************************************** */\nDM32UVCodeplug::GroupListBankElement::GroupListBitmapElement::GroupListBitmapElement(uint8_t *ptr)\n  : BitmapElement(ptr, 4)\n{\n  // pass...\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GroupListBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass...\n}\n\nDM32UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nDM32UVCodeplug::GroupListBankElement::GroupListBitmapElement\nDM32UVCodeplug::GroupListBankElement::bitmap() const {\n  return GroupListBitmapElement(_data + Offset::bitmap());\n}\n\nDM32UVCodeplug::GroupListElement\nDM32UVCodeplug::GroupListBankElement::groupList(unsigned int n) const {\n  return GroupListElement(_data + Offset::groupLists() + n*GroupListElement::size());\n}\n\n\nbool\nDM32UVCodeplug::GroupListBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0,c=0; i<Limit::groupLists(); i++) {\n    if (! bitmap().isEncoded(i))\n      continue;\n    auto gl = groupList(i).decode(ctx, err);\n    if (nullptr == gl) {\n      errMsg(err) << \"Cannot decode \" << i << \"-th group list.\";\n      return false;\n    }\n    ctx.add(gl, c++);\n    ctx.config()->rxGroupLists()->add(gl);\n  }\n\n  return true;\n}\n\nbool\nDM32UVCodeplug::GroupListBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0,c=0; i<Limit::groupLists(); i++) {\n    if (! bitmap().isEncoded(i))\n      continue;\n    auto gl = ctx.get<RXGroupList>(c++);\n    if (! groupList(i).link(gl, ctx, err)) {\n      errMsg(err) << \"Cannot link \" << i << \"-th group list.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  bitmap().enableFirst(ctx.count<RXGroupList>());\n  for (unsigned int i=0; i<ctx.count<RXGroupList>(); i++) {\n    auto gl = ctx.get<RXGroupList>(i);\n    if (! groupList(i).encode(gl, ctx, err)) {\n      errMsg(err) << \"Cannot encode \" << i << \"-th group list.\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RadioIdElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RadioIdElement::RadioIdElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nunsigned int\nDM32UVCodeplug::RadioIdElement::id() const {\n  return getUInt24_le(Offset::id());\n}\n\nvoid\nDM32UVCodeplug::RadioIdElement::setId(unsigned int id) {\n  setUInt24_le(Offset::id(), id);\n}\n\n\nQString\nDM32UVCodeplug::RadioIdElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::RadioIdElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nDMRRadioID *\nDM32UVCodeplug::RadioIdElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new DMRRadioID(name(), id());\n}\n\n\nbool\nDM32UVCodeplug::RadioIdElement::encode(const DMRRadioID *id, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(id->name());\n  setId(id->number());\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RadioIdBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RadioIdBankElement::RadioIdBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nunsigned int\nDM32UVCodeplug::RadioIdBankElement::idCount() const {\n  return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::RadioIdBankElement::setIdCount(unsigned int n) {\n  setUInt8(Offset::count(), n);\n}\n\n\nDM32UVCodeplug::RadioIdElement\nDM32UVCodeplug::RadioIdBankElement::id(unsigned int n) const {\n  return RadioIdElement(_data + Offset::ids() + n*Offset::betweenIds());\n}\n\n\nbool\nDM32UVCodeplug::RadioIdBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<idCount(); i++) {\n    auto rid = id(i).decode(ctx, err);\n    if (nullptr == rid) {\n      errMsg(err) << \"Cannot decode radio ID at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(rid, i);\n    ctx.config()->radioIDs()->add(rid);\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::RadioIdBankElement::encode(Context &ctx, const ErrorStack &err) {\n  setIdCount(ctx.count<DMRRadioID>());\n\n  for (unsigned int i=0; i<std::min(ctx.count<DMRRadioID>(), Limit::ids()); i++) {\n    if (! id(i).encode(ctx.get<DMRRadioID>(i), err)) {\n      errMsg(err) << \"Cannot encode DMR radio ID '\" << ctx.get<DMRRadioID>(i)->name()\n                  << \"' at idx \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ZoneElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nQString\nDM32UVCodeplug::ZoneElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nDM32UVCodeplug::ZoneElement::setName(const QString &name) {\n  return writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nunsigned int\nDM32UVCodeplug::ZoneElement::channelCount() const {\n  return getUInt8(Offset::channelCount());\n}\n\nvoid\nDM32UVCodeplug::ZoneElement::setChannelCount(unsigned int n) {\n  setUInt8(Offset::channelCount(), n);\n}\n\n\nbool\nDM32UVCodeplug::ZoneElement::channelIndexValid(unsigned int n) const {\n  return 0 != getUInt16_le(Offset::channels() + n*sizeof(uint16_t));\n}\n\nunsigned int\nDM32UVCodeplug::ZoneElement::channelIndex(unsigned int n) const {\n  return getUInt16_le(Offset::channels() + n*sizeof(uint16_t)) - 1;\n}\n\nvoid\nDM32UVCodeplug::ZoneElement::setChannelIndex(unsigned int n, unsigned int idx) {\n  setUInt16_le(Offset::channels() + n*sizeof(uint16_t), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ZoneElement::clearChannelIndex(unsigned int n) {\n  setUInt16_le(Offset::channels() + n*sizeof(uint16_t), 0);\n}\n\n\nZone *\nDM32UVCodeplug::ZoneElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new Zone(name());\n}\n\nbool\nDM32UVCodeplug::ZoneElement::link(Zone *zone, Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<channelCount(); i++) {\n    if (! channelIndexValid(i))\n      continue;\n    if (! ctx.has<Channel>(channelIndex(i))) {\n      errMsg(err) << \"Cannot link zone, \" << i << \"-th channel index \" << channelIndex(i)\n                  << \" not defined.\";\n      return false;\n    }\n    zone->A()->add(ctx.get<Channel>(channelIndex(i)));\n  }\n\n  return true;\n}\n\nbool\nDM32UVCodeplug::ZoneElement::encode(const Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(zone->name());\n  setChannelCount(std::min(Limit::channels(), (unsigned int)zone->A()->count()));\n  for (unsigned int i=0; i<channelCount(); i++) {\n    setChannelIndex(i, ctx.index(zone->A()->get(i)->as<Channel>()));\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ZoneBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nunsigned int\nDM32UVCodeplug::ZoneBankElement::count() const {\n  return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::setCount(unsigned int n) {\n  return setUInt8(Offset::count(), n);\n}\n\n\nbool\nDM32UVCodeplug::ZoneBankElement::channelIndexAValid() const {\n  return 0 != getUInt8(Offset::channelIndexA());\n}\n\nunsigned int\nDM32UVCodeplug::ZoneBankElement::channelIndexA() const {\n  return getUInt8(Offset::channelIndexA())-1;\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::setChannelIndexA(unsigned int idx) {\n  setUInt8(Offset::channelIndexA(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::clearChannelIndexA() {\n  setUInt8(Offset::channelIndexA(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ZoneBankElement::channelIndexBValid() const {\n  return 0 != getUInt8(Offset::channelIndexB());\n}\n\nunsigned int\nDM32UVCodeplug::ZoneBankElement::channelIndexB() const {\n  return getUInt8(Offset::channelIndexB())-1;\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::setChannelIndexB(unsigned int idx) {\n  setUInt8(Offset::channelIndexB(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::clearChannelIndexB() {\n  setUInt8(Offset::channelIndexB(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ZoneBankElement::zoneIndexAValid() const {\n  return 0 != getUInt8(Offset::zoneIndexA());\n}\n\nunsigned int\nDM32UVCodeplug::ZoneBankElement::zoneIndexA() const {\n  return getUInt8(Offset::zoneIndexA())-1;\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::setZoneIndexA(unsigned int idx) {\n  setUInt8(Offset::zoneIndexA(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::clearZoneIndexA() {\n  setUInt8(Offset::zoneIndexA(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ZoneBankElement::zoneIndexBValid() const {\n  return 0 != getUInt8(Offset::zoneIndexB());\n}\n\nunsigned int\nDM32UVCodeplug::ZoneBankElement::zoneIndexB() const {\n  return getUInt8(Offset::zoneIndexB())-1;\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::setZoneIndexB(unsigned int idx) {\n  setUInt8(Offset::zoneIndexB(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ZoneBankElement::clearZoneIndexB() {\n  setUInt8(Offset::zoneIndexB(), 0);\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ScanListElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nQString\nDM32UVCodeplug::ScanListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\nunsigned int\nDM32UVCodeplug::ScanListElement::channelCount() const {\n  return getUInt8(Offset::channelCount());\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setChannelCount(unsigned int n) {\n  setUInt8(Offset::channelCount(), n);\n}\n\n\nDM32UVCodeplug::ScanListElement::TransmitMode\nDM32UVCodeplug::ScanListElement::transmitMode() const {\n  return (TransmitMode)getUInt4(Offset::transmitMode());\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setTransmitMode(TransmitMode mode) {\n  setUInt4(Offset::transmitMode(), (unsigned int)mode);\n}\n\n\nDM32UVCodeplug::ScanListElement::ToneDetectionMode\nDM32UVCodeplug::ScanListElement::toneDetectionMode() const {\n  return (ToneDetectionMode) getUInt4(Offset::toneDetection());\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setToneDetectionMode(ToneDetectionMode mode) {\n  setUInt4(Offset::toneDetection(), (unsigned int) mode);\n}\n\n\nInterval\nDM32UVCodeplug::ScanListElement::hangTime() const {\n  return Interval::fromMilliseconds(500 * getUInt4(Offset::hangTime()));\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setHangTime(const Interval &dur) {\n  setUInt4(Offset::hangTime(), dur.milliseconds()/500);\n}\n\n\nbool\nDM32UVCodeplug::ScanListElement::secondaryChannelIndexValid() const {\n  return 0 != getUInt4(Offset::secondaryChannel());\n}\n\nunsigned int\nDM32UVCodeplug::ScanListElement::secondaryChannelIndex() const {\n  return getUInt4(Offset::secondaryChannel())-1;\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setSecondaryChannelIndex(unsigned int idx) {\n  setUInt4(Offset::secondaryChannel(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::clearSecondaryChannelIndex() {\n  setUInt4(Offset::secondaryChannel(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ScanListElement::primaryChannelIndexValid() const {\n  return 0 != getUInt4(Offset::primaryChannel());\n}\n\nunsigned int\nDM32UVCodeplug::ScanListElement::primaryChannelIndex() const {\n  return getUInt4(Offset::primaryChannel())-1;\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setPrimaryChannelIndex(unsigned int idx) {\n  setUInt4(Offset::primaryChannel(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::clearPrimaryChannelIndex() {\n  setUInt4(Offset::primaryChannel(), 0);\n}\n\n\nbool\nDM32UVCodeplug::ScanListElement::revertChannelIndexValid() const {\n  return 0 != getUInt16_le(Offset::revertChannel());\n}\n\nunsigned int\nDM32UVCodeplug::ScanListElement::revertChannelIndex() const {\n  return getUInt16_le(Offset::revertChannel())-1;\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setRevertChannelIndex(unsigned int idx) {\n  setUInt16_le(Offset::revertChannel(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::clearRevertChannelIndex() {\n  setUInt16_le(Offset::revertChannel(), 0);\n}\n\n\nInterval\nDM32UVCodeplug::ScanListElement::prioritySweepTime() const {\n  return Interval::fromMilliseconds(500 + 300*getUInt6(Offset::prioritySweepTime()));\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setPrioritySweepTime(const Interval &dur) {\n  if (dur.milliseconds() >= 500) {\n    setUInt6(Offset::prioritySweepTime(), (dur.milliseconds()-500)/300);\n  } else {\n    setUInt6(Offset::prioritySweepTime(), 0);\n  }\n}\n\n\nbool\nDM32UVCodeplug::ScanListElement::isCurrentChannel(unsigned int n) const {\n  return 0 == getUInt16_le(Offset::channels() + n*sizeof(uint16_t));\n}\n\nunsigned int\nDM32UVCodeplug::ScanListElement::channelIndex(unsigned int n) const {\n  return getUInt16_le(Offset::channels() + n*sizeof(uint16_t)) - 1;\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setChannelIndex(unsigned int n, unsigned int idx) {\n  setUInt16_le(Offset::channels() + n*sizeof(uint16_t), idx+1);\n}\n\nvoid\nDM32UVCodeplug::ScanListElement::setCurrentChannel(unsigned int n) {\n  setUInt16_le(Offset::channels() + n*sizeof(uint16_t), 0);\n}\n\nScanList *\nDM32UVCodeplug::ScanListElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new ScanList(name());\n}\n\nbool\nDM32UVCodeplug::ScanListElement::link(ScanList *lst, Context &ctx, const ErrorStack &err) {\n  // Link primary, secondary and revert channels if set\n  if (primaryChannelIndexValid()) {\n    if (! ctx.has<Channel>(primaryChannelIndex())) {\n      errMsg(err) << \"Cannot link primary priority channel for scan list '\" << lst->name()\n                  << \"': Channel with index \" << primaryChannelIndex() << \" not defined.\";\n      return false;\n    }\n    lst->setPrimaryChannel(ctx.get<Channel>(primaryChannelIndex()));\n  }\n\n  if (secondaryChannelIndexValid()) {\n    if (! ctx.has<Channel>(secondaryChannelIndex())) {\n      errMsg(err) << \"Cannot link secondary priority channel for scan list '\" << lst->name()\n      << \"': Channel with index \" << secondaryChannelIndex() << \" not defined.\";\n      return false;\n    }\n    lst->setSecondaryChannel(ctx.get<Channel>(secondaryChannelIndex()));\n  }\n\n  if (revertChannelIndexValid()) {\n    if (! ctx.has<Channel>(revertChannelIndex())) {\n      errMsg(err) << \"Cannot link revert channel for scan list '\" << lst->name()\n      << \"': Channel with index \" << revertChannelIndex() << \" not defined.\";\n      return false;\n    }\n    lst->setRevertChannel(ctx.get<Channel>(revertChannelIndex()));\n  }\n\n  for (unsigned int i=0; i<channelCount(); i++) {\n    if (isCurrentChannel(i)) {\n      lst->addChannel(SelectedChannel::get());\n    } else if (! ctx.has<Channel>(channelIndex(i))) {\n      errMsg(err) << \"Cannot link scan list '\" << lst->name()\n                  << \"': Channel with index \" << channelIndex(i) << \"not defined\";\n      return false;\n    }\n    lst->addChannel(ctx.get<Channel>(channelIndex(i)));\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::ScanListElement::encode(const ScanList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(lst->name());\n\n  if (! lst->primaryChannelRef()->isNull())\n    setPrimaryChannelIndex(ctx.index(lst->primaryChannel()));\n  if (! lst->secondaryChannelRef()->isNull())\n    setSecondaryChannelIndex(ctx.index(lst->secondaryChannel()));\n  if (! lst->revertChannelRef()->isNull())\n    setRevertChannelIndex(ctx.index(lst->revertChannel()));\n  setChannelCount(std::min(Limit::channels(), (unsigned int)lst->count()));\n  for (unsigned int i=0; i<channelCount(); i++) {\n    if (SelectedChannel::get() == lst->channel(i))\n      setCurrentChannel(i);\n    else\n      setChannelIndex(i, ctx.index(lst->channel(i)));\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::ScanListBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nunsigned int\nDM32UVCodeplug::ScanListBankElement::count() const {\n   return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::ScanListBankElement::setCount(unsigned int n) {\n  setUInt8(Offset::count(), n);\n}\n\n\nDM32UVCodeplug::ScanListElement\nDM32UVCodeplug::ScanListBankElement::scanList(unsigned int n) const {\n  return ScanListElement(_data + Offset::scanLists() + n*ScanListElement::size());\n}\n\n\nDM32UVCodeplug::ScanListBankElement::ScanMode\nDM32UVCodeplug::ScanListBankElement::scanMode() const {\n  return (ScanMode)getUInt8(Offset::scanMode());\n}\n\nvoid\nDM32UVCodeplug::ScanListBankElement::setScanMode(ScanMode mode) {\n  setUInt8(Offset::scanMode(), (unsigned int)mode);\n}\n\n\nFrequencyRange\nDM32UVCodeplug::ScanListBankElement::vhfRange() const {\n  return {Frequency::fromMHz(getBCD4_le(Offset::vhfLower())),\n    Frequency::fromMHz(getBCD4_le(Offset::vhfUpper())) };\n}\n\nvoid\nDM32UVCodeplug::ScanListBankElement::setVHFRange(const FrequencyRange &range) {\n  setBCD4_le(Offset::vhfLower(), range.lower.inMHz());\n  setBCD4_le(Offset::vhfUpper(), range.upper.inMHz());\n}\n\nFrequencyRange\nDM32UVCodeplug::ScanListBankElement::uhfRange() const {\n  return {Frequency::fromMHz(getBCD4_le(Offset::uhfLower())),\n    Frequency::fromMHz(getBCD4_le(Offset::uhfUpper())) };\n}\n\nvoid\nDM32UVCodeplug::ScanListBankElement::setUHFRange(const FrequencyRange &range) {\n  setBCD4_le(Offset::uhfLower(), range.lower.inMHz());\n  setBCD4_le(Offset::uhfUpper(), range.upper.inMHz());\n}\n\n\nbool\nDM32UVCodeplug::ScanListBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto lst = scanList(i).decode(ctx, err);\n    if (nullptr == lst) {\n      errMsg(err) << \"Cannot decode scan list at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(lst, i);\n    ctx.config()->scanlists()->add(lst);\n  }\n  return true;\n}\n\nbool\nDM32UVCodeplug::ScanListBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto lst = ctx.get<ScanList>(i);\n    if (! scanList(i).link(lst, ctx, err)) {\n      errMsg(err) << \"Cannot link scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::ScanListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  setCount(std::min(Limit::scanLists(), ctx.count<ScanList>()));\n  for (unsigned int i=0; i<count(); i++) {\n    if (! scanList(i).encode(ctx.get<ScanList>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RoamingChannelElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RoamingChannelElement::RoamingChannelElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nQString\nDM32UVCodeplug::RoamingChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nFrequency\nDM32UVCodeplug::RoamingChannelElement::rxFrequency() const {\n  return Frequency::fromHz((unsigned long long)getBCD8_le(Offset::rxFrequency()) * 10);\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelElement::setRXFrequency(const Frequency &f) {\n  setBCD8_le(Offset::rxFrequency(), f.inHz()/10);\n}\n\nFrequency\nDM32UVCodeplug::RoamingChannelElement::txFrequency() const {\n  return Frequency::fromHz((unsigned long long)getBCD8_le(Offset::txFrequency()) * 10);\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelElement::setTXFrequency(const Frequency &f) {\n  setBCD8_le(Offset::txFrequency(), f.inHz()/10);\n}\n\nunsigned int\nDM32UVCodeplug::RoamingChannelElement::colorCode() const {\n  return getUInt8(Offset::colorCode());\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelElement::setColorCode(unsigned int cc) {\n  setUInt8(Offset::colorCode(), std::min(cc, 15u));\n}\n\nDMRChannel::TimeSlot\nDM32UVCodeplug::RoamingChannelElement::timeSlot() const {\n  switch ((TimeSlot) getUInt8(Offset::timeSlot())) {\n  case TimeSlot::TS1: return DMRChannel::TimeSlot::TS1;\n  case TimeSlot::TS2: return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), (unsigned int)TimeSlot::TS1); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), (unsigned int)TimeSlot::TS2); break;\n  }\n}\n\n\nRoamingChannel *\nDM32UVCodeplug::RoamingChannelElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  auto rc = new RoamingChannel();\n  rc->setName(name());\n  rc->setRXFrequency(rxFrequency());\n  rc->setTXFrequency(txFrequency());\n  rc->overrideColorCode(true);\n  rc->setColorCode(colorCode());\n  rc->overrideTimeSlot(true);\n  rc->setTimeSlot(timeSlot());\n  return rc;\n}\n\n\nbool\nDM32UVCodeplug::RoamingChannelElement::encode(const RoamingChannel *ch, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(ch->name());\n  setRXFrequency(ch->rxFrequency());\n  setTXFrequency(ch->txFrequency());\n  setColorCode(ch->colorCode());\n  setTimeSlot(ch->timeSlot());\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RoamingChannelBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RoamingChannelBankElement::RoamingChannelBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nunsigned int\nDM32UVCodeplug::RoamingChannelBankElement::count() const {\n  return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::RoamingChannelBankElement::setCount(unsigned int n) {\n  setUInt8(Offset::count(), std::min(n, Limit::channels()));\n}\n\nDM32UVCodeplug::RoamingChannelElement\nDM32UVCodeplug::RoamingChannelBankElement::channel(unsigned int n) {\n  return RoamingChannelElement(_data + Offset::channels() + n*Offset::betweenChannels());\n}\n\nbool\nDM32UVCodeplug::RoamingChannelBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto rc = channel(i).decode(ctx, err);\n    if (nullptr == rc) {\n      errMsg(err) << \"Cannot decode roaming channel at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(rc, i);\n    ctx.config()->roamingChannels()->add(rc);\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::RoamingChannelBankElement::encode(Context &ctx, const ErrorStack &err) {\n  setCount(std::min(Limit::channels(), ctx.count<RoamingChannel>()));\n  for (unsigned int i=0; i<count(); i++) {\n    if (! channel(i).encode(ctx.get<RoamingChannel>(i), err)) {\n      errMsg(err) << \"Cannot encode roaming channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RoamingZoneElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RoamingZoneElement::RoamingZoneElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass..\n}\n\n\nQString\nDM32UVCodeplug::RoamingZoneElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nunsigned int\nDM32UVCodeplug::RoamingZoneElement::count() const {\n  return getUInt8(Offset::channelCount());\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneElement::setCount(unsigned int n) {\n  setUInt8(Offset::channelCount(), std::min(n, Limit::channels()));\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneElement::channelIndexValid(unsigned int n) {\n  return 0 != getUInt8(Offset::channels() + n*Offset::betweenChannels());\n}\n\nunsigned int\nDM32UVCodeplug::RoamingZoneElement::channelIndex(unsigned int n) {\n  return getUInt8(Offset::channels() + n*Offset::betweenChannels())-1;\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneElement::setChannelIndex(unsigned int n, unsigned int idx) {\n  setUInt8(Offset::channels() + n*Offset::betweenChannels(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneElement::clearChannelIndex(unsigned int n) {\n  setUInt8(Offset::channels() + n*Offset::betweenChannels(), 0);\n}\n\nRoamingZone *\nDM32UVCodeplug::RoamingZoneElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new RoamingZone(name());\n}\n\nbool\nDM32UVCodeplug::RoamingZoneElement::link(RoamingZone *zone, Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    if (! ctx.has<RoamingChannel>(channelIndex(i))) {\n      errMsg(err) << \"Cannot resolve \" << i << \"-th channel index \" << channelIndex(i)\n                  << \": Not defined.\";\n      return false;\n    }\n    zone->addChannel(ctx.get<RoamingChannel>(channelIndex(i)));\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneElement::encode(const RoamingZone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(zone->name());\n  setCount(std::min(Limit::channels(), (unsigned int)zone->count()));\n  for (unsigned int i=0; i<count(); i++) {\n    setChannelIndex(i, ctx.index(zone->channel(i)));\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::RoamingZoneBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::RoamingZoneBankElement::RoamingZoneBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nunsigned int\nDM32UVCodeplug::RoamingZoneBankElement::count() const {\n  return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneBankElement::setCount(unsigned int n) {\n  setUInt8(Offset::count(), std::min(n, Limit::zones()));\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneBankElement::autoRoamEnabled() const {\n  return 0x01 == getUInt8(Offset::autoRoam());\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneBankElement::enableAutoRoam(bool enable) {\n  setUInt8(Offset::autoRoam(), enable ? 0x01 : 0x00);\n}\n\n\nInterval\nDM32UVCodeplug::RoamingZoneBankElement::roamingDelay() const {\n  return Interval::fromMinutes(getUInt8(Offset::roamingDelay()));\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneBankElement::setRoamingDelay(const Interval &delay) {\n  setUInt8(Offset::roamingDelay(), delay.minutes());\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneBankElement::defaultRoamingZoneIndexValid() const {\n  return 0 != getUInt8(Offset::defaultRoamingZone());\n}\n\nunsigned int\nDM32UVCodeplug::RoamingZoneBankElement::defaultRoamingZoneIndex() const {\n  return getUInt8(Offset::defaultRoamingZone())-1;\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneBankElement::setDefaultRoamingZoneIndex(unsigned int idx) {\n  return setUInt8(Offset::defaultRoamingZone(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::RoamingZoneBankElement::clearDefaultRoamingZoneIndex() {\n  setUInt8(Offset::defaultRoamingZone(), 0);\n}\n\n\nDM32UVCodeplug::RoamingZoneElement\nDM32UVCodeplug::RoamingZoneBankElement::zone(unsigned int n) {\n  return RoamingZoneElement(_data + Offset::zones() + n*Offset::betweenZones());\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto z = zone(i).decode(ctx, err);\n    if (nullptr == z) {\n      errMsg(err) << \"Cannot decode zone at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(z, i);\n    ctx.config()->roamingZones()->add(z);\n  }\n  return true;\n}\n\nbool\nDM32UVCodeplug::RoamingZoneBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto z = ctx.get<RoamingZone>(i);\n    if (nullptr == z) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \": Cannot resolve index.\";\n      return false;\n    }\n    if (! zone(i).link(z, ctx, err)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::RoamingZoneBankElement::encode(Context &ctx, const ErrorStack &err) {\n  setCount(std::min(Limit::zones(), ctx.count<RoamingZone>()));\n  for (unsigned int i=0; i<count(); i++) {\n    if (! zone(i).encode(ctx.get<RoamingZone>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode roaming zone at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::SMSTemplateElement\n * ******************************************************************************************** */\nDM32UVCodeplug::SMSTemplateElement::SMSTemplateElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nQString\nDM32UVCodeplug::SMSTemplateElement::message() const {\n  uint8_t len = getUInt8(Offset::length());\n  return readASCII(Offset::message(), len, 0x00);\n}\n\nvoid\nDM32UVCodeplug::SMSTemplateElement::setMessage(const QString &msg) {\n  unsigned int len = std::min((unsigned int)msg.length(), Limit::messageLength());\n  setUInt8(Offset::length(), len);\n  writeASCII(Offset::message(), msg, Limit::messageLength(), 0x00);\n}\n\nSMSTemplate *\nDM32UVCodeplug::SMSTemplateElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  auto tmpl = new SMSTemplate(); tmpl->setMessage(message());\n  return tmpl;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::SMSTemplateBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::SMSTemplateBankElement::SMSTemplateBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nunsigned int\nDM32UVCodeplug::SMSTemplateBankElement::count() const {\n  return getUInt8(Offset::count());\n}\n\nvoid\nDM32UVCodeplug::SMSTemplateBankElement::setCount(unsigned int n) {\n  n = std::min(n, Limit::messages());\n  setUInt8(Offset::count(), n);\n}\n\nDM32UVCodeplug::SMSTemplateElement\nDM32UVCodeplug::SMSTemplateBankElement::message(unsigned int n) const {\n  return SMSTemplateElement(_data + Offset::messages() + n*Offset::betweenMessages());\n}\n\nbool\nDM32UVCodeplug::SMSTemplateBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<count(); i++) {\n    auto tmpl = message(i).decode(ctx, err);\n    if (nullptr == tmpl) {\n      errMsg(err) << \"Cannot decode SMS template at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(tmpl, i);\n    ctx.config()->smsExtension()->smsTemplates()->add(tmpl);\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GeneralSettingsElement::Color\n * ******************************************************************************************** */\nunsigned int\nDM32UVCodeplug::GeneralSettingsElement::Color::encode(Code name) {\n  return (unsigned int) name;\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::Color::decode(unsigned int code) {\n  return (Code)code;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GeneralSettingsElement::Function\n * ******************************************************************************************** */\nunsigned int\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::encode(Function func) {\n  return (unsigned int) func;\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::decode(unsigned int code) {\n  return (Function)code;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nDM32UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nBootSettings::BootDisplay\nDM32UVCodeplug::GeneralSettingsElement::bootDisplay() const {\n  switch ((BootDisplay) getUInt8(Offset::bootDisplay())) {\n  case BootDisplay::Message: return BootSettings::BootDisplay::Text;\n  case BootDisplay::Image: return BootSettings::BootDisplay::Image;\n  default: break;\n  }\n  return BootSettings::BootDisplay::Logo;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setBootDisplay(BootSettings::BootDisplay dis) {\n  switch (dis) {\n  case BootSettings::BootDisplay::Text:\n    setUInt8(Offset::bootDisplay(), (unsigned int)BootDisplay::Message);\n    break;\n  case BootSettings::BootDisplay::Logo:\n  case BootSettings::BootDisplay::Image:\n    setUInt8(Offset::bootDisplay(), (unsigned int)BootDisplay::Image);\n    break;\n  }\n}\n\n\nQString\nDM32UVCodeplug::GeneralSettingsElement::bootMessage1() const {\n  return readASCII(Offset::bootMessage1(), Limit::bootMessageLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setBootMessage1(const QString &msg) {\n  writeASCII(Offset::bootMessage1(), msg, Limit::bootMessageLength(), 0x00);\n}\n\nQString\nDM32UVCodeplug::GeneralSettingsElement::bootMessage2() const {\n  return readASCII(Offset::bootMessage2(), Limit::bootMessageLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setBootMessage2(const QString &msg) {\n  writeASCII(Offset::bootMessage2(), msg, Limit::bootMessageLength(), 0x00);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::mcuResetEnabled() const {\n  return 0x01 == getUInt8(Offset::mcuReset());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableMCUReset(bool enable) {\n  setUInt8(Offset::mcuReset(), enable ? 0x01 : 0x00);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::autoPowerOffDelay() const {\n  switch ((AutoPowerOffDelay)getUInt8(Offset::autoPowerOffDelay())) {\n  case AutoPowerOffDelay::Off: return Interval::infinity();\n  case AutoPowerOffDelay::T30Min: return Interval::fromMinutes(30);\n  case AutoPowerOffDelay::T60Min: return Interval::fromMinutes(60);\n  case AutoPowerOffDelay::T2h: return Interval::fromMinutes(120);\n  case AutoPowerOffDelay::T4h: return Interval::fromMinutes(240);\n  case AutoPowerOffDelay::T8h: return Interval::fromMinutes(480);\n  }\n  return Interval::infinity();\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setAutoPowerOffDelay(const Interval &delay) {\n  if (delay.isNull() || delay.isInfinite())\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::Off);\n  else if (delay.minutes() <= 30)\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::T30Min);\n  else if (delay.minutes() <= 60)\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::T60Min);\n  else if (delay.minutes() <= 120)\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::T2h);\n  else if (delay.minutes() <= 240)\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::T4h);\n  else if (delay.minutes() <= 480)\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::T8h);\n  else\n    setUInt8(Offset::autoPowerOffDelay(), (unsigned int)AutoPowerOffDelay::Off);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::radioSilentEnabled() const {\n  return getBit(Offset::radioSilent());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableRadioSilent(bool enable) {\n  setBit(Offset::radioSilent(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::keyToneEnabled() const {\n  return getBit(Offset::keyTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) {\n  setBit(Offset::keyTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::smsToneEnabled() const {\n  return getBit(Offset::smsTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableSMSTone(bool enable) {\n  setBit(Offset::smsTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::groupCallToneEnabled() const {\n  return getBit(Offset::groupCallTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableGroupCallTone(bool enable) {\n  setBit(Offset::groupCallTone(), enable);\n}\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::privateCallToneEnabled() const {\n  return getBit(Offset::privateCallTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enablePrivateCallTone(bool enable) {\n  setBit(Offset::privateCallTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::eotToneEnabled() const {\n  return getBit(Offset::eotTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableEOTTone(bool enable) {\n  setBit(Offset::eotTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::talkPermitToneEnabled() const {\n  return getBit(Offset::talkPermitTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableTalkPermitTone(bool enable) {\n  setBit(Offset::talkPermitTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::bootToneEnabled() const {\n  return getBit(Offset::bootTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableBootTone(bool enable) {\n  setBit(Offset::bootTone(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::voicePromptEnabled() const {\n  return getBit(Offset::voicePrompt());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableVoicePrompt(bool enable) {\n  setBit(Offset::voicePrompt(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::lowBatteryToneEnabled() const {\n  return getBit(Offset::lowBatteryTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableLowBatteryTone(bool enable) {\n  setBit(Offset::lowBatteryTone(), enable);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::FMRogerTone\nDM32UVCodeplug::GeneralSettingsElement::fmRogerTone() const {\n  return (FMRogerTone)getUInt2(Offset::fmRogerTone());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setFMRogerTone(FMRogerTone tone) {\n  setUInt2(Offset::fmRogerTone(), (unsigned int)tone);\n}\n\n\nunsigned int\nDM32UVCodeplug::GeneralSettingsElement::displayBrightness() const {\n  return (getUInt8(Offset::displayBrightness())*10)/Limit::maxBrightness();\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDisplayBrightness(unsigned int n) {\n  setUInt8(Offset::displayBrightness(), (n*Limit::maxBrightness())/10);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::backlightDuration() const {\n  switch ((BacklightDuration)getUInt8(Offset::backlightDuration())) {\n  case BacklightDuration::Infinity: return Interval::infinity();\n  case BacklightDuration::T5s: return Interval::fromSeconds(5);\n  case BacklightDuration::T10s: return Interval::fromSeconds(10);\n  case BacklightDuration::T15s: return Interval::fromSeconds(15);\n  case BacklightDuration::T20s: return Interval::fromSeconds(20);\n  case BacklightDuration::T25s: return Interval::fromSeconds(25);\n  case BacklightDuration::T30s: return Interval::fromSeconds(30);\n  case BacklightDuration::T1min: return Interval::fromMinutes(1);\n  case BacklightDuration::T2min: return Interval::fromMinutes(2);\n  case BacklightDuration::T3min: return Interval::fromMinutes(3);\n  case BacklightDuration::T4min: return Interval::fromMinutes(4);\n  case BacklightDuration::T5min: return Interval::fromMinutes(5);\n  }\n  return Interval::infinity();\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setBacklightDuration(Interval duration) {\n  if (duration.isNull() || duration.isInfinite()) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::Infinity);\n  } else if (duration.seconds() <= 5) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T5s);\n  } else if (duration.seconds() <= 10) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T10s);\n  } else if (duration.seconds() <= 15) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T15s);\n  } else if (duration.seconds() <= 20) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T20s);\n  } else if (duration.seconds() <= 25) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T25s);\n  } else if (duration.seconds() <= 30) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T30s);\n  } else if (duration.minutes() <= 1) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T1min);\n  } else if (duration.minutes() <= 2) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T2min);\n  } else if (duration.minutes() <= 3) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T3min);\n  } else if (duration.minutes() <= 4) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T4min);\n  } else if (duration.minutes() <= 5) {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::T5min);\n  } else {\n    setUInt8(Offset::backlightDuration(), (unsigned int)BacklightDuration::Infinity);\n  }\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::menuDuration() const {\n  if (0 == getUInt8(Offset::menuDuration())) {\n    return Interval::infinity();\n  }\n  return Interval::fromSeconds(5 * getUInt8(Offset::menuDuration()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setMenuDuration(Interval duration) {\n  if (duration.isNull() || duration.isInfinite()) {\n    setUInt8(Offset::menuDuration(), 0);\n  } else {\n    setUInt8(Offset::menuDuration(), duration.seconds()/5);\n  }\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::showVolmueChange() const {\n  return getBit(Offset::showVolumeChange());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableShowVolumeChange(bool enable) {\n  setBit(Offset::showVolumeChange(), enable);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::DateFormat\nDM32UVCodeplug::GeneralSettingsElement::dateFormat() const {\n  return getBit(Offset::dateFormat()) ? DateFormat::DDMMYYYY : DateFormat::YYYYMMDD;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDateFormat(DateFormat format) {\n  setBit(Offset::dateFormat(), DateFormat::DDMMYYYY == format);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::showClock() const {\n  return getBit(Offset::showClock());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableShowClock(bool enable) {\n  setBit(Offset::showClock(), enable);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::callColor() const {\n  return Color::decode(getUInt8(Offset::callColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setCallColor(Color::Code c) {\n  setUInt8(Offset::callColor(), Color::encode(c));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::standbyColor() const {\n  return Color::decode(getUInt8(Offset::standbyColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setStandbyColor(Color::Code c) {\n  setUInt8(Offset::standbyColor(), Color::encode(c));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::channelNameAColor() const {\n  return Color::decode(getUInt8(Offset::channelNameAColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setChannelNameAColor(Color::Code c) {\n  setUInt8(Offset::channelNameAColor(), Color::encode(c));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::channelNameBColor() const {\n  return Color::decode(getUInt8(Offset::channelNameBColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setChannelNameBColor(Color::Code c) {\n  setUInt8(Offset::channelNameBColor(), Color::encode(c));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::zoneNameAColor() const {\n  return Color::decode(getUInt8(Offset::zoneNameAColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setZoneNameAColor(Color::Code c) {\n  setUInt8(Offset::zoneNameAColor(), Color::encode(c));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::Color::Code\nDM32UVCodeplug::GeneralSettingsElement::zoneNameBColor() const {\n  return Color::decode(getUInt8(Offset::zoneNameBColor()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setZoneNameBColor(Color::Code c) {\n  setUInt8(Offset::zoneNameBColor(), Color::encode(c));\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::PositionFormat\nDM32UVCodeplug::GeneralSettingsElement::positionFormat() const {\n  return getBit(Offset::positionFormat()) ? PositionFormat::DMS : PositionFormat::DD;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setPositionFormat(PositionFormat format) {\n  setBit(Offset::positionFormat(), PositionFormat::DMS == format);\n}\n\n\nGNSSSettings::Systems\nDM32UVCodeplug::GeneralSettingsElement::gnss() const {\n  switch ((GNSSMode)getUInt2(Offset::gnssMode())) {\n  case GNSSMode::GPS: return GNSSSettings::System::GPS;\n  case GNSSMode::Beidou: return GNSSSettings::System::Beidou;\n  case GNSSMode::Both: return GNSSSettings::System::GPS | GNSSSettings::System::Beidou;\n  }\n  return GNSSSettings::System::GPS;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setGNSS(GNSSSettings::Systems mode) {\n  if (mode.testFlag(GNSSSettings::System::GPS))\n    setUInt2(Offset::gnssMode(), (unsigned int)GNSSMode::GPS);\n  if (mode.testFlag(GNSSSettings::System::Beidou))\n    setUInt2(Offset::gnssMode(), (unsigned int)GNSSMode::Beidou);\n  if (mode.testFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou))\n    setUInt2(Offset::gnssMode(), (unsigned int)GNSSMode::Both);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::gnssEnabled() const {\n  return getBit(Offset::enableGNSS());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableGNSS(bool enable) {\n  setBit(Offset::enableGNSS(), enable);\n}\n\n\nQTimeZone\nDM32UVCodeplug::GeneralSettingsElement::timeZone() const {\n  return QTimeZone( (((int)getUInt8(Offset::timeZone()))-11) * 3600);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTimeZone(const QTimeZone &timeZone) {\n  setUInt8(Offset::timeZone(), timeZone.offsetFromUtc(QDateTime::currentDateTime())/3600+11);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::posUpdatePeriod() const {\n  return Interval::fromMinutes(getUInt8(Offset::posUpdatePeriod()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setPosUpdatePeriod(const Interval &period) {\n  setUInt8(Offset::posUpdatePeriod(), period.minutes());\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::RecordMode\nDM32UVCodeplug::GeneralSettingsElement::recordMode() const {\n  return (RecordMode) getUInt2(Offset::recordMode());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setRecordMode(RecordMode mode) {\n  setUInt2(Offset::recordMode(), (unsigned int)mode);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::recordingEnabled() const {\n  return getBit(Offset::enableRecording());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableRecording(bool enable) {\n  setBit(Offset::enableRecording(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::groupCallMatchEnabled() const {\n  return getBit(Offset::groupCallMatch());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableGroupCallMatch(bool enable) {\n  setBit(Offset::groupCallMatch(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::privateCallMatchEnabled() const {\n  return getBit(Offset::privateCallMatch());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enablePrivateCallMatch(bool enable) {\n  setBit(Offset::privateCallMatch(), enable);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::dmrCallHangTime() const {\n  return Interval::fromMilliseconds( ((unsigned int)getUInt8(Offset::dmrCallHangTime())) * 500);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDMRCallHangTime(const Interval &time) {\n  setUInt8(Offset::dmrCallHangTime(), time.milliseconds()/500);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::activeWaitTime() const {\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::activeWaitTime()))*30 + 300);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setActiveWaitTime(Interval waitTime) {\n  setUInt8(Offset::activeWaitTime(), (waitTime.milliseconds()-300)/30);\n}\n\n\nunsigned int\nDM32UVCodeplug::GeneralSettingsElement::activeRetries() const {\n  return getUInt8(Offset::activeReties());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setActiveRetries(unsigned int retries) {\n  setUInt8(Offset::activeReties(), retries);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::dmrPreambleDuration() const {\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::dmrPreambleDur()))*120 + 120);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDmrPreambleDuration(Interval duration) {\n  setUInt8(Offset::dmrPreambleDur(), (duration.milliseconds()-120)/120);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::dmrRemoteMonitorEnabled() const {\n  return  getBit(Offset::dmrMonitor());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableDMRRemoteMonitor(bool enable) {\n  setBit(Offset::dmrMonitor(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::dmrRemoteKillEnabled() const {\n  return getBit(Offset::dmrKill());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableDMRRemoteKill(bool enable) {\n  setBit(Offset::dmrKill(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::dmrRemoteRadioCheckEnabled() const {\n  return getBit(Offset::dmrRadioCheck());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableDMRRemoteRadioCheck(bool enable) {\n  setBit(Offset::dmrRadioCheck(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::dmrRemoteReenableEnabled() const {\n  return getBit(Offset::dmrReenable());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableDMRRemoteReenable(bool enable) {\n  setBit(Offset::dmrReenable(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::dmrRXAlertEnabled() const {\n  return getBit(Offset::dmrRXAlert());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableDMRRXAlert(bool enable) {\n  setBit(Offset::dmrRXAlert(), enable);\n}\n\n\nSMSExtension::Format\nDM32UVCodeplug::GeneralSettingsElement::smsFormat() const {\n  switch ((SMSFormat)getUInt2(Offset::smsFormat())) {\n  case SMSFormat::Motorola: return SMSExtension::Format::Motorola;\n  case SMSFormat::Hytera: return SMSExtension::Format::Hytera;\n  case SMSFormat::DMR: return SMSExtension::Format::DMR;\n  }\n  return SMSExtension::Format::DMR;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSMSFormat(SMSExtension::Format format) {\n  switch (format) {\n  case SMSExtension::Format::Motorola: setUInt2(Offset::smsFormat(), (unsigned int)SMSFormat::Motorola); break;\n  case SMSExtension::Format::Hytera: setUInt2(Offset::smsFormat(), (unsigned int)SMSFormat::Hytera); break;\n  case SMSExtension::Format::DMR: setUInt2(Offset::smsFormat(), (unsigned int)SMSFormat::DMR); break;\n  }\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::missedCallNotificationEnabled() const {\n  return getBit(Offset::missedCallNotification());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableMissedCallNotification(bool enable) {\n  setBit(Offset::missedCallNotification(), enable);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::dmrRemoteMonitorDuration() const {\n  return Interval::fromSeconds(((unsigned int)getUInt8(Offset::dmrRemoteMonitorDuration()))*10 + 10);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDMRRemoteMonitorDuration(Interval duration) {\n  setUInt8(Offset::dmrRemoteMonitorDuration(), (duration.seconds()-10)/10);\n}\n\n\nDMRSettings::TalkerAliasEncoding\nDM32UVCodeplug::GeneralSettingsElement::talkerAliasEncoding() const {\n  return getBit(Offset::dmrTalkerAliasFormat())\n           ? DMRSettings::TalkerAliasEncoding::Unicode\n           : DMRSettings::TalkerAliasEncoding::Iso8;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding format) {\n  setBit(Offset::dmrTalkerAliasFormat(), DMRSettings::TalkerAliasEncoding::Unicode == format);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::txTalkerAliasEnabled() const {\n  return getBit(Offset::txTalkerAlias());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableTXTalkerAlias(bool enable) {\n  setBit(Offset::txTalkerAlias(), enable);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::TalkerAliasSource\nDM32UVCodeplug::GeneralSettingsElement::talkerAliasSource() const {\n  return getBit(Offset::talkerSource()) ? TalkerAliasSource::OverTheAir : TalkerAliasSource::CallsignDB;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTalkerAliasSource(TalkerAliasSource source) {\n  setBit(Offset::talkerSource(), TalkerAliasSource::OverTheAir == source);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::DualStandbyMode\nDM32UVCodeplug::GeneralSettingsElement::dualStandbyMode() const {\n  return (DualStandbyMode) getUInt2(Offset::dualStandbyMode());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDualStandbyMode(DualStandbyMode mode) {\n  setUInt2(Offset::dualStandbyMode(), (unsigned int)mode);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::VFO\nDM32UVCodeplug::GeneralSettingsElement::mainVFO() const {\n  return getBit(Offset::mainVFO()) ? VFO::B : VFO::A;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setMainVFO(VFO mainVFO) {\n  setBit(Offset::mainVFO(), VFO::B == mainVFO);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::VFODisplayMode\nDM32UVCodeplug::GeneralSettingsElement::vfoDisplayModeA() const {\n  return getBit(Offset::displayModeA()) ? VFODisplayMode::ChannelName : VFODisplayMode::Frequency;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVFODisplayModeA(VFODisplayMode mode) {\n  setBit(Offset::displayModeA(), VFODisplayMode::ChannelName == mode);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::VFODisplayMode\nDM32UVCodeplug::GeneralSettingsElement::vfoDisplayModeB() const {\n  return getBit(Offset::displayModeB()) ? VFODisplayMode::ChannelName : VFODisplayMode::Frequency;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVFODisplayModeB(VFODisplayMode mode) {\n  setBit(Offset::displayModeB(), VFODisplayMode::ChannelName == mode);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::VFOMode\nDM32UVCodeplug::GeneralSettingsElement::vfoModeA() const {\n  return getBit(Offset::vfoModeA()) ? VFOMode::VFO : VFOMode::Channel;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVFOModeA(VFOMode mode) {\n  setBit(Offset::vfoModeA(), VFOMode::VFO == mode);\n}\n\nDM32UVCodeplug::GeneralSettingsElement::VFOMode\nDM32UVCodeplug::GeneralSettingsElement::vfoModeB() const {\n  return getBit(Offset::vfoModeB()) ? VFOMode::VFO : VFOMode::Channel;\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVFOModeB(VFOMode mode) {\n  setBit(Offset::vfoModeB(), VFOMode::VFO == mode);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::vfoModeDisabled() const {\n  return getBit(Offset::disableVFOMode());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::disableVFOMode(bool enable) {\n  setBit(Offset::disableVFOMode(), enable);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::dualStandbyHangTime() const {\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::dualStandbyHangTime()))*500);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDualStandbyHangTime(Interval hangTime) {\n  setUInt8(Offset::dualStandbyHangTime(), hangTime.milliseconds()/500);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::sideKeyLockEnabled() const {\n  return getBit(Offset::sideKeyLock());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableSideKeyLock(bool enable) {\n  setBit(Offset::sideKeyLock(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::knobLockEnabled() const {\n  return getBit(Offset::knobLock());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {\n  setBit(Offset::knobLock(), enable);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::autoKeyLockDelay() const {\n  if (! getBit(Offset::enableAutoKeyLock()))\n    return Interval::infinity();\n  return Interval::fromMinutes(5 + getUInt8(Offset::autoKeyLockDelay()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setAutoKeyLockDelay(Interval delay) {\n  if (delay.isInfinite()||delay.isNull()) {\n    setBit(Offset::enableAutoKeyLock(), false);\n  } else {\n    setBit(Offset::enableAutoKeyLock(), true);\n    setUInt8(Offset::autoKeyLockDelay(), delay.minutes()-5);\n  }\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::sk1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::sk1Short()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSK1Short(KeyFunction::Function function) {\n  setUInt8(Offset::sk1Short(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::sk1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::sk1Long()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSK1Long(KeyFunction::Function function) {\n  setUInt8(Offset::sk1Long(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::sk2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::sk2Short()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSK2Short(KeyFunction::Function function) {\n  setUInt8(Offset::sk2Short(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::sk2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::sk2Long()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSK2Long(KeyFunction::Function function) {\n  setUInt8(Offset::sk2Long(), KeyFunction::encode(function));\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::p1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::p1Short()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setP1Short(KeyFunction::Function function) {\n  setUInt8(Offset::p1Short(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::p1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::p1Long()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setP1Long(KeyFunction::Function function) {\n  setUInt8(Offset::p1Long(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::p2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::p2Short()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setP2Short(KeyFunction::Function function) {\n  setUInt8(Offset::p2Short(), KeyFunction::encode(function));\n}\n\nDM32UVCodeplug::GeneralSettingsElement::KeyFunction::Function\nDM32UVCodeplug::GeneralSettingsElement::p2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::p2Long()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setP2Long(KeyFunction::Function function) {\n  setUInt8(Offset::p2Long(), KeyFunction::encode(function));\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::longPressDuration() const {\n  return Interval::fromSeconds(getUInt8(Offset::longPressDuration()) + 1);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setLongPressDuration(Interval duration) {\n  duration = Limit::longPressDuration().limit(duration);\n  setUInt8(Offset::longPressDuration(), duration.seconds()-1);\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::transmitTimeout() const {\n  if (0 == getUInt8(Offset::transmitTimeout()))\n    return Interval::infinity();\n  return Interval::fromSeconds(((unsigned int)getUInt8(Offset::transmitTimeout()))*5 +  15);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTransmitTimeout(Interval timeout) {\n  if (timeout.isInfinite() || timeout.isNull()) {\n    setUInt8(Offset::transmitTimeout(), 0);\n  } else {\n    timeout = Limit::transmitTimeout().limit(timeout);\n    setUInt8(Offset::transmitTimeout(), (timeout.seconds()-15)/5);\n  }\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::transmitTimeoutReminder() const {\n  return Interval::fromSeconds(getUInt8(Offset::totReminder()));\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTransmitTimeoutReminder(Interval timeout) {\n  setUInt8(Offset::totReminder(), timeout.seconds());\n}\n\n\nLevel\nDM32UVCodeplug::GeneralSettingsElement::voxLevel() const {\n  if (0 == getUInt8(Offset::voxLevel()))\n    return Level::null();\n  return Level::fromValue(getUInt8(Offset::voxLevel()), Limit::vox());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVOXLevel(Level voxLevel) {\n  if (voxLevel.isNull())\n    setUInt8(Offset::voxLevel(), 0);\n  else\n    setUInt8(Offset::voxLevel(), voxLevel.mapTo(Limit::vox()));\n}\n\n\nInterval\nDM32UVCodeplug::GeneralSettingsElement::voxDelay() const {\n  return Interval::fromMilliseconds(((unsigned int)getUInt8(Offset::voxDelay()))*100);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setVoxDelay(Interval delay) {\n  delay = Limit::voxDelay().limit(delay);\n  setUInt8(Offset::voxDelay(), delay.milliseconds()/100);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::PowerSaveMode\nDM32UVCodeplug::GeneralSettingsElement::powerSaveMode() const {\n  return (PowerSaveMode) getUInt2(Offset::powerSaveMode());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setPowerSaveMode(PowerSaveMode mode) {\n  setUInt2(Offset::powerSaveMode(), (unsigned int)mode);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::weatherAlarmEnabled() const {\n  return getBit(Offset::weatherAlarm());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::enableWeatherAlarm(bool enable) {\n  setBit(Offset::weatherAlarm(), enable);\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::allLEDsDisabled() const {\n  return getBit(Offset::disableLEDs());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::disableAllLEDs(bool disable) {\n  setBit(Offset::disableLEDs(), disable);\n}\n\n\nFrequency\nDM32UVCodeplug::GeneralSettingsElement::tbstFrequency() const {\n  switch ((TBSTFrequency)getUInt2(Offset::tbstFrequency())) {\n  case TBSTFrequency::Hz1000: return Frequency::fromHz(1000);\n  case TBSTFrequency::Hz1450: return Frequency::fromHz(1450);\n  case TBSTFrequency::Hz1750: return Frequency::fromHz(1750);\n  case TBSTFrequency::Hz2100: return Frequency::fromHz(2100);\n  }\n  return Frequency::fromHz(1750);\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setTBSTFrequency(const Frequency &frequency) {\n  if (frequency.inHz() <= 1000)\n    setUInt2(Offset::tbstFrequency(), (unsigned int)TBSTFrequency::Hz1000);\n  else if (frequency.inHz() <= 1450)\n    setUInt2(Offset::tbstFrequency(), (unsigned int)TBSTFrequency::Hz1450);\n  else if (frequency.inHz() <= 1750)\n    setUInt2(Offset::tbstFrequency(), (unsigned int)TBSTFrequency::Hz1750);\n  else\n    setUInt2(Offset::tbstFrequency(), (unsigned int)TBSTFrequency::Hz2100);\n}\n\n\nDM32UVCodeplug::GeneralSettingsElement::STEMode\nDM32UVCodeplug::GeneralSettingsElement::steMode() const {\n  return (STEMode)getUInt2(Offset::steMode());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setSTEMode(STEMode mode) {\n  setUInt2(Offset::steMode(), (unsigned int)mode);\n}\n\n\nLevel\nDM32UVCodeplug::GeneralSettingsElement::fmMicLevel() const {\n  return Level::fromValue(getUInt8(Offset::fmMicLevel()), Limit::micGain());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setFMMicLevel(Level level) {\n  return setUInt8(Offset::fmMicLevel(), level.mapTo(Limit::micGain()));\n}\n\nLevel\nDM32UVCodeplug::GeneralSettingsElement::dmrMicLevel() const {\n  return Level::fromValue(getUInt8(Offset::dmrMicLevel()), Limit::micGain());\n}\n\nvoid\nDM32UVCodeplug::GeneralSettingsElement::setDMRMicLevel(Level level) {\n  return setUInt8(Offset::dmrMicLevel(), level.mapTo(Limit::micGain()));\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // Boot settings\n  ctx.config()->settings()->boot()->setBootDisplay(bootDisplay());\n  ctx.config()->settings()->boot()->setMessage1(bootMessage1());\n  ctx.config()->settings()->boot()->setMessage2(bootMessage2());\n  ctx.config()->settings()->boot()->enableReset(mcuResetEnabled());\n\n  // Audio settings\n  ctx.config()->settings()->audio()->setMicGain(dmrMicLevel());\n  if (dmrMicLevel() != fmMicLevel())\n    ctx.config()->settings()->audio()->setFMMicGain(fmMicLevel());\n  ctx.config()->settings()->audio()->setVOXDelay(voxDelay());\n  ctx.config()->settings()->audio()->enableSpeechSynthesis(voicePromptEnabled());\n  ctx.config()->settings()->audio()->setVox(voxLevel());\n\n  // Tone settings\n  ctx.config()->settings()->tone()->enableSilent(radioSilentEnabled());\n  ctx.config()->settings()->tone()->setKeyToneVolume(\n    keyToneEnabled() ? Level::fromValue(5) : Level::null());\n  ctx.config()->settings()->tone()->enableSMSTone(smsToneEnabled());\n  ctx.config()->settings()->tone()->enableBootTone(bootToneEnabled());\n  ctx.config()->settings()->tone()->enableRingtone(\n    privateCallToneEnabled());\n  ctx.config()->settings()->tone()->setTalkPermit(\n    talkPermitToneEnabled() ? (Channel::Type::FM|Channel::Type::DMR) : Channel::Type::None );\n  ctx.config()->settings()->tone()->setCallEnd(\n    eotToneEnabled() ? (Channel::Type::FM|Channel::Type::DMR) : Channel::Type::None );\n\n  if (transmitTimeout().isInfinite())\n    ctx.config()->settings()->disableTOT();\n  else\n    ctx.config()->settings()->setTOT(transmitTimeout());\n  ctx.config()->smsExtension()->setFormat(smsFormat());\n\n  ctx.config()->settings()->gnss()->setSystems(gnss());\n\n  ctx.config()->settings()->dmr()->enablePrivateCallMatch(privateCallMatchEnabled());\n  ctx.config()->settings()->dmr()->enableGroupCallMatch(groupCallMatchEnabled());\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(dmrCallHangTime());\n  ctx.config()->settings()->dmr()->enableSendTalkerAlias(txTalkerAliasEnabled());\n  ctx.config()->settings()->dmr()->setTalkerAliasEncoding(talkerAliasEncoding());\n  ctx.config()->settings()->dmr()->setPreamble(dmrPreambleDuration());\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::GeneralSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // boot settings\n  setBootDisplay(ctx.config()->settings()->boot()->bootDisplay());\n  setBootMessage1(ctx.config()->settings()->boot()->message1());\n  setBootMessage2(ctx.config()->settings()->boot()->message2());\n  enableMCUReset(ctx.config()->settings()->boot()->resetEnabled());\n\n  // audio settings\n  setDMRMicLevel(ctx.config()->settings()->audio()->micGain());\n  if (ctx.config()->settings()->audio()->fmMicGainEnabled())\n    setFMMicLevel(ctx.config()->settings()->audio()->fmMicGain());\n  else\n    setFMMicLevel(ctx.config()->settings()->audio()->micGain());\n  enableVoicePrompt(ctx.config()->settings()->audio()->speechSynthesisEnabled());\n  if (ctx.config()->settings()->audio()->voxEnabled())\n    setVOXLevel(ctx.config()->settings()->audio()->vox());\n  else\n    setVOXLevel(Level::null());\n  setVoxDelay(ctx.config()->settings()->audio()->voxDelay());\n\n  // tone settings\n  enableRadioSilent(ctx.config()->settings()->tone()->silent());\n  enableKeyTone(ctx.config()->settings()->tone()->keyToneEnabled());\n  enableSMSTone(ctx.config()->settings()->tone()->smsToneEnabled());\n  enableBootTone(ctx.config()->settings()->tone()->bootToneEnabled());\n  enablePrivateCallTone(ctx.config()->settings()->tone()->ringtoneEnabled());\n  enableTalkPermitTone(\n    ctx.config()->settings()->tone()->talkPermit().testAnyFlags(\n      Channel::Type::FM | Channel::Type::DMR));\n  enableEOTTone(ctx.config()->settings()->tone()->callEnd().testAnyFlags(\n    Channel::Type::FM | Channel::Type::DMR));\n\n  if (ctx.config()->settings()->totDisabled())\n    setTransmitTimeout(Interval::infinity());\n  else\n    setTransmitTimeout(ctx.config()->settings()->tot());\n  if (ctx.config()->smsExtension())\n    setSMSFormat(ctx.config()->smsExtension()->format());\n\n  setGNSS(ctx.config()->settings()->gnss()->systems());\n\n  enablePrivateCallMatch(ctx.config()->settings()->dmr()->privateCallMatchEnabled());\n  enableGroupCallMatch(ctx.config()->settings()->dmr()->groupCallMatchEnabled());\n  setDMRCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n  enableTXTalkerAlias(ctx.config()->settings()->dmr()->sendTalkerAliasEnabled());\n  setTalkerAliasEncoding(ctx.config()->settings()->dmr()->talkerAliasEncoding());\n  setDmrPreambleDuration(ctx.config()->settings()->dmr()->preamble());\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::APRSSettingsElement\n * ******************************************************************************************** */\nDM32UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nInterval\nDM32UVCodeplug::APRSSettingsElement::updatePeriod() {\n  if (0 == getUInt8(Offset::updatePeriod()))\n    return Interval::infinity();\n  return Interval::fromSeconds(((unsigned int)getUInt8(Offset::updatePeriod()))*30);\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setUpdatePeriod(Interval interval) {\n  if (interval.isInfinite() || interval.isNull()) {\n    setUInt8(Offset::updatePeriod(), 0);\n  } else {\n    interval = Limit::updatePeriod().limit(interval);\n    setUInt8(Offset::updatePeriod(), (unsigned int)interval.seconds()/30);\n  }\n}\n\n\nbool\nDM32UVCodeplug::APRSSettingsElement::fixedLocationEnabled() const {\n  return 0 != getUInt8(Offset::enableFixedLocation());\n}\n\nQGeoCoordinate\nDM32UVCodeplug::APRSSettingsElement::fixedLocation() const {\n  static QRegularExpression re(R\"((\\d+\\.\\d+)([NSEW]))\");\n  QString lat = readASCII(Offset::fixedLocationLatitude(), 10, 0x00);\n  QString lon = readASCII(Offset::fixedLocationLongitude(), 10, 0x00);\n  auto latMatch = re.match(lat), lonMatch = re.match(lon);\n\n  if (latMatch.hasMatch() && lonMatch.hasMatch()) {\n    return QGeoCoordinate{\n      latMatch.captured(1).toDouble() * (latMatch.captured(2)==\"S\" ? -1 : 1),\n      lonMatch.captured(1).toDouble() * (lonMatch.captured(2)==\"W\" ? -1 : 1),\n    };\n  }\n\n  return {};\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setFixedLocation(const QGeoCoordinate &coordinate) {\n  if (! coordinate.isValid()) {\n    setUInt8(Offset::enableFixedLocation(), 0);\n    return;\n  }\n\n  QString latString, lonString;\n  latString.asprintf(\"%02.6f%c\",std::abs(coordinate.latitude()), coordinate.latitude()<0 ? 'S' : 'N');\n  lonString.asprintf(\"%03.5f%c\",std::abs(coordinate.longitude()), coordinate.longitude()<0 ? 'W' : 'E');\n  setUInt8(Offset::enableFixedLocation(), 1);\n  writeASCII(Offset::fixedLocationLatitude(), latString, 10);\n  writeASCII(Offset::fixedLocationLongitude(), lonString, 10);\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::enableFixedLocation(bool enable) {\n  setUInt8(Offset::enableFixedLocation(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nDM32UVCodeplug::APRSSettingsElement::revertChannelIsCurrent(unsigned int n) {\n  return 0 == getUInt16_le(Offset::revertChannelIndices()\n    + n * Offset::betweenRevertChannelIndices());\n}\n\nunsigned int\nDM32UVCodeplug::APRSSettingsElement::revertChannelIndex(unsigned int n) const {\n  return getUInt16_le(Offset::revertChannelIndices()\n    + n * Offset::betweenRevertChannelIndices()) -1 ;\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setRevertChannelIndex(unsigned int n, unsigned int idx) {\n  setUInt16_le(Offset::revertChannelIndices()\n    + n * Offset::betweenRevertChannelIndices(), idx+1);\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setRevertChannelToCurrent(unsigned int n) {\n  setUInt16_le(Offset::revertChannelIndices()\n    + n * Offset::betweenRevertChannelIndices(), 0);\n}\n\n\nInterval\nDM32UVCodeplug::APRSSettingsElement::preWaveDelay() const {\n  return Interval::fromMilliseconds((unsigned int)getUInt8(Offset::prewaveDelay()) * 100);\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setPreWaveDelay(const Interval &delay) {\n  setUInt8(Offset::prewaveDelay(), delay.milliseconds()/100);\n}\n\n\nDMRContact::Type\nDM32UVCodeplug::APRSSettingsElement::callType() const {\n  switch ((CallType)getUInt8(Offset::callType())) {\n  case CallType::Private: return DMRContact::Type::PrivateCall;\n  case CallType::Group: return DMRContact::Type::GroupCall;\n  }\n  return DMRContact::Type::PrivateCall;\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::Type::PrivateCall:\n    setUInt8(Offset::callType(), (unsigned int) CallType::Private);\n    break;\n  case DMRContact::Type::AllCall:\n  case DMRContact::Type::GroupCall:\n    setUInt8(Offset::callType(), (unsigned int) CallType::Group);\n    break;\n  }\n}\n\n\nunsigned int\nDM32UVCodeplug::APRSSettingsElement::destinationId() const {\n  return getUInt24_le(Offset::destinationId());\n}\n\nvoid\nDM32UVCodeplug::APRSSettingsElement::setDestinationId(unsigned int id) {\n  setUInt24_le(Offset::destinationId(), id);\n}\n\n\nbool\nDM32UVCodeplug::APRSSettingsElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // GNSS settings\n  ctx.config()->settings()->gnss()->setFixedPosition(fixedLocation());\n  ctx.config()->settings()->gnss()->enableFixedPosition(fixedLocationEnabled());\n\n  if (0 == destinationId())\n    return true;\n\n  auto aprs = new DMRAPRSSystem(\"DMR APRS System\");\n  if (updatePeriod().isFinite())\n    aprs->setPeriod(updatePeriod());\n  else\n    aprs->disablePeriod();\n\n  ctx.add(aprs, 0);\n  ctx.config()->posSystems()->add(aprs);\n\n  return true;\n}\n\nbool\nDM32UVCodeplug::APRSSettingsElement::link(Context &ctx, const ErrorStack &err) {\n  if (0 == destinationId())\n    return true;\n\n  auto aprs = ctx.get<DMRAPRSSystem>(0);\n  if (nullptr == aprs) {\n    errMsg(err) << \"Cannot resolve DMR APRS System at index 0!\";\n    return false;\n  }\n\n  DMRContact *cont = nullptr;\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    if (destinationId() == ctx.get<DMRContact>(i)->number()) {\n      cont = ctx.get<DMRContact>(i);\n      break;\n    }\n  }\n  if (nullptr == cont) {\n    cont = new DMRContact(callType(), \"DMR APRS Contact\", destinationId());\n    ctx.config()->contacts()->add(cont);\n  }\n\n  aprs->setContact(cont);\n  if (revertChannelIsCurrent(0)) {\n    aprs->resetRevertChannel();\n  } else {\n    if (! ctx.has<Channel>(revertChannelIndex(0))) {\n      errMsg(err) << \"Cannot resolve revert channel index \" << revertChannelIndex(0) << \".\";\n      return false;\n    }\n    if (! ctx.get<Channel>(revertChannelIndex(0))->is<DMRChannel>()) {\n      errMsg(err) << \"Revert channel with index \" << revertChannelIndex(0) << \" must be DMR channel.\";\n      return false;\n    }\n    aprs->setRevertChannel(ctx.get<Channel>(revertChannelIndex(0))->as<DMRChannel>());\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::APRSSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // GNSS settings\n  if (ctx.config()->settings()->gnss()->fixedPosition().isValid()) {\n    setFixedLocation(ctx.config()->settings()->gnss()->fixedPosition());\n    enableFixedLocation(ctx.config()->settings()->gnss()->fixedPositionEnabled());\n  }\n\n  if (0 == ctx.count<DMRAPRSSystem>()) {\n    setDestinationId(0);\n    return true;\n  }\n\n  // We can only encode a single system -> use the first\n  auto sys = ctx.get<DMRAPRSSystem>(0);\n  if (sys->periodDisabled())\n    setUpdatePeriod(Interval::infinity());\n  else\n    setUpdatePeriod(sys->period());\n  setDestinationId(sys->contact()->number());\n  setCallType(sys->contact()->type());\n\n  for (unsigned int i=0; i<Limit::revertChannels(); i++)\n    setRevertChannelToCurrent(i);\n  if (sys->hasRevertChannel())\n    setRevertChannelIndex(0, ctx.index((Channel*)sys->revertChannel()));\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::PasswordSettingsElement\n * ******************************************************************************************** */\nDM32UVCodeplug::PasswordSettingsElement::PasswordSettingsElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nbool\nDM32UVCodeplug::PasswordSettingsElement::bootPasswordEnabled() const {\n  return 165 == getUInt8(Offset::enableBootPassword());\n}\n\nQString\nDM32UVCodeplug::PasswordSettingsElement::bootPassword() const {\n  return readASCII(Offset::bootPassword(), Limit::passwordLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::setBootPassword(const QString &value) {\n  writeASCII(Offset::bootPassword(), value, Limit::passwordLength(), 0x00);\n  setUInt8(Offset::enableBootPassword(), 165);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::clearBootPassword() {\n  setUInt8(Offset::enableBootPassword(), 0);\n}\n\n\nbool\nDM32UVCodeplug::PasswordSettingsElement::writePasswordEnabled() const {\n  return 165 == getUInt8(Offset::enableWritePassword());\n}\n\nQString\nDM32UVCodeplug::PasswordSettingsElement::writePassword() const {\n  return readASCII(Offset::writePassword(), Limit::passwordLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::setWritePassword(const QString &value) {\n  writeASCII(Offset::writePassword(), value, Limit::passwordLength(), 0x00);\n  setUInt8(Offset::enableWritePassword(), 165);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::clearWritePassword() {\n  setUInt8(Offset::enableWritePassword(), 0);\n}\n\n\nbool\nDM32UVCodeplug::PasswordSettingsElement::readPasswordEnabled() const {\n  return 165 == getUInt8(Offset::enableReadPassword());\n}\n\nQString\nDM32UVCodeplug::PasswordSettingsElement::readPassword() const {\n  return readASCII(Offset::readPassword(), Limit::passwordLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::setReadPassword(const QString &value) {\n  writeASCII(Offset::readPassword(), value, Limit::passwordLength(), 0x00);\n  setUInt8(Offset::enableReadPassword(), 165);\n}\n\nvoid\nDM32UVCodeplug::PasswordSettingsElement::clearReadPassword() {\n  setUInt8(Offset::enableReadPassword(), 0);\n}\n\n\nbool\nDM32UVCodeplug::PasswordSettingsElement::encode(Context &ctx, ErrorStack err) {\n  if (ctx.config()->settings()->boot()->bootPasswordEnabled()) {\n    if (ctx.config()->settings()->boot()->bootPassword().length() > Limit::passwordLength()) {\n      errMsg(err) << \"Cannot encode boot password: password is too long.\";\n      clearBootPassword();\n      return false;\n    }\n    setBootPassword(ctx.config()->settings()->boot()->bootPassword());\n  } else {\n    clearBootPassword();\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::PasswordSettingsElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  ctx.config()->settings()->boot()->enableBootPassword(bootPasswordEnabled());\n  if (bootPasswordEnabled())\n    ctx.config()->settings()->boot()->setBootPassword(bootPassword());\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::EncrpytionKeyElement\n * ******************************************************************************************** */\nDM32UVCodeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nvoid\nDM32UVCodeplug::EncryptionKeyElement::clear() {\n  setType(Type::Off);\n  setName(\"\");\n  setKey(QByteArray(Limit::keyLength(), '\\x00'));\n}\n\n\nunsigned int\nDM32UVCodeplug::EncryptionKeyElement::keyId() const {\n  return getUInt8(Offset::keyId());\n}\n\nvoid\nDM32UVCodeplug::EncryptionKeyElement::setKeyId(unsigned int id) {\n  setUInt8(Offset::keyId(), id);\n}\n\n\nQString\nDM32UVCodeplug::EncryptionKeyElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\n\nvoid\nDM32UVCodeplug::EncryptionKeyElement::setName(const QString &value) {\n  writeASCII(Offset::name(), value, Limit::nameLength(), 0x00);\n}\n\n\nDM32UVCodeplug::EncryptionKeyElement::Type\nDM32UVCodeplug::EncryptionKeyElement::type() const {\n  return (Type)getUInt8(Offset::type());\n}\n\nvoid\nDM32UVCodeplug::EncryptionKeyElement::setType(Type type) {\n  setUInt8(Offset::type(), (unsigned int)type);\n}\n\n\nQByteArray\nDM32UVCodeplug::EncryptionKeyElement::key() const {\n  return QByteArray((const char *)_data + Offset::key(), (qsizetype) Limit::keyLength());\n}\n\nvoid\nDM32UVCodeplug::EncryptionKeyElement::setKey(const QByteArray &key) {\n  std::memset(_data + Offset::key(), 0, Limit::keyLength());\n  std::memcpy(_data + Offset::key(), key.constData(),\n    std::min(Limit::keyLength(), (unsigned int)key.length()));\n}\n\n\nEncryptionKey *\nDM32UVCodeplug::EncryptionKeyElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx);\n\n  switch (type()) {\n  case Type::Off:\n  case Type::Custom:\n    errMsg(err) << \"Cannot decode disabled or custom key.\";\n    return nullptr;\n  case Type::ARC4: {\n    auto key = new ARC4EncryptionKey();\n    key->setName(name());\n    if (! key->setKey(this->key().left(5), err))\n      return nullptr;\n    return  key;\n  }\n  case Type::AES128: {\n    auto key = new AESEncryptionKey();\n    key->setName(name());\n    if (! key->setKey(this->key().left(16), err))\n      return nullptr;\n    return  key;\n  }\n  case Type::AES256: {\n    auto key = new AESEncryptionKey();\n    key->setName(name());\n    if (! key->setKey(this->key(), err))\n      return nullptr;\n    return  key;\n  }\n  }\n\n  errMsg(err) << \"Unhandled key type \" << (unsigned int)type() << \".\";\n  return nullptr;\n}\n\n\nbool\nDM32UVCodeplug::EncryptionKeyElement::encode(const EncryptionKey *key, Context &ctx, const ErrorStack &err) {\n  setKeyId(ctx.index((EncryptionKey*)key)+1);\n  setName(key->name());\n  setKey(key->key());\n\n  if (key->is<ARC4EncryptionKey>()) {\n    setType(Type::ARC4);\n  } else if (key->is<AESEncryptionKey>() && (16 == key->key().size())) {\n    setType(Type::AES128);\n  } else if (key->is<AESEncryptionKey>() && (32 == key->key().size())) {\n    setType(Type::AES256);\n  } else {\n    errMsg(err) << \"Cannot encode encryption key: Format not supported.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug::EncryptionKeyBankElement\n * ******************************************************************************************** */\nDM32UVCodeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nbool\nDM32UVCodeplug::EncryptionKeyBankElement::keyValid(unsigned int idx) const {\n  return (EncryptionKeyElement::Type::ARC4 == key(idx).type()) ||\n    (EncryptionKeyElement::Type::AES128 == key(idx).type()) ||\n      (EncryptionKeyElement::Type::AES256 == key(idx).type());\n}\n\nDM32UVCodeplug::EncryptionKeyElement\nDM32UVCodeplug::EncryptionKeyBankElement::key(unsigned int idx) const {\n  return EncryptionKeyElement(_data + Offset::keys() + idx*Offset::betweenKeys());\n}\n\n\nbool\nDM32UVCodeplug::EncryptionKeyBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::keys(); i++) {\n    if (! keyValid(i))\n      continue;\n    auto key = this->key(i).decode(ctx, err);\n    if (nullptr == key) {\n      errMsg(err) << \"Cannot decode encryption key at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::EncryptionKeyBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::keys(); i++) {\n    key(i).clear();\n    if (i >= ctx.count<EncryptionKey>())\n      continue;\n    if (! key(i).encode(ctx.get<EncryptionKey>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode encryption key.\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DM32UVCodeplug\n * ******************************************************************************************** */\nDM32UVCodeplug::DM32UVCodeplug(QObject *parent)\n  : Codeplug{parent}\n{\n  addImage(\"Baofeng DM32UV Codeplug\");\n}\n\n\nConfig *\nDM32UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *copy = Codeplug::preprocess(config, err);\n  if (nullptr == copy) {\n    errMsg(err) << \"Cannot pre-process DM32UV codeplug.\";\n    return nullptr;\n  }\n\n  // Remove all M17 channels\n  ObjectFilterVisitor m17Filter{M17Channel::staticMetaObject};\n  if (! m17Filter.process(copy, err)) {\n    errMsg(err) << \"Remove M17 channels.\";\n    delete copy;\n    return nullptr;\n  }\n\n  // Keep only ARC4, AES (128 and 256)\n  EncryptionKeyFilterVisitor encFilter{\n    EncryptionKeyFilterVisitor::Filter(ARC4EncryptionKey::staticMetaObject, 40),\n    EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 128),\n    EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 256)\n  };\n  if (! encFilter.process(copy, err)) {\n    errMsg(err) << \"Clear encryption keys.\";\n    errMsg(err) << \"Cannot pre-process DM32UV codeplug.\";\n    delete copy;\n    return nullptr;\n  }\n\n  // Split dual-zones into two.\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(copy, err)) {\n    errMsg(err) << \"Cannot split dual VFO zones.\";\n    errMsg(err) << \"Cannot pre-process DM32UV codeplug.\";\n    delete copy;\n    return nullptr;\n  }\n\n  return copy;\n}\n\nbool\nDM32UVCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process DM32UV codeplug.\";\n    return false;\n  }\n\n  // Merge split zones into one.\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot post-process DM32UV codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // All indices as 0-based. That is, the first channel gets index 0 etc.\n\n  // There must be a default DMR radio ID.\n  if (nullptr == ctx.config()->settings()->defaultId()) {\n    errMsg(err) << \"No default DMR radio ID specified.\";\n    errMsg(err) << \"Cannot index codeplug for encoding for the BTECH DM32UV.\";\n    return false;\n  }\n\n  // Map radio IDs\n  for (int i=0; i<ctx.config()->radioIDs()->count(); i++) {\n    if (ctx.config()->radioIDs()->get(i)->is<DMRRadioID>())\n      ctx.add(ctx.config()->radioIDs()->get(i)->as<DMRRadioID>(), i);\n  }\n\n  // Map DMR contacts\n  for (int i=0, d=0; i<config->contacts()->count(); i++) {\n    if (config->contacts()->contact(i)->is<DMRContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DMRContact>(), d); d++;\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i);\n\n  // Map channels\n  for (int i=0; i<config->channelList()->count(); i++)\n    ctx.add(config->channelList()->channel(i), i);\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i);\n\n  // Map roaming channels and zones\n  for (int i=0; i<config->roamingChannels()->count(); i++)\n    ctx.add(config->roamingChannels()->channel(i), i);\n  for (int i=0; i<config->roamingZones()->count(); i++)\n    ctx.add(config->roamingZones()->zone(i), i);\n\n  // Map encryption keys\n  if (config->commercialExtension()) {\n    for (int i=0; i<config->commercialExtension()->encryptionKeys()->count(); i++)\n      ctx.add(config->commercialExtension()->encryptionKeys()->key(i), i);\n  }\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  Context ctx(config);\n  ctx.remTable(&BasicEncryptionKey::staticMetaObject, true);\n  ctx.remTable(&ARC4EncryptionKey::staticMetaObject, true);\n  ctx.remTable(&AESEncryptionKey::staticMetaObject, true);\n  ctx.addTable(&EncryptionKey::staticMetaObject);\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Index elements.\";\n    return false;\n  }\n\n  if (! encodeElements(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::decode(Config *config, const ErrorStack &err) {\n  Context ctx(config);\n\n  if (! decodeElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode elements.\";\n    return false;\n  }\n\n  if (! linkElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode elements.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! decodeChannels(ctx, err)) {\n    errMsg(err) << \"Cannot decode channels.\";\n    return false;\n  }\n\n  if (! decodeContacts(ctx, err)) {\n    errMsg(err) << \"Cannot decode contacts.\";\n    return false;\n  }\n\n  if (! GroupListBankElement(data(Offset::groupListBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode group lists.\";\n    return false;\n  }\n\n  if (! RadioIdBankElement(data(Offset::radioIdBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode radio IDs.\";\n    return false;\n  }\n\n  if (! decodeZones(ctx, err)) {\n    errMsg(err) << \"Cannot decode zones.\";\n    return false;\n  }\n\n  if (! ScanListBankElement(data(Offset::scanListBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode scan lists.\";\n    return false;\n  }\n\n  if (! RoamingChannelBankElement(data(Offset::roamingChannelBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode roaming channels.\";\n    return false;\n  }\n\n  if (! RoamingZoneBankElement(data(Offset::roamingZoneBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode roaming zones.\";\n    return false;\n  }\n\n  if (! GeneralSettingsElement(data(Offset::generalSettings())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings.\";\n    return false;\n  }\n\n  if (! PasswordSettingsElement(data(Offset::passwordSettings())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode password settings.\";\n    return false;\n  }\n\n  if (! APRSSettingsElement(data(Offset::aprsSettings())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode APRS settings.\";\n    return false;\n  }\n\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode encryption keys.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) {\n  if (! linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  if (! GroupListBankElement(data(Offset::groupListBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n\n  if (! linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  if (! ScanListBankElement(data(Offset::scanListBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link scan lists.\";\n    return false;\n  }\n\n  if (! RoamingZoneBankElement(data(Offset::roamingZoneBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link roaming zones.\";\n    return false;\n  }\n\n  if (! APRSSettingsElement(data(Offset::aprsSettings())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link APRS settings.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::encodeElements(Context &ctx, const ErrorStack &err) {\n  if (! encodeChannels(ctx, err)) {\n    errMsg(err) << \"Cannot encode channels.\";\n    return false;\n  }\n\n  if (! encodeContacts(ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  if (! image(0).isAllocated(Offset::groupListBank()))\n    image(0).addElement(Offset::groupListBank(), GroupListBankElement::size());\n  if (! GroupListBankElement(data(Offset::groupListBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n\n  if (! image(0).isAllocated(Offset::radioIdBank()))\n    image(0).addElement(Offset::radioIdBank(), RadioIdBankElement::size());\n  if (! RadioIdBankElement(data(Offset::radioIdBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode radio IDs.\";\n    return false;\n  }\n\n  if (! encodeZones(ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  if (! image(0).isAllocated(Offset::scanListBank()))\n    image(0).addElement(Offset::scanListBank(), ScanListBankElement::size());\n  if (! ScanListBankElement(data(Offset::scanListBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode scan lists.\";\n    return false;\n  }\n\n\n  if (! image(0).isAllocated(Offset::roamingChannelBank()))\n    image(0).addElement(Offset::roamingChannelBank(), RoamingChannelBankElement::size());\n  if (! RoamingChannelBankElement(data(Offset::roamingChannelBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode roaming channels.\";\n    return false;\n  }\n  if (! image(0).isAllocated(Offset::roamingZoneBank()))\n    image(0).addElement(Offset::roamingZoneBank(), RoamingZoneBankElement::size());\n  if (! RoamingZoneBankElement(data(Offset::roamingZoneBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode roaming zones.\";\n    return false;\n  }\n\n  if (! image(0).isAllocated(Offset::generalSettings()))\n    image(0).addElement(Offset::generalSettings(), Limit::blockSize());\n  if (! GeneralSettingsElement(data(Offset::generalSettings())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode settings.\";\n    return false;\n  }\n  if (! APRSSettingsElement(data(Offset::aprsSettings())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode APRS settings.\";\n    return false;\n  }\n  if (! PasswordSettingsElement(data(Offset::passwordSettings())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode password settings.\";\n    return false;\n  }\n\n  if (! image(0).isAllocated(Offset::extendedSettings()))\n    image(0).addElement(Offset::extendedSettings(), Limit::blockSize());\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode encryption keys.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::decodeChannels(Context &ctx, const ErrorStack &err) {\n  ChannelBankElement bank(data(Offset::channelBanks()));\n  for (unsigned int i=0; i<bank.channelCount(); i++) {\n    auto blockNumber = ChannelBankElement::channelBank(i);\n    auto indexInBlock = ChannelBankElement::indexInBank(i);\n    uint32_t addr = Offset::channelBanks()\n                    + (0 == blockNumber ? ChannelBankElement::Offset::channelBlock0()\n                                        : blockNumber * ChannelBankElement::Offset::betweenChannelBlocks())\n                    + indexInBlock * ChannelElement::size();\n    // Create channel\n    auto ch = ChannelElement(data(addr)).decode(ctx, err);\n    if (nullptr == ch) {\n      errMsg(err) << \"Cannot decode channel at index \" << i << \".\";\n      return false;\n    }\n    // Store in config and context\n    ctx.config()->channelList()->add(ch);\n    ctx.add(ch, i);\n  }\n\n  // Link channel extensions\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    unsigned int blockNumber  = i / ChannelExtensionBankElement::Limit::count();\n    unsigned int indexInBlock = i % ChannelExtensionBankElement::Limit::count();\n    uint32_t addr = Offset::channelExtensionBanks()\n        + blockNumber * ChannelExtensionBankElement::Offset::betweenBanks()\n        + indexInBlock * ChannelExtensionElement::size();\n    if (! ChannelExtensionElement(data(addr)).decode(ctx.get<Channel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot decode channel extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  ChannelBankElement bank(data(Offset::channelBanks()));\n  for (unsigned int i=0; i<bank.channelCount(); i++) {\n    unsigned int blockNumber  = ChannelBankElement::channelBank(i);\n    unsigned int indexInBlock = ChannelBankElement::indexInBank(i);\n    uint32_t addr = Offset::channelBanks()\n                    + (0 == blockNumber ? ChannelBankElement::Offset::channelBlock0()\n                                        : blockNumber * ChannelBankElement::Offset::betweenChannelBlocks())\n                    + indexInBlock * ChannelElement::size();\n    // get channel\n    auto ch = ctx.get<Channel>(i);\n    // link channel\n    if (! ChannelElement(data(addr)).link(ch, ctx, err)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  // Link channel extensions\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    unsigned int blockNumber  = i / ChannelExtensionBankElement::Limit::count();\n    unsigned int indexInBlock = i % ChannelExtensionBankElement::Limit::count();\n    uint32_t addr = Offset::channelExtensionBanks()\n        + blockNumber * ChannelExtensionBankElement::Offset::betweenBanks()\n        + indexInBlock * ChannelExtensionElement::size();\n    if (! ChannelExtensionElement(data(addr)).link(ctx.get<Channel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot link channel extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::encodeChannels(Context &ctx, const ErrorStack &err) {\n  // Allocate blocks\n  auto numBlocks = Limit::channelBanks().limit(\n    ChannelBankElement::bankCount(ctx.count<Channel>()));\n  for (unsigned int b=0; b<numBlocks; b++) {\n    unsigned int addr = Offset::channelBanks() + b*Limit::blockSize();\n    if (! isAllocated(addr))\n      image(0).addElement(addr, Limit::blockSize());\n  }\n\n  // Encode channels\n  ChannelBankElement bank(data(Offset::channelBanks()));\n  bank.setChannelCount(std::min(ctx.count<Channel>(), ChannelBankElement::Limit::channels()));\n  for (unsigned int i=0; i<bank.channelCount(); i++) {\n    unsigned int blockNumber  = ChannelBankElement::channelBank(i);\n    unsigned int indexInBlock = ChannelBankElement::indexInBank(i);\n    uint32_t addr = Offset::channelBanks()\n                    + (0 == blockNumber ? ChannelBankElement::Offset::channelBlock0()\n                                        : blockNumber * ChannelBankElement::Offset::betweenChannelBlocks())\n                    + indexInBlock * ChannelElement::size();\n    // Create channel\n    if (! ChannelElement(data(addr)).encode(ctx.get<Channel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode channel at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  // Allocate blocks for extensions\n  auto numExtBlocks = (ctx.count<Channel>()>ChannelExtensionBankElement::Limit::count()) ? 2u : 1u;\n  for (unsigned int i=0; i<numExtBlocks; i++) {\n    unsigned int addr = Offset::channelExtensionBanks() + i*Limit::blockSize();\n    if (! isAllocated(addr))\n      image(0).addElement(addr, Limit::blockSize());\n  }\n\n  // Encode channel extensions\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    unsigned int blockNumber  = i / ChannelExtensionBankElement::Limit::count();\n    unsigned int indexInBlock = i % ChannelExtensionBankElement::Limit::count();\n    uint32_t addr = Offset::channelExtensionBanks()\n        + blockNumber * ChannelExtensionBankElement::Offset::betweenBanks()\n        + indexInBlock * ChannelExtensionElement::size();\n    if (! ChannelExtensionElement(data(addr)).encode(ctx.get<Channel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode channel extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::decodeContacts(Context &ctx, const ErrorStack &err) {\n  ContactIndexElement index(data(Offset::contactIndex()));\n  for (unsigned int i=0,c=0; (i<ContactBankElement::Limit::contacts()) && (c < index.contactCount()); i++) {\n    if (! index.bitmap().isEncoded(i))\n      continue;\n    unsigned int blockIndex    = i / ContactBankElement::Limit::contactsPerBlock(),\n      indexInBlock = i % ContactBankElement::Limit::contactsPerBlock();\n    uint32_t addr = Offset::contactBanks()\n                    + blockIndex * ContactBankElement::Offset::betweenBlocks()\n                    + indexInBlock * ContactElement::size();\n    auto contact = ContactElement(data(addr)).decode(ctx, err);\n    logDebug() << \"Decoded contact '\" << contact->name()\n               << \"' (\" << contact->number() << \") at index \" << i << \".\";\n    ctx.add(contact, c++);\n    ctx.config()->contacts()->add(contact);\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::encodeContacts(Context &ctx, const ErrorStack &err) {\n  // Allocate index\n  if (! isAllocated(Offset::contactIndex()))\n    image(0).addElement(Offset::contactIndex(), ContactIndexElement::size());\n\n    // Allocate blocks\n  auto numBlocks = Limit::contactBanks().limit(\n    ctx.count<DigitalContact>()/ContactBankElement::Limit::contactsPerBlock()\n    + ((0 != ctx.count<DigitalContact>() % ContactBankElement::Limit::contactsPerBlock()) ? 1 : 0));\n  for (unsigned int b=0; b<numBlocks; b++) {\n    unsigned int addr = Offset::contactBanks() + b*Limit::blockSize();\n    if (! isAllocated(addr))\n      image(0).addElement(addr, Limit::blockSize());\n  }\n\n  if (! ContactIndexElement(data(Offset::contactIndex())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode contact index.\";\n    return false;\n  }\n\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    unsigned int blockIndex    = i / ContactBankElement::Limit::contactsPerBlock(),\n      indexInBlock = i % ContactBankElement::Limit::contactsPerBlock();\n    uint32_t addr = Offset::contactBanks()\n                    + blockIndex * ContactBankElement::Offset::betweenBlocks()\n                    + indexInBlock * ContactElement::size();\n    if (! ContactElement(data(addr)).encode(ctx.get<DMRContact>(i), err)) {\n      errMsg(err) << \"Cannot encode contact '\" << ctx.get<DMRContact>(i)->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::decodeZones(Context &ctx, const ErrorStack &err) {\n  ZoneBankElement bank(data(Offset::zoneBanks()));\n  for (unsigned int i=0; i<bank.count(); i++) {\n    unsigned int blockNumber  = i / ZoneBankElement::Limit::zonesPerBlock();\n    unsigned int indexInBlock = i % ZoneBankElement::Limit::zonesPerBlock();\n    uint32_t addr = Offset::zoneBanks()\n                    + (0 == blockNumber ? ZoneBankElement::Offset::zones0()\n                                        : blockNumber * ZoneBankElement::Offset::betweenBlocks())\n                    + indexInBlock * ZoneElement::size();\n    // Create zone\n    auto zone = ZoneElement(data(addr)).decode(ctx, err);\n    if (nullptr == zone) {\n      errMsg(err) << \"Cannot decode zone at index \" << i << \".\";\n      return false;\n    }\n    // Store in config and context\n    ctx.config()->zones()->add(zone);\n    ctx.add(zone, i);\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  ZoneBankElement bank(data(Offset::zoneBanks()));\n  for (unsigned int i=0; i<bank.count(); i++) {\n    unsigned int blockNumber  = i / ZoneBankElement::Limit::zonesPerBlock();\n    unsigned int indexInBlock = i % ZoneBankElement::Limit::zonesPerBlock();\n    uint32_t addr = Offset::zoneBanks()\n                    + (0 == blockNumber ? ZoneBankElement::Offset::zones0()\n                                        : blockNumber * ZoneBankElement::Offset::betweenBlocks())\n                    + indexInBlock * ZoneElement::size();\n    // get zone\n    auto zone = ctx.get<Zone>(i);\n    // link zone\n    if (! ZoneElement(data(addr)).link(zone, ctx, err)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVCodeplug::encodeZones(Context &ctx, const ErrorStack &err) {\n  // Allocate blocks\n  auto numBlocks = Limit::zoneBanks().limit(\n    ctx.count<Zone>()/ZoneBankElement::Limit::zonesPerBlock()\n    + ((0 != ctx.count<Zone>() % ZoneBankElement::Limit::zonesPerBlock()) ? 1 : 0));\n  for (unsigned int b=0; b<numBlocks; b++) {\n    unsigned int addr = Offset::zoneBanks() + b*Limit::blockSize();\n    if (! isAllocated(addr))\n      image(0).addElement(addr, Limit::blockSize());\n  }\n  // Encode all zones\n  ZoneBankElement bank(data(Offset::zoneBanks()));\n  bank.setCount(std::min(ZoneBankElement::Limit::zones(), ctx.count<Zone>()));\n  for (unsigned int i=0; i<bank.count(); i++) {\n    unsigned int blockNumber  = i / ZoneBankElement::Limit::zonesPerBlock();\n    unsigned int indexInBlock = i % ZoneBankElement::Limit::zonesPerBlock();\n    uint32_t addr = Offset::zoneBanks()\n                    + (0 == blockNumber ? ZoneBankElement::Offset::zones0()\n                                        : blockNumber * ZoneBankElement::Offset::betweenBlocks())\n                    + indexInBlock * ZoneElement::size();\n    // encode zone\n    if (! ZoneElement(data(addr)).encode(ctx.get<Zone>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode zone '\" << ctx.get<Zone>(i)->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dm32uv_codeplug.hh",
    "content": "#ifndef DM32UV_CODEPLUG_HH\n#define DM32UV_CODEPLUG_HH\n\n#include <QObject>\n#include <QString>\n\n#include \"channel.hh\"\n#include \"codeplug.hh\"\n#include \"contact.hh\"\n#include \"frequency.hh\"\n#include \"ranges.hh\"\n#include \"roamingchannel.hh\"\n#include \"bootsettings.hh\"\n#include \"smsextension.hh\"\n#include \"gnsssettings.hh\"\n#include \"dmrsettings.hh\"\n\n// forward declaration\nclass Zone;\nclass SMSTemplate;\n\n\n\n/** Implementation of the binary codeplug for the Baofeng DM32UV.\n * @ingroup dm32uv */\nclass DM32UVCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements encoding channel for the binary codeplug. */\n  class ChannelElement: public Element\n  {\n  public:\n    /** Possible channel types. */\n    enum class ChannelType {\n      FM = 0, DMR = 1, FMFixed = 2, DMRFixed = 3\n    };\n\n    /** Possible power settings. */\n    enum class Power {\n      Low = 0, Medium = 1, High = 2\n    };\n\n    /** Possible admit criteria. */\n    enum class Admit {\n      Always = 0, ChannelFree = 1, ToneOrCCMatch = 2, ToneMismatch = 3\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    void clear() override;\n\n    /** Returns the channel name. */\n    virtual QString name() const;\n    /** Sets the channel name. */\n    virtual void setName(const QString &name);\n\n    /** Returns the RX frequency. */\n    virtual Frequency rxFrequency() const;\n    /** Set RX frequency. */\n    virtual void setRXFrequency(const Frequency &freq);\n\n    /** Returns @c true, if a valid TX frequency is stored. */\n    virtual bool validTXFrequency() const;\n    /** Returns the TX frequency. */\n    virtual Frequency txFrequency() const;\n    /** Set TX frequency. */\n    virtual void setTXFrequency(const Frequency &freq);\n    /** Invalidates the TX frequency. */\n    virtual void clearTXFrequency();\n\n    /** Returns the channel type. */\n    virtual ChannelType channelType() const;\n    /** Sets the channel type. */\n    virtual void setChannelType(ChannelType type);\n\n    /** Returns the channel power setting. */\n    virtual Channel::Power power() const;\n    /** Sets the channel power. */\n    virtual void setPower(Channel::Power power);\n\n    /** Returns @c true, if the lone-worker feature is enabled. */\n    virtual bool loneWorkerEnabled() const;\n    /** Enables/disables lone worker feature. */\n    virtual void enableLoneWorker(bool enable);\n\n    /** Returns the bandwidth for FM channels. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the FM channel bandwidth. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns @c true, if the scan list index is set. */\n    virtual bool validScanListIndex() const;\n    /** Returns the 0-based scan list index. */\n    virtual unsigned int scanListIndex() const;\n    /** Sets the scan list index. */\n    virtual void setScanListIndex(unsigned int idx);\n    /** Invalidates the scan list index. */\n    virtual void clearScanListIndex();\n\n    /** Returns @c true if talkaround is enabled on this channel. */\n    virtual bool talkaroundEnabled() const;\n    /** Enables/disables talkaround feature on this channel. */\n    virtual void enableTalkaround(bool enable);\n\n    /** Returns the admit criterion. */\n    virtual Admit admitCriterion() const;\n    /** Sets the admit criterion. */\n    virtual void setAdmitCriterion(Admit admit);\n\n    /** Returns @c true if DMR APRS reception is enabled. */\n    virtual bool rxDMRAPRSEnabled() const;\n    /** Enables/disables DMR APRS reception. */\n    virtual void enableRXDMRAPRS(bool enable);\n\n    /** Retruns @c true, if the emergency notification is enabled. */\n    virtual bool emergencyNotificationEnabled() const;\n    /** Enables/disables emergency notification. */\n    virtual void enableEmergencyNotification(bool enable);\n    /** Retruns @c true, if the emergency ACK is enabled. */\n    virtual bool emergencyACKEnabled() const;\n    /** Enables/disables emergency notification. */\n    virtual void enableEmergencyACK(bool enable);\n    /** Returns @c true if the emergency system index is set. */\n    virtual bool validEmergencySystemIndex() const;\n    /** Returns the emergency system index. */\n    virtual unsigned int emergencySystemIndex() const;\n    /** Sets the emergency system index. */\n    virtual void setEmergencySystemIndex(unsigned int idx);\n    /** Invalidates the emergency system index. */\n    virtual void clearEmergencySystemIndex();\n\n    /** Returns FM and DMR squelch level. */\n    virtual Level squelchLevel() const;\n    /** Sets the FM and DMR squelch level. */\n    virtual void setSquelchLevel(Level level);\n\n    /** Returns @c true if the channel is RX only. */\n    virtual bool rxOnlyEnabled() const;\n    /** Enables RX only for the channel. */\n    virtual void enableRXOnly(bool enable);\n\n    /** Returns @c true if DMR APRS is enabled. */\n    virtual bool dmrAPRSEnabled() const;\n    /** Enables DMR APRS for the channel. */\n    virtual void enableDMRAPRS(bool enable);\n\n    /** Return @c true if private calls are confirmed. */\n    virtual bool privateCallACKEnabled() const;\n    /** Enables confirmation of private calls. */\n    virtual void enablePrivateCallACK(bool enable);\n    /** Return @c true if data is confirmed. */\n    virtual bool dataACKEnabled() const;\n    /** Enables confirmation of data. */\n    virtual void enableDataACK(bool enable);\n    /** Return @c true if DCDM (dual capacity direct mode) is enabled. */\n    virtual bool dcdmEnabled() const;\n    /** Enables DCDM (dual capacity direct mode).. */\n    virtual void enableDCDM(bool enable);\n\n    /** Returns the timeslot of the channel. */\n    virtual DMRChannel::TimeSlot timeslot() const;\n    /** Sets the timeslot of the channel. */\n    virtual void setTimeslot(DMRChannel::TimeSlot ts);\n    /** Returns the color code of the channel. */\n    virtual unsigned int colorCode() const;\n    /** Sets the color code of the channel. */\n    virtual void setColorCode(unsigned int cc);\n\n    /** Returns @c true if encryption is enabled. */\n    virtual bool encryptionEnabled() const;\n    /* Enables encryption. */\n    virtual void enableEncryption(bool enable);\n\n    /** Returns @c true if group list index is valid. */\n    virtual bool validGroupListIndex() const;\n    /** Get group list index. */\n    virtual unsigned int groupListIndex() const;\n    /** Set group list index. */\n    virtual void setGroupListIndex(unsigned int idx);\n    /** Invalidates group list index. */\n    virtual void clearGroupListIndex();\n\n    /** Returns DMR APRS report channel index. */\n    virtual unsigned int dmrAPRSChannelIndex() const;\n    /** Sets the DMR APRS report channel index. */\n    virtual void setDMRAPRSChannelIndex(unsigned int idx);\n\n    /** Returns the RX tone. */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the RX tone. */\n    virtual void setRXTone(const SelectiveCall &tone);\n    /** Returns the TX tone. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the TX tone. */\n    virtual void setTXTone(const SelectiveCall &tone);\n\n    /** Returns @c true, if the VOX is enabled. */\n    virtual bool voxEnabled() const;\n    /** Enables the VOX. */\n    virtual void enableVOX(bool enable);\n\n    /** Returns the DMR radio id index. */\n    virtual unsigned int dmrIdIndex() const;\n    /** Sets the dmr ID index. */\n    virtual void setDMRIdIndex(unsigned int id);\n\n    /** Constructs a channel object. */\n    virtual Channel *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the channel object. */\n    virtual bool link(Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes a channel. */\n    virtual bool encode(const Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** Maximum squelch level. */\n      static constexpr Range<unsigned int> squelchLevel() { return {0, 15}; }\n    };\n\n  protected:\n    /** Helper function to decode selective call. */\n    static SelectiveCall decodeSelectiveCall(uint16_t code);\n    /** Helper function to encode selective call. */\n    static uint16_t encodeSelectiveCall(const SelectiveCall &tone);\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()        { return 0x0000; }\n      static constexpr unsigned int rxFrequency() { return 0x0010; }\n      static constexpr unsigned int txFrequency() { return 0x0014; }\n      static constexpr Bit channelType()          { return {0x0018, 4}; }\n      static constexpr Bit rxOnly()               { return {0x0018, 3}; }\n      static constexpr Bit power()                { return {0x0018, 1}; }\n      static constexpr Bit loneWorker()           { return {0x0018, 0}; }\n      static constexpr Bit bandwidth()            { return {0x0019, 7}; }\n      static constexpr Bit scanListIndex()        { return {0x0019, 2}; }\n      static constexpr Bit preventTalkaround()    { return {0x001a, 7}; }\n      static constexpr Bit admitCriterion()       { return {0x001a, 4}; }\n      static constexpr Bit rxDMRAPRS()            { return {0x001a, 2}; }\n      static constexpr Bit emergencyNotification() { return {0x001b, 7}; }\n      static constexpr Bit emergencyACK()         { return {0x001b, 6}; }\n      static constexpr Bit emergencySystemIndex() { return {0x001b, 0}; }\n      static constexpr Bit squelchLevel()         { return {0x001c, 4}; }\n      static constexpr Bit dmrAPRS()              { return {0x001c, 2}; }\n      static constexpr Bit privateCallACK()       { return {0x001d, 7}; }\n      static constexpr Bit dataACK()              { return {0x001d, 6}; }\n      static constexpr Bit dcdm()                 { return {0x001d, 5}; }\n      static constexpr Bit timeslot()             { return {0x001d, 4}; }\n      static constexpr Bit colorcode()            { return {0x001d, 0}; }\n      static constexpr Bit encryptionEnable()     { return {0x001f, 6}; }\n      static constexpr Bit groupListIndex()       { return {0x001f, 0}; }\n      static constexpr Bit dmrAPRSChannelIndex()  { return {0x0020, 0}; }\n      static constexpr unsigned int rxTone()      { return 0x0021; }\n      static constexpr unsigned int txTone()      { return 0x0023; }\n      static constexpr Bit vox()                  { return {0x0025, 4}; }\n      static constexpr Bit showPTTId()            { return {0x0026, 7}; }\n      static constexpr Bit optSigEnable()         { return {0x0026, 4}; }\n      static constexpr Bit optSigType()           { return {0x0026, 0}; }\n      static constexpr Bit pttIdEnable()          { return {0x0029, 2}; }\n      static constexpr unsigned int dmrIdIndex()  { return 0x002b; }\n      /// @endcond\n    };\n  };\n\n\n  /** Channel bank element. */\n  class ChannelBankElement: public Element\n  {\n  public:\n    /** Constructor. */\n    explicit ChannelBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Returns the channel count. */\n    virtual unsigned int channelCount() const;\n    /** Sets the channel count. */\n    virtual void setChannelCount(unsigned int n);\n\n  public:\n    /** Returns the block index for the given channel index. */\n    static unsigned int channelBank(unsigned int index);\n    /** Returns the channel index within the block for the given channel index. */\n    static unsigned int indexInBank(unsigned int index);\n    /** Computes the number of channel banks required to encode the given number of channels. */\n    static unsigned int bankCount(unsigned int channelCount);\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of channels. */\n      static constexpr unsigned int channels() { return 4000; }\n      /** Maximum number of channels in block 0. */\n      static constexpr unsigned int channelsInBlock0() { return 84; }\n      /** Maximum number of channels per block. */\n      static constexpr unsigned int channelsPerBlock() { return 85; }\n    };\n\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int channelCount() { return 0x0000; }\n      static constexpr unsigned int channelBlock0() { return 0x0010; }\n      static constexpr unsigned int betweenChannelBlocks() {\n        return DM32UVCodeplug::Limit::blockSize();\n      }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements an extended settings field for each channel.\n   * This is obviously a bug fix by a very unexperienced FW engineer. They hot-fixed the missing\n   * transmit contact via these extended settings although there are enough unused bits available\n   * within the channel element. Moveover, the encoding is a wild mix of little and big endian. */\n  class ChannelExtensionElement: public Element\n  {\n  public:\n    /** Constructor. */\n    explicit ChannelExtensionElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0002; }\n\n    /** Resets the element. */\n    void clear() override;\n\n    /** Returns @c true if a TX contact index is set. */\n    virtual bool hasContactIndex() const;\n    /** Returns the TX contact index. */\n    virtual unsigned int contactIndex() const;\n    /** Sets the contact index. */\n    virtual void setContactIndex(unsigned int index);\n    /** Resets the contact index. */\n    virtual void clearContactIndex();\n\n    /** Encodes the extended settings from the given channel. */\n    virtual bool encode(const Channel *ch, Context &ctx, const ErrorStack &err);\n    /** Updates the given channel. */\n    virtual bool decode(Channel *ch, Context &ctx, const ErrorStack &err) const;\n    /** Links the given channel. */\n    virtual bool link(Channel *ch, Context &ctx, const ErrorStack &err) const;\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit indexMSN() { return { 0x0000, 4}; }\n      static constexpr unsigned int indexLSB() { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Encodes a bank of channel extension settings. */\n  class ChannelExtensionBankElement: public Element\n  {\n  public:\n    /** Some limits. */\n    struct Limit : Element::Limit {\n      /** The number of elements within each bank. */\n      static constexpr unsigned int count() { return 2047; }\n    };\n\n    /** Some offsets. */\n    struct Offset: Element::Offset {\n      /** Offset betwenn banks. */\n      static constexpr unsigned int betweenBanks(){ return DM32UVCodeplug::Limit::blockSize(); }\n    };\n  };\n\n\n  /** Implements a single DMR contact. */\n  class ContactElement: public Element\n  {\n  public:\n    /** Possible contact types. */\n    enum class Type {\n      Private = 3, Group = 4, All = 5\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0018; }\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Returns the call type. */\n    virtual DMRContact::Type callType() const;\n    /** Sets the call type. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns the DMR Id. */\n    virtual unsigned int dmrId() const;\n    /** Sets the DMR Id. */\n    virtual void setDMRId(unsigned int id);\n\n    /** Constructs a contact object. */\n    virtual DMRContact *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the contact. */\n    virtual bool encode(const DMRContact *contact, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()        { return 0x0002; }\n      static constexpr unsigned int dmrId()       { return 0x0013; }\n      static constexpr unsigned int callType()    { return 0x0016; }\n      // @endcond\n    };\n  };\n\n\n  /** Implements the contact bank. */\n  class ContactBankElement: public Element\n  {\n  public:\n    /** Some limts for the contact bank. */\n    struct Limit: Element::Limit {\n      /** Maximum number of contacts. */\n      static constexpr unsigned int contacts() { return 800; }\n      static constexpr unsigned int contactsPerBlock() { return 170; }\n    };\n\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int betweenBlocks() { return 0x1000; };\n      /// @endcond\n    };\n  };\n\n\n  /** Contact index block. */\n  class ContactIndexElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactIndexElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Contact bitmap element. */\n    class ContactBitmapElement: public InvertedBitmapElement\n    {\n    public:\n      /** Constructor. */\n      ContactBitmapElement(uint8_t *ptr);\n      /** Returns the size of the bitmap. */\n      static constexpr unsigned int size() { return 0x0064; }\n    };\n\n    /** Contact index entry. */\n    class EntryElement: public Element\n    {\n    public:\n      typedef ContactElement::Type Type;\n\n    protected:\n      /** Hidden constructor. */\n      EntryElement(uint8_t *ptr, size_t size);\n\n    public:\n      /** Constructor. */\n      EntryElement(uint8_t *ptr);\n\n      void clear() override;\n\n      /** Returns the size of the element. */\n      static constexpr unsigned int size() { return 0x0002; }\n\n      /** Returns @c true if the entry is valid. */\n      bool isValid() const override;\n\n      /** Returns the call type. */\n      virtual DMRContact::Type callType() const;\n      /** Sets the call type. */\n      virtual void setCallType(DMRContact::Type type);\n\n      /** Returns the contact index. */\n      virtual unsigned int index() const;\n      /** Sets the index. */\n      virtual void setIndex(unsigned int idx);\n    };\n\n  public:\n    /** Constructor. */\n    ContactIndexElement(uint8_t *ptr);\n\n    /** Size of this element. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the number of contacts encoded. */\n    virtual unsigned int contactCount() const;\n    /** Sets the number of contacts. */\n    virtual void setContactCount(unsigned int n);\n    /** Returns the number of group call contacts encoded. */\n    virtual unsigned int groupCallCount() const;\n    /** Sets the number of group call contacts. */\n    virtual void setGroupCallCount(unsigned int n);\n    /** Returns the number of private call contacts encoded. */\n    virtual unsigned int privateCallCount() const;\n    /** Sets the number of private call contacts. */\n    virtual void setPrivateCallCount(unsigned int n);\n\n    /** Returns the contact bitmap. */\n    virtual ContactBitmapElement bitmap() const;\n\n    /** Returns the n-th index entry. */\n    virtual EntryElement indexEntry(unsigned n);\n    /** Returns the n-th sorted index entry. */\n    virtual EntryElement sortedIndexEntry(unsigned n);\n\n    /** Encodes the contact index. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static unsigned int contactCount()    { return 0x0000; }\n      static unsigned int groupCount()      { return 0x0002; }\n      static unsigned int privateCount()    { return 0x0004; }\n      static unsigned int bitmap()          { return 0x0010; }\n      static unsigned int index()           { return 0x0100; }\n      static unsigned int sorted()          { return 0x0740; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the group list bank element. */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x006d; }\n\n    /** Returns the name of the list. */\n    virtual QString name() const;\n    /** Sets the name of the list. */\n    virtual void setName(const QString &name);\n\n    /** Retruns @c true if the n-th ID is set. */\n    virtual bool validId(unsigned int n);\n    /** Returns the n-th ID. */\n    virtual unsigned int id(unsigned int n);\n    /** Sets the n-th ID. */\n    virtual void setId(unsigned int n, unsigned int id);\n    /** Clears the n-th ID. */\n    virtual void clearId(unsigned int n);\n\n    /** Decodes this group list. */\n    virtual RXGroupList *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links this group list. */\n    virtual bool link(RXGroupList *gl, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes this group list. */\n    virtual bool encode(const RXGroupList *gl, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 11; }\n      /** Maximum number of indices. */\n      static constexpr unsigned int contacts() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int ids() { return 0x000b; }\n      static constexpr unsigned int betweenIds() { return 0x0003; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the group list bank element. */\n  class GroupListBankElement: public Element\n  {\n  public:\n    /** Bitmap for all group lists. */\n    class GroupListBitmapElement : public BitmapElement\n    {\n    public:\n      /** Constructor. */\n      GroupListBitmapElement(uint8_t *ptr);\n    };\n\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Retunrs the bitmap. */\n    virtual GroupListBitmapElement bitmap() const;\n\n    /** Returns the n-th group list element. */\n    virtual GroupListElement groupList(unsigned int n) const;\n\n    /** Decodes all group lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links all group lists. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes all group lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of group lists. */\n      static constexpr unsigned int groupLists() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bitmap()     { return 0x0000; }\n      static constexpr unsigned int groupLists() { return 0x0011; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a DMR radio ID. */\n  class RadioIdElement: public Element\n  {\n  public:\n    /** Contsturctor. */\n    RadioIdElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Returns the DMR Id. */\n    virtual unsigned int id() const;\n    /** Sets the DMR id. */\n    virtual void setId(unsigned int id);\n\n    /** Returns the name of the id. */\n    virtual QString name() const;\n    /** Sets the name of the id. */\n    virtual void setName(const QString &name);\n\n    /** Decodes the radio ID. */\n    virtual DMRRadioID *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the given ID. */\n    virtual bool encode(const DMRRadioID *id, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 12; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int id() { return 0x0000; }\n      static constexpr unsigned int name() { return 0x0003; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the DMR radio ID bank. */\n  class RadioIdBankElement: public Element\n  {\n  public:\n    /** Contsturctor. */\n    RadioIdBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the number of IDs. */\n    virtual unsigned int idCount() const;\n    /** Sets the number of encoded IDs. */\n    virtual void setIdCount(unsigned int n);\n\n    /** Returns the n-th DMR Id. */\n    virtual RadioIdElement id(unsigned int n) const;\n\n    /** Decodes all radio IDs. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes add radio IDs. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of IDs. */\n      static constexpr unsigned int ids() { return 250; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()      { return 0x0000; }\n      static constexpr unsigned int ids()        { return 0x0010; }\n      static constexpr unsigned int betweenIds() { return RadioIdElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implementation of zone. */\n  class ZoneElement: public Element\n  {\n  public:\n    /** Constructor. */\n    ZoneElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0091; }\n\n    /** Returns the name. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Retunrs the number of channels. */\n    virtual unsigned int channelCount() const;\n    /** Sets the number of channels. */\n    virtual void setChannelCount(unsigned int n);\n\n    /** Returns @c true, if the channel index is set. */\n    virtual bool channelIndexValid(unsigned int n) const;\n    /** Returns the n-th channel index. */\n    virtual unsigned int channelIndex(unsigned int n) const;\n    /** Sets the n-th channel index. */\n    virtual void setChannelIndex(unsigned int n, unsigned int idx);\n    /** Clears the n-th channel index. */\n    virtual void clearChannelIndex(unsigned int n);\n\n    /** Decode zone. */\n    virtual Zone *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Link zone. */\n    virtual bool link(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes a zone. */\n    virtual bool encode(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** Maximum number of channels. */\n      static constexpr unsigned int channels()   { return 64; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()         { return 0x0000; }\n      static constexpr unsigned int channelCount() { return 0x0010; }\n      static constexpr unsigned int channels()     { return 0x0011; }\n      /// @endcond\n    };\n  };\n\n\n  class ZoneBankElement: public Element\n  {\n  public:\n    /** Constructor. */\n    ZoneBankElement(uint8_t *ptr);\n\n    /** Returns the size of a zone bank. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the total number of zones. */\n    virtual unsigned int count() const;\n    /** Sets the total number of zones. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns @c true if the channel index VFO A is set. */\n    virtual bool channelIndexAValid() const;\n    /** Returns the channel index VFO A. */\n    virtual unsigned int channelIndexA() const;\n    /** Sets the channel index VFO A. */\n    virtual void setChannelIndexA(unsigned int idx);\n    /** Clears the channel index VFO A. */\n    virtual void clearChannelIndexA();\n\n    /** Returns @c true if the channel index VFO B is set. */\n    virtual bool channelIndexBValid() const;\n    /** Returns the channel index VFO B. */\n    virtual unsigned int channelIndexB() const;\n    /** Sets the channel index VFO B. */\n    virtual void setChannelIndexB(unsigned int idx);\n    /** Clears the channel index VFO B. */\n    virtual void clearChannelIndexB();\n\n    /** Returns @c true if the zone index VFO A is set. */\n    virtual bool zoneIndexAValid() const;\n    /** Returns the zone index VFO A. */\n    virtual unsigned int zoneIndexA() const;\n    /** Sets the zone index VFO A. */\n    virtual void setZoneIndexA(unsigned int idx);\n    /** Clears the zone index VFO A. */\n    virtual void clearZoneIndexA();\n\n    /** Returns @c true if the zone index VFO B is set. */\n    virtual bool zoneIndexBValid() const;\n    /** Returns the zone index VFO B. */\n    virtual unsigned int zoneIndexB() const;\n    /** Sets the zone index VFO B. */\n    virtual void setZoneIndexB(unsigned int idx);\n    /** Clears the zone index VFO B. */\n    virtual void clearZoneIndexB();\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of zones. */\n      static constexpr unsigned int zones()   { return 250; }\n      /** Maximum number of zones per block. */\n      static constexpr unsigned int zonesPerBlock() { return 28; }\n\n    };\n\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()         { return 0x0000; }\n      static constexpr unsigned int channelIndexA() { return 0x0001; }\n      static constexpr unsigned int channelIndexB() { return 0x0003; }\n      static constexpr unsigned int zoneIndexA()    { return 0x0005; }\n      static constexpr unsigned int zoneIndexB()    { return 0x0007; }\n      static constexpr unsigned int zones0()        { return 0x0010; }\n      static constexpr unsigned int betweenBlocks() { return 0x1000; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a scan list. */\n  class ScanListElement: public Element\n  {\n  public:\n    /** Possible transmit modes. */\n    enum class TransmitMode {\n      CurrentChannel=0, ActiveChannel=1, RevertChannel=2\n    };\n\n    /** Possible CTCSS/DCS detection modes. */\n    enum class ToneDetectionMode {\n      None = 0, NonPriority = 1, Priority = 2, All = 3\n    };\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0039; }\n\n    /** Returns the name of the scan list. */\n    virtual QString name() const;\n    /** Sets the name of the scan list. */\n    virtual void setName(const QString &name);\n\n    /** Returns the number of channels. */\n    virtual unsigned int channelCount() const;\n    /** Sets the channel count. */\n    virtual void setChannelCount(unsigned int count);\n\n    /** Returns the transmit mode. */\n    virtual TransmitMode transmitMode() const;\n    /** Sets the transmit mode. */\n    virtual void setTransmitMode(TransmitMode mode);\n\n    /** Returns the tone-detection mode. */\n    virtual ToneDetectionMode toneDetectionMode() const;\n    /** Sets the tone-detection mode. */\n    virtual void setToneDetectionMode(ToneDetectionMode mode);\n\n    /** Returns the scan hang time. */\n    virtual Interval hangTime() const;\n    /** Sets the scan hang time. */\n    virtual void setHangTime(const Interval &dur);\n\n    /** Returns @c true if the secondary priority channel index is set. */\n    virtual bool secondaryChannelIndexValid() const;\n    /** Returns the secondary priority channel index. */\n    virtual unsigned int secondaryChannelIndex() const;\n    /** Sets the secondary priority channel index. */\n    virtual void setSecondaryChannelIndex(unsigned int idx);\n    /** Clears the secondary priority channel index. */\n    virtual void clearSecondaryChannelIndex();\n\n    /** Returns @c true if the primary priority channel index is set. */\n    virtual bool primaryChannelIndexValid() const;\n    /** Returns the primary priority channel index. */\n    virtual unsigned int primaryChannelIndex() const;\n    /** Sets the primary priority channel index. */\n    virtual void setPrimaryChannelIndex(unsigned int idx);\n    /** Clears the primary priority channel index. */\n    virtual void clearPrimaryChannelIndex();\n\n    /** Returns @c true if the revert channel index is set. */\n    virtual bool revertChannelIndexValid() const;\n    /** Returns the revert channel index. */\n    virtual unsigned int revertChannelIndex() const;\n    /** Sets the revert channel index. */\n    virtual void setRevertChannelIndex(unsigned int idx);\n    /** Clears the revert channel index. */\n    virtual void clearRevertChannelIndex();\n\n    /** Returns the priority sweep time. */\n    virtual Interval prioritySweepTime() const;\n    /** Sets the priority sweep time. */\n    virtual void setPrioritySweepTime(const Interval &dur);\n\n    /** Returns @c true, if the n-th channel index refers to the current channel. */\n    virtual bool isCurrentChannel(unsigned int n) const;\n    /** Returns the n-th channel index. */\n    virtual unsigned int channelIndex(unsigned int n) const;\n    /** Sets the n-th channel index. */\n    virtual void setChannelIndex(unsigned int n, unsigned int idx);\n    /** Sets the n-th channel index to the current channel. */\n    virtual void setCurrentChannel(unsigned int n);\n\n    /** Decodes the scan list. */\n    virtual ScanList *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the given scan list. */\n    virtual bool link(ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encode the scan list. */\n    virtual bool encode(const ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 11; }\n      /** Maximum number of channels. */\n      static constexpr unsigned int channels()   { return 15; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()            { return 0x0000; }\n      static constexpr unsigned int channelCount()    { return 0x000b; }\n      static constexpr Bit transmitMode()             { return {0x000c, 4}; }\n      static constexpr Bit toneDetection()            { return {0x000c, 0}; }\n      static constexpr Bit hangTime()                 { return {0x000d, 0}; }\n      static constexpr Bit secondaryChannel()         { return {0x000e, 4}; }\n      static constexpr Bit primaryChannel()           { return {0x000e, 0}; }\n      static constexpr unsigned int revertChannel()   { return 0x000f; }\n      static constexpr Bit prioritySweepTime()        { return {0x0015, 2}; }\n      static constexpr unsigned int channels()        { return 0x0018; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the scan list bank. */\n  class ScanListBankElement: public Element\n  {\n  public:\n    /** Possible scan modes. */\n    enum class ScanMode {\n      Time = 0 , Carrier = 1, Search = 2\n    };\n\n  public:\n    /* Constructor from pointer to the element. */\n    ScanListBankElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the number of scan lists. */\n    virtual unsigned int count() const;\n    /** Sets the number of scan lists. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns the n-th scan list. */\n    virtual ScanListElement scanList(unsigned int n) const;\n\n    /** Returns the scan mode. */\n    virtual ScanMode scanMode() const;\n    /** Sets the scan mode. */\n    virtual void setScanMode(ScanMode mode);\n\n    /** Returns the VHF scan range. */\n    virtual FrequencyRange vhfRange() const;\n    /** Sets the VHF scan range. */\n    virtual void setVHFRange(const FrequencyRange &range);\n\n    /** Returns the UHF scan range. */\n    virtual FrequencyRange uhfRange() const;\n    /** Sets the UHF scan range. */\n    virtual void setUHFRange(const FrequencyRange &range);\n\n    /** Decode all scan lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Link all scan lists. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes all scan lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of scan lists. */\n      static constexpr unsigned int scanLists()   { return 32; }\n    };\n\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()         { return 0x0000; }\n      static constexpr unsigned int scanLists()     { return 0x0001; }\n      static constexpr unsigned int scanMode()      { return 0x0e00; }\n      static constexpr unsigned int vhfLower()      { return 0x0e01; }\n      static constexpr unsigned int vhfUpper()      { return 0x0e03; }\n      static constexpr unsigned int uhfLower()      { return 0x0e05; }\n      static constexpr unsigned int uhfUpper()      { return 0x0e07; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a roaming channel. */\n  class RoamingChannelElement: public Element\n  {\n  public:\n    /** Possible time-slot settings. */\n    enum class TimeSlot {\n      TS1 = 0, TS2 = 1\n    };\n\n  public:\n    /** Constructor from pointer to element. */\n    RoamingChannelElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x001a; }\n\n    /** Returns the name of the channel. */\n    virtual QString name() const;\n    /** Sets the name of the channel. */\n    virtual void setName(const QString &name);\n\n    /** Returns the RX frequency. */\n    virtual Frequency rxFrequency() const;\n    /** Sets the RX frequency. */\n    virtual void setRXFrequency(const Frequency &f);\n\n    /** Returns the TX frequency. */\n    virtual Frequency txFrequency() const;\n    /** Sets the TX frequency. */\n    virtual void setTXFrequency(const Frequency &f);\n\n    /** Return the color code. */\n    virtual unsigned int colorCode() const;\n    /** Sets the color code. */\n    virtual void setColorCode(unsigned int cc);\n\n    /** Returns the time slot. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slots. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Decodes the roaming channel.\n     * @returns nullptr on error. */\n    virtual RoamingChannel *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the roaming channel. */\n    virtual bool encode(const RoamingChannel *ch, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength()  { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()               { return 0x0000; }\n      static constexpr unsigned int rxFrequency()        { return 0x0010; }\n      static constexpr unsigned int txFrequency()        { return 0x0014; }\n      static constexpr unsigned int colorCode()          { return 0x0018; }\n      static constexpr unsigned int timeSlot()           { return 0x0019; }\n      /// @endcond\n    };\n  };\n\n\n  /** The bank of all roaming channels. */\n  class RoamingChannelBankElement: public Element\n  {\n  public:\n    /** Constructor from pointer to element. */\n    RoamingChannelBankElement(uint8_t *ptr);\n\n    /** Returns the size of the elment. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the number of channels. */\n    virtual unsigned int count() const;\n    /** Sets the number of channels. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns the n-th channel. */\n    virtual RoamingChannelElement channel(unsigned int n);\n\n    /** Decides all romaming channels. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes all roaming channels. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of channels. */\n      static constexpr unsigned int channels()        { return 150; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int channels()        { return 0x0000; }\n      static constexpr unsigned int count()           { return 0x0ff0; }\n      static constexpr unsigned int betweenChannels() { return RoamingChannelElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** En/Decodes a roaming zone. */\n  class RoamingZoneElement: public Element\n  {\n  public:\n    /** Constructor from pointer to element. */\n    RoamingZoneElement(uint8_t *ptr);\n\n    /** Returns the size of the elment. */\n    static constexpr unsigned int size() { return 0x0021; }\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name. */\n    virtual void setName(const QString &name);\n\n    /** Returns the channel count. */\n    virtual unsigned int count() const;\n    /** Sets the number of channels. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns @c true if the n-th channel index is set. */\n    virtual bool channelIndexValid(unsigned int n);\n    /** Returns the n-th channel index. */\n    virtual unsigned int channelIndex(unsigned int n);\n    /** Sets the n-th channel index. */\n    virtual void setChannelIndex(unsigned int n, unsigned int idx);\n    /** Clears the n-th channel index. */\n    virtual void clearChannelIndex(unsigned int n);\n\n    /** Decodes the roaming zone. */\n    virtual RoamingZone *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the given roaming zone. */\n    virtual bool link(RoamingZone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the given roaming zone. */\n    virtual bool encode(const RoamingZone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength()  { return 16; }\n      /** Maximum number of channels per zone. */\n      static constexpr unsigned int channels()    { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()               { return 0x0000; }\n      static constexpr unsigned int channelCount()       { return 0x0010; }\n      static constexpr unsigned int channels()           { return 0x0011; }\n      static constexpr unsigned int betweenChannels()    { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** En/decodes the roaming zone bank. */\n  class RoamingZoneBankElement : public Element\n  {\n  public:\n    /** Constructor from pointer to element. */\n    RoamingZoneBankElement(uint8_t *ptr);\n\n    /** Returns the size of the elment. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the zone count. */\n    virtual unsigned int count() const;\n    /** Sets the number of zones. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns @c true if auto roaming is enabled. */\n    virtual bool autoRoamEnabled() const;\n    /** Enables auto roaming. */\n    virtual void enableAutoRoam(bool enable);\n\n    /** Returns the roaming delay. */\n    virtual Interval roamingDelay() const;\n    /** Sets the roaming delay. */\n    virtual void setRoamingDelay(const Interval &delay);\n\n    /** Returns @c true, if a default roaming zone is set. */\n    virtual bool defaultRoamingZoneIndexValid() const;\n    /** Returns the default roaming zone index. */\n    virtual unsigned int defaultRoamingZoneIndex() const;\n    /** Sets the default roaming zone index. */\n    virtual void setDefaultRoamingZoneIndex(unsigned int idx);\n    /** Clears the default roaming zone index. */\n    virtual void clearDefaultRoamingZoneIndex();\n\n    /** Returns the n-th roaming zone. */\n    virtual RoamingZoneElement zone(unsigned int n);\n\n    /** Decodes all roaming zones. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links all roaming zones. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encode all roaming zones. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of zones. */\n      static constexpr unsigned int zones()        { return 64; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()              { return 0x0000; }\n      static constexpr unsigned int autoRoam()           { return 0x0001; }\n      static constexpr unsigned int roamingDelay()       { return 0x0002; }\n      static constexpr unsigned int defaultRoamingZone() { return 0x0003; }\n      static constexpr unsigned int zones()              { return 0x0010; }\n      static constexpr unsigned int betweenZones()       { return RoamingZoneElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** A sinlge preset message. */\n  class SMSTemplateElement: public Element\n  {\n  public:\n    /** Constructor from pointer to element. */\n    SMSTemplateElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0081; }\n\n    /** Returns the message text. */\n    virtual QString message() const;\n    /** Sets the message text. */\n    virtual void setMessage(const QString &msg);\n\n    /** Decodes a single message. */\n    SMSTemplate *decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum message length. */\n      static constexpr unsigned int messageLength()  { return 126; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int length()         { return 0x0000; }\n      static constexpr unsigned int message()        { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Preset message (SMS) bank. */\n  class SMSTemplateBankElement: public Element\n  {\n  public:\n    /** Constructor from pointer. */\n    SMSTemplateBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x1000; }\n\n    /** Returns the number of messages. */\n    virtual unsigned int count() const;\n    /** Sets the number of messages. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns the n-th message. */\n    virtual SMSTemplateElement message(unsigned int n) const;\n\n    /** Decodes all preset SMS messages. */\n    virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum number of messages. */\n      static constexpr unsigned int messages()        { return 20; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()           { return 0x0000; }\n      static constexpr unsigned int messages()        { return 0x0010; }\n      static constexpr unsigned int betweenMessages() { return SMSTemplateElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Common settings block. */\n  class GeneralSettingsElement: public Element\n  {\n  public:\n    /** Possible boot display settings. */\n    enum class BootDisplay {\n      Image=0, Message=1, Voltage=2\n    };\n\n    /** Possible auto-power-off delays. */\n    enum class AutoPowerOffDelay {\n      Off = 0, T30Min=1, T60Min=2, T2h=3, T4h=4, T8h=5\n    };\n\n    /** Possible FM roger tones. */\n    enum class FMRogerTone {\n      Off=0, Beep=1, BDC=2\n    };\n\n    /** Possible settings for the backlight duration. */\n    enum class BacklightDuration {\n      Infinity = 0, T5s=1, T10s=2, T15s=3, T20s=4, T25s=5, T30s=6, T1min=7, T2min=8, T3min=9,\n      T4min=10, T5min=11\n    };\n\n    /** Possible date formats. */\n    enum class DateFormat {\n      YYYYMMDD = 0, DDMMYYYY = 1\n    };\n\n    /** Implements the translation between color names and code.\n     * For now, this struct is futile but becomes necessary, once there is a common color name\n     * scheme. */\n    struct Color {\n      enum class Code {\n        White = 0, Black = 1, Orange = 2, Red = 3, Yellow = 4, Green = 5, Cyan = 6, Blue = 7\n      };\n\n      static unsigned int encode(Code name);\n      static Code decode(unsigned int code);\n    };\n\n    /** Possible position formats. */\n    enum class PositionFormat {\n      DD = 0,   ///< Decimal degree\n      DMS = 1   ///< Degree, minute, second\n    };\n\n    /** GNSS modes. */\n    enum class GNSSMode {\n      GPS = 0, Beidou = 1, Both = 2\n    };\n\n    /** Possible recording modes. */\n    enum class RecordMode {\n      RX = 0, TX = 1, Both = 2\n    };\n\n    /** Possible SMS formats. */\n    enum class SMSFormat {\n      Hytera = 0, Motorola = 1, DMR = 2\n    };\n\n    /** Talker alias formats. */\n    enum class TalkerAliasFormat {\n      ISO8 = 0, UnicodeU16 = 1\n    };\n\n    /** Talker alias sources. */\n    enum class TalkerAliasSource {\n      CallsignDB = 0, OverTheAir = 1\n    };\n\n    /** Possible dual-standby modes. */\n    enum class DualStandbyMode {\n      SingleVFO = 0, DoubleStandby = 1, SingleStandby = 2\n    };\n\n    /** VFO selection. */\n    enum class VFO {\n      A = 0, B = 1\n    };\n\n    /** VFO display modes. */\n    enum class VFODisplayMode {\n      Frequency = 0, ChannelName = 1\n    };\n\n    /** VFO Modes. */\n    enum class VFOMode {\n      Channel = 0, VFO = 1\n    };\n\n    /** Helper encoding/decoding key functions. */\n    struct KeyFunction\n    {\n      enum class Function {\n        None=0, PowerSelect=1, Volt=2, Talkaround=3, DMREncryption=4, VOX=6, ChannelMode=7, Alarm=8,\n        OneTouch1=9, OneTouch2=10, OneTouch3=11,OneTouch4=12, OneTouch5=13, SMS=14, Contacts=15,\n        ZoneUp=16, ZoneDown=17, Scan=18, ToggleRecord=19, PreviousRecord=20, NextRecord=21,\n        FMBCRadio=22, FMBCScan=23, GPSInformation=24, Monitor=25, ToggleMainChannel=26, LoneWorker=27,\n        KeypadLock=28, Mute=29, TBST=30, APRSTX=31, ChannelType=32, DisplayMode=33, CTCSSDSCScan=34,\n        CTCSSDSCSettings=25, SilentTone=36, Roaming=37, SubPTT=38, OneKeyScanFrequency=40,\n        Flashlight=41\n      };\n\n      static unsigned int encode(Function func);\n      static Function decode(unsigned int code);\n    };\n\n    /** Possible power-save mode. */\n    enum class PowerSaveMode {\n      Off=0, Percent50 = 1, Percent66 = 2, Percent75=3\n    };\n\n    /** Possible TBST frequencies. */\n    enum class TBSTFrequency {\n      Hz1000=0, Hz1450=1, Hz1750=2, Hz2100=3\n    };\n\n    /** Possible squelch tail-eliminations. */\n    enum class STEMode {\n      Off = 0, Deg120 = 1, Deg180 = 2, Hz55 = 3\n    };\n\n\n  public:\n    /** Constructor from pointer to element. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size og the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Returns the boot display setting. */\n    virtual BootSettings::BootDisplay bootDisplay() const;\n    /** Sets the boot display. */\n    virtual void setBootDisplay(BootSettings::BootDisplay dis);\n\n    /** Returns the first boot message line. */\n    virtual QString bootMessage1() const;\n    /** Sets the first boot message line. */\n    virtual void setBootMessage1(const QString &msg);\n    /** Returns the second boot message line. */\n    virtual QString bootMessage2() const;\n    /** Sets the second boot message line. */\n    virtual void setBootMessage2(const QString &msg);\n\n    /** Returns @c true if MCU reset is enabled. */\n    virtual bool mcuResetEnabled() const;\n    /** Enables MCU reset. */\n    virtual void enableMCUReset(bool enable);\n\n    /** Returns the auto-power-off delay. */\n    virtual Interval autoPowerOffDelay() const;\n    /** Sets the auto-power-off delay. */\n    virtual void setAutoPowerOffDelay(const Interval &delay);\n\n    /** Returns @c true if all tones are disabled. */\n    virtual bool radioSilentEnabled() const;\n    /** Disables all tones. */\n    virtual void enableRadioSilent(bool enable);\n\n    /** Returns @c true if key tones are enabled. */\n    virtual bool keyToneEnabled() const;\n    /** Enables key tones. */\n    virtual void enableKeyTone(bool enable);\n\n    /** Returns @c true if sms tones are enabled. */\n    virtual bool smsToneEnabled() const;\n    /** Enables sms tones. */\n    virtual void enableSMSTone(bool enable);\n\n    /** Returns @c true if group call tone is enabled. */\n    virtual bool groupCallToneEnabled() const;\n    /** Enables group call tones. */\n    virtual void enableGroupCallTone(bool enable);\n    /** Returns @c true if private call tone is enabled. */\n    virtual bool privateCallToneEnabled() const;\n    /** Enables group call tones. */\n    virtual void enablePrivateCallTone(bool enable);\n\n    /** Returns @c true if EOT tone is enabled. */\n    virtual bool eotToneEnabled() const;\n    /** Enables EOT tones. */\n    virtual void enableEOTTone(bool enable);\n\n    /** Returns @c true if talk permit tone is enabled. */\n    virtual bool talkPermitToneEnabled() const;\n    /** Enables talk permit tones. */\n    virtual void enableTalkPermitTone(bool enable);\n\n    /** Returns @c true if boot tone is enabled. */\n    virtual bool bootToneEnabled() const;\n    /** Enables boot tones. */\n    virtual void enableBootTone(bool enable);\n\n    /** Returns @c true if voice prompt is enabled. */\n    virtual bool voicePromptEnabled() const;\n    /** Enables voice prompt. */\n    virtual void enableVoicePrompt(bool enable);\n\n    /** Returns @c true if low-battery tone is enabled. */\n    virtual bool lowBatteryToneEnabled() const;\n    /** Enables low-battery tones. */\n    virtual void enableLowBatteryTone(bool enable);\n\n    /** Returns the FM roger tone setting. */\n    virtual FMRogerTone fmRogerTone() const;\n    /** Sets the FM roger tone. */\n    virtual void setFMRogerTone(FMRogerTone tone);\n\n    /** Returns the display brightness setting [0-10]. */\n    virtual unsigned int displayBrightness() const;\n    /** Sets the display brightness [0-10]. */\n    virtual void setDisplayBrightness(unsigned int n);\n\n    /** Returns the backlight duration. */\n    virtual Interval backlightDuration() const;\n    /** Sets the backlight duration. */\n    virtual void setBacklightDuration(Interval duration);\n\n    /** Returns the menu hold time. */\n    virtual Interval menuDuration() const;\n    /** Sets the menu hold time. */\n    virtual void setMenuDuration(Interval duration);\n\n    /** Returns @c true if the volume change prompt is shown. */\n    virtual bool showVolmueChange() const;\n    /** Enable volume change prompt. */\n    virtual void enableShowVolumeChange(bool enable);\n\n    /** Returns the date format. */\n    virtual DateFormat dateFormat() const;\n    /** Sets the date format. */\n    virtual void setDateFormat(DateFormat format);\n\n    /** Returns @c true if the clock is shown. */\n    virtual bool showClock() const;\n    /** Enable clock. */\n    virtual void enableShowClock(bool enable);\n\n    /** Returns the call color. */\n    virtual Color::Code callColor() const;\n    /** Sets the call color. */\n    virtual void setCallColor(Color::Code c);\n    /** Returns the standby text color. */\n    virtual Color::Code standbyColor() const;\n    /** Sets the standby text color. */\n    virtual void setStandbyColor(Color::Code c);\n    /** Returns the channel name A color. */\n    virtual Color::Code channelNameAColor() const;\n    /** Sets the channel name A color. */\n    virtual void setChannelNameAColor(Color::Code c);\n    /** Returns the channel name B color. */\n    virtual Color::Code channelNameBColor() const;\n    /** Sets the channel name B color. */\n    virtual void setChannelNameBColor(Color::Code c);\n    /** Returns the zone name A color. */\n    virtual Color::Code zoneNameAColor() const;\n    /** Sets the zone name A color. */\n    virtual void setZoneNameAColor(Color::Code c);\n    /** Returns the zone name B color. */\n    virtual Color::Code zoneNameBColor() const;\n    /** Sets the zone name B color. */\n    virtual void setZoneNameBColor(Color::Code c);\n\n    /** Returns the position display format. */\n    virtual PositionFormat positionFormat() const;\n    /** Sets the position display format. */\n    virtual void setPositionFormat(PositionFormat format);\n\n    /** Returns the GNSS mode. */\n    virtual GNSSSettings::Systems gnss() const;\n    /** Sets the GNSS mode. */\n    virtual void setGNSS(GNSSSettings::Systems mode);\n\n    /** Returns @c true if GNSS is enabled. */\n    virtual bool gnssEnabled() const;\n    /** Enables GNSS. */\n    virtual void enableGNSS(bool enable);\n\n    /** Returns the time zone. */\n    virtual QTimeZone timeZone() const;\n    /** Sets the timezone. */\n    virtual void setTimeZone(const QTimeZone &timeZone);\n\n    /** Returns the position update period. */\n    virtual Interval posUpdatePeriod() const;\n    /** Sets the position update period. */\n    virtual void setPosUpdatePeriod(const Interval &period);\n\n    /** Returns possible recording modes. */\n    virtual RecordMode recordMode() const;\n    /** Sets the record mode. */\n    virtual void setRecordMode(RecordMode mode);\n\n    /** Returns @c true if recording is enabled. */\n    virtual bool recordingEnabled() const;\n    /** Enables/disables recording. */\n    virtual void enableRecording(bool enable);\n\n    /** Returns @c true if group call must match. */\n    virtual bool groupCallMatchEnabled() const;\n    /** Enables group call match. */\n    virtual void enableGroupCallMatch(bool enable);\n\n    /** Returns @c true if private call must match. */\n    virtual bool privateCallMatchEnabled() const;\n    /** Enables private call match. */\n    virtual void enablePrivateCallMatch(bool enable);\n\n    /** Returns the DMR call hang time. */\n    virtual Interval dmrCallHangTime() const;\n    /** Sets the DMR call hang time. */\n    virtual void setDMRCallHangTime(const Interval &hangTime);\n\n    /** Returns the active wait time. */\n    virtual Interval activeWaitTime() const;\n    /** Sets the active wait time. */\n    virtual void setActiveWaitTime(Interval waitTime);\n\n    /** Returns the number of active retries. */\n    virtual unsigned int activeRetries() const;\n    /** Sets the number of active retires. */\n    virtual void setActiveRetries(unsigned int retries);\n\n    /** Retruns the DMR preamble duration. */\n    virtual Interval dmrPreambleDuration() const;\n    /** Sets the DMR preamble duration. */\n    virtual void setDmrPreambleDuration(Interval duration);\n\n    /** Returns @c true if the DMR remote monitor is enabled. */\n    virtual bool dmrRemoteMonitorEnabled() const;\n    /** Enables the remote monitor. */\n    virtual void enableDMRRemoteMonitor(bool enable);\n\n    /** Returns @c true if the remote kill switch is enabled. */\n    virtual bool dmrRemoteKillEnabled() const;\n    /** Enables the remote kill swtich. */\n    virtual void enableDMRRemoteKill(bool enable);\n\n    /** Returns @c true if the remote radio check is enabled. */\n    virtual bool dmrRemoteRadioCheckEnabled() const;\n    /** Enables remote radio check. */\n    virtual void enableDMRRemoteRadioCheck(bool enable);\n\n    /** Returns @c true if the remote reenable is enabled. */\n    virtual bool dmrRemoteReenableEnabled() const;\n    /** Enables DMR remote reenable. */\n    virtual void enableDMRRemoteReenable(bool enable);\n\n    /** Returns @c true reception of DMR alerts is enabled. */\n    virtual bool dmrRXAlertEnabled() const;\n    /** Enables enables reception of DMR alerts. */\n    virtual void enableDMRRXAlert(bool enable);\n\n    /** Returns the DMR SMS format. */\n    virtual SMSExtension::Format smsFormat() const;\n    /** Sets the DMR SMS format. */\n    virtual void setSMSFormat(SMSExtension::Format format);\n\n    /** Retruns @c true missed call notification is enabled. */\n    virtual bool missedCallNotificationEnabled() const;\n    /** Enables missed call notification. */\n    virtual void enableMissedCallNotification(bool enable);\n\n    /** Returns the remote monitor duration. */\n    virtual Interval dmrRemoteMonitorDuration() const;\n    /** Sets the remote monitor duration. */\n    virtual void setDMRRemoteMonitorDuration(Interval duration);\n\n    /** Returns the talker alias format. */\n    virtual DMRSettings::TalkerAliasEncoding talkerAliasEncoding() const;\n    /** Sets the talker alias format. */\n    virtual void setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding format);\n\n    /** Returns @c true, if transmission of talker alias is enabled. */\n    virtual bool txTalkerAliasEnabled() const;\n    /** Enables transmission of talker alias. */\n    virtual void enableTXTalkerAlias(bool enable);\n\n    /** Returns the talker alias source. */\n    virtual TalkerAliasSource talkerAliasSource() const;\n    /** Sets the talker alias source. */\n    virtual void setTalkerAliasSource(TalkerAliasSource source);\n\n    /** Returns the dual-standby mode. */\n    virtual DualStandbyMode dualStandbyMode() const;\n    /** Sets the dual-standby mode. */\n    virtual void setDualStandbyMode(DualStandbyMode mode);\n\n    /** Returns the main VFO. */\n    virtual VFO mainVFO() const;\n    /** Sets the main VFO. */\n    virtual void setMainVFO(VFO mainVFO);\n\n    /** Returns the VFO A display mode. */\n    virtual VFODisplayMode vfoDisplayModeA() const;\n    /** Sets the VFO A display mode. */\n    virtual void setVFODisplayModeA(VFODisplayMode mode);\n    /** Returns the VFO B display mode. */\n    virtual VFODisplayMode vfoDisplayModeB() const;\n    /** Sets the VFO B display mode. */\n    virtual void setVFODisplayModeB(VFODisplayMode mode);\n    /** Returns the VFO A mode. */\n    virtual VFOMode vfoModeA() const;\n\n    /** Sets the VFO A mode. */\n    virtual void setVFOModeA(VFOMode mode);\n    /** Returns the VFO B mode. */\n    virtual VFOMode vfoModeB() const;\n    /** Sets the VFO B mode. */\n    virtual void setVFOModeB(VFOMode mode);\n\n    /** Returns @c true if VFO modes are disabled. */\n    virtual bool vfoModeDisabled() const;\n    /** Disables VFO modes. */\n    virtual void disableVFOMode(bool enable);\n\n    /** Returns the dual-standby hang-time. */\n    virtual Interval dualStandbyHangTime() const;\n    /** Sets the dual-standby hang-time. */\n    virtual void setDualStandbyHangTime(Interval hangTime);\n\n    /** Returns @c true if the side keys are locked. */\n    virtual bool sideKeyLockEnabled() const;\n    /** Enable side key lock. */\n    virtual void enableSideKeyLock(bool enable);\n    /** Returns @c true, if the knowb is locked. */\n    virtual bool knobLockEnabled() const;\n    /** Enables the knob lock. */\n    virtual void enableKnobLock(bool enable);\n    /** Returns the auto key-lock delay. If set to infinity, auto key-lock is disabled. */\n    virtual Interval autoKeyLockDelay() const;\n    /** Sets the auto key-lock delay. If set to infinity, the auto key-lock is disabled. */\n    virtual void setAutoKeyLockDelay(Interval delay);\n\n    /** Returns the side-key 1 short-press function. */\n    virtual KeyFunction::Function sk1Short() const;\n    /** Sets the side-key 1 short-press function. */\n    virtual void setSK1Short(KeyFunction::Function function);\n    /** Returns the side-key 1 long-press function. */\n    virtual KeyFunction::Function sk1Long() const;\n    /** Sets the side-key 1 long-press function. */\n    virtual void setSK1Long(KeyFunction::Function function);\n    /** Returns the side-key 2 short-press function. */\n    virtual KeyFunction::Function sk2Short() const;\n    /** Sets the side-key 2 short-press function. */\n    virtual void setSK2Short(KeyFunction::Function function);\n    /** Returns the side-key 2 long-press function. */\n    virtual KeyFunction::Function sk2Long() const;\n    /** Sets the side-key 2 long-press function. */\n    virtual void setSK2Long(KeyFunction::Function function);\n\n    /** Returns the programmable key 1 short-press function. */\n    virtual KeyFunction::Function p1Short() const;\n    /** Sets the programmable key 1 short-press function. */\n    virtual void setP1Short(KeyFunction::Function function);\n    /** Returns the programmable key 1 long-press function. */\n    virtual KeyFunction::Function p1Long() const;\n    /** Sets the programmable key 1 long-press function. */\n    virtual void setP1Long(KeyFunction::Function function);\n    /** Returns the programmable key 2 short-press function. */\n    virtual KeyFunction::Function p2Short() const;\n    /** Sets the programmable key 2 short-press function. */\n    virtual void setP2Short(KeyFunction::Function function);\n    /** Returns the programmable key 2 long-press function. */\n    virtual KeyFunction::Function p2Long() const;\n    /** Sets the programmable key 2 long-press function. */\n    virtual void setP2Long(KeyFunction::Function function);\n\n    /** Returns the long-press duration. */\n    virtual Interval longPressDuration() const;\n    /** Sets the long-press duration. */\n    virtual void setLongPressDuration(Interval duration);\n\n    /** Returns the transmit timeout. If infinite, the transmit timeout is disabled. */\n    virtual Interval transmitTimeout() const;\n    /** Sets transmit timeout. If set to infinity, the tot is disabled. */\n    virtual void setTransmitTimeout(Interval timeout);\n    /** Returns the transmit timeout reminder. If 0 disabled. */\n    virtual Interval transmitTimeoutReminder() const;\n    /** Sets transmit timeout reminder. If set to 0 disabled. */\n    virtual void setTransmitTimeoutReminder(Interval timeout);\n\n    /** Returns the VOX level. */\n    virtual Level voxLevel() const;\n    /** Sets the VOX level. */\n    virtual void setVOXLevel(Level voxLevel);\n    /** Returns the VOX delay. */\n    virtual Interval voxDelay() const;\n    /** Sets the VOX delay. */\n    virtual void setVoxDelay(Interval delay);\n\n    /** Returns the power-save mode. */\n    virtual PowerSaveMode powerSaveMode() const;\n    /** Sets the power-save mode. */\n    virtual void setPowerSaveMode(PowerSaveMode mode);\n\n    /** Returns @c true, if the NOAA weather alarm is enabled. */\n    virtual bool weatherAlarmEnabled() const;\n    /** Enables the NOAA weather alarm. */\n    virtual void enableWeatherAlarm(bool enable);\n\n    /** Returns @c true if all LEDs are disabled. */\n    virtual bool allLEDsDisabled() const;\n    /** Disables all LEDs. */\n    virtual void disableAllLEDs(bool disable);\n\n    /** Returns the TBST frequency. */\n    virtual Frequency tbstFrequency() const;\n    /** Sets the TBST frequency. */\n    virtual void setTBSTFrequency(const Frequency &frequency);\n\n    /** Returns the squelch tail elimination mode. */\n    virtual STEMode steMode() const;\n    /** Sets the STE mode. */\n    virtual void setSTEMode(STEMode mode);\n\n    /** Returns the FM mic level. */\n    virtual Level fmMicLevel() const;\n    /** Sets the FM mic level.*/\n    virtual void setFMMicLevel(Level level);\n\n    /** Returns the DMR mic level. */\n    virtual Level dmrMicLevel() const;\n    /** Sets the DMR mic level.*/\n    virtual void setDMRMicLevel(Level level);\n\n    /** Decodes the general settings. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encode general settings. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum boot message length */\n      static constexpr unsigned int bootMessageLength()  { return 14; }\n      /** Maximum display brightness. */\n      static constexpr unsigned int maxBrightness()      { return 5; }\n      /** Range of valid active retires. */\n      static constexpr Range<unsigned int> activeRetires() { return {1, 8}; }\n      /** Range of valid long-press durations. */\n      static constexpr Range<Interval> longPressDuration() {\n        return {Interval::fromSeconds(1), Interval::fromSeconds(5)};\n      }\n      /** Range of valid transmit timeouts. */\n      static constexpr Range<Interval> transmitTimeout() {\n        return {Interval::fromSeconds(20), Interval::fromSeconds(500)};\n      }\n      /** Range of valid VOX delays. */\n      static constexpr Range<Interval> voxDelay() {\n        return {Interval::fromMilliseconds(300), Interval::fromSeconds(5)};\n      }\n      /** Valid VOX sensitivity levels. */\n      static constexpr Range<unsigned int> vox() { return {1,5}; }\n      /** Valid mic gain settings. */\n      static constexpr Range<unsigned int> micGain() { return {0,4}; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bootDisplay()        { return 0x0000; }\n      static constexpr unsigned int bootMessage1()       { return 0x0001; }\n      static constexpr unsigned int bootMessage2()       { return 0x000f; }\n      static constexpr unsigned int mcuReset()           { return 0x001d; }\n      static constexpr unsigned int autoPowerOffDelay()  { return 0x001e; }\n      static constexpr Bit radioSilent()                 { return {0x0020, 7}; }\n      static constexpr Bit keyTone()                     { return {0x0020, 6}; }\n      static constexpr Bit smsTone()                     { return {0x0020, 5}; }\n      static constexpr Bit groupCallTone()               { return {0x0020, 4}; }\n      static constexpr Bit privateCallTone()             { return {0x0020, 3}; }\n      static constexpr Bit eotTone()                     { return {0x0020, 2}; }\n      static constexpr Bit talkPermitTone()              { return {0x0020, 1}; }\n      static constexpr Bit bootTone()                    { return {0x0020, 0}; }\n      static constexpr Bit voicePrompt()                 { return {0x0021, 7}; }\n      static constexpr Bit lowBatteryTone()              { return {0x0021, 6}; }\n      static constexpr Bit fmRogerTone()                 { return {0x0021, 1}; }\n      static constexpr unsigned int displayBrightness()  { return 0x0030; }\n      static constexpr unsigned int backlightDuration()  { return 0x0031; }\n      static constexpr unsigned int menuDuration()       { return 0x0032; }\n      static constexpr Bit showVolumeChange()            { return {0x0033, 4}; }\n      static constexpr Bit dateFormat()                  { return {0x0033, 3}; }\n      static constexpr Bit showClock()                   { return {0x0033, 0}; }\n      static constexpr unsigned int callColor()          { return 0x0034; }\n      static constexpr unsigned int standbyColor()       { return 0x0035; }\n      static constexpr unsigned int channelNameAColor()  { return 0x0038; }\n      static constexpr unsigned int channelNameBColor()  { return 0x0039; }\n      static constexpr unsigned int zoneNameAColor()     { return 0x003a; }\n      static constexpr unsigned int zoneNameBColor()     { return 0x003b; }\n      static constexpr Bit positionFormat()              { return {0x0040, 6}; }\n      static constexpr Bit gnssMode()                    { return {0x0040, 3}; }\n      static constexpr Bit enableGNSS()                  { return {0x0040, 0}; }\n      static constexpr unsigned int timeZone()           { return 0x0041; }\n      static constexpr unsigned int posUpdatePeriod()    { return 0x0042; }\n      static constexpr Bit recordMode()                  { return {0x0043, 2}; }\n      static constexpr Bit enableRecording()             { return {0x0045, 0}; }\n      static constexpr Bit groupCallMatch()              { return {0x0060, 1}; }\n      static constexpr Bit privateCallMatch()            { return {0x0060, 0}; }\n      static constexpr unsigned int dmrCallHangTime()    { return 0x0061; }\n      static constexpr unsigned int activeWaitTime()     { return 0x0062; }\n      static constexpr unsigned int activeReties()       { return 0x0063; }\n      static constexpr unsigned int dmrPreambleDur()     { return 0x0064; }\n      static constexpr Bit dmrMonitor()                  { return {0x0065, 7}; }\n      static constexpr Bit dmrKill()                     { return {0x0065, 6}; }\n      static constexpr Bit dmrRadioCheck()               { return {0x0065, 5}; }\n      static constexpr Bit dmrReenable()                 { return {0x0065, 4}; }\n      static constexpr Bit dmrRXAlert()                  { return {0x0065, 3}; }\n      static constexpr Bit smsFormat()                   { return {0x0065, 1}; }\n      static constexpr Bit missedCallNotification()      { return {0x0065, 0}; }\n      static constexpr unsigned int dmrRemoteMonitorDuration() { return 0x0066; }\n      static constexpr Bit dmrTalkerAliasFormat()        { return {0x0067, 4}; }\n      static constexpr Bit txTalkerAlias()               { return {0x0067, 3}; }\n      static constexpr Bit talkerSource()                { return {0x0067, 2}; }\n      static constexpr Bit dualStandbyMode()             { return {0x0080, 6}; }\n      static constexpr Bit mainVFO()                     { return {0x0080, 5}; }\n      static constexpr Bit displayModeB()                { return {0x0080, 4}; }\n      static constexpr Bit displayModeA()                { return {0x0080, 3}; }\n      static constexpr Bit vfoModeB()                    { return {0x0080, 2}; }\n      static constexpr Bit vfoModeA()                    { return {0x0080, 1}; }\n      static constexpr Bit disableVFOMode()              { return {0x0080, 0}; }\n      static constexpr unsigned int dualStandbyHangTime() { return 0x0081;}\n      static constexpr Bit sideKeyLock()                 { return {0x0085, 2}; }\n      static constexpr Bit knobLock()                    { return {0x0085, 1}; }\n      static constexpr Bit enableAutoKeyLock()           { return {0x0085, 0}; }\n      static constexpr unsigned int autoKeyLockDelay()   { return 0x0086; }\n      static constexpr unsigned int sk1Short()           { return 0x0087; }\n      static constexpr unsigned int sk1Long()            { return 0x0088; }\n      static constexpr unsigned int sk2Short()           { return 0x0089; }\n      static constexpr unsigned int sk2Long()            { return 0x008a; }\n      static constexpr unsigned int p1Short()            { return 0x008d; }\n      static constexpr unsigned int p1Long()             { return 0x008e; }\n      static constexpr unsigned int p2Short()            { return 0x008f; }\n      static constexpr unsigned int p2Long()             { return 0x0090; }\n      static constexpr unsigned int longPressDuration()  { return 0x0094; }\n      static constexpr unsigned int transmitTimeout()    { return 0x00a0; }\n      static constexpr unsigned int totReminder()        { return 0x00a1; }\n      static constexpr unsigned int voxLevel()           { return 0x00a2; }\n      static constexpr unsigned int voxDelay()           { return 0x00a3; }\n      static constexpr Bit powerSaveMode()               { return {0x00a4, 4}; }\n      static constexpr Bit weatherAlarm()                { return {0x00a4, 2}; }\n      static constexpr Bit disableLEDs()                 { return {0x00a4, 0}; }\n      static constexpr Bit tbstFrequency()               { return {0x00a5, 4}; }\n      static constexpr Bit steMode()                     { return {0x00a5, 0}; }\n      static constexpr unsigned int fmMicLevel()         { return 0x00a6; }\n      static constexpr unsigned int dmrMicLevel()        { return 0x00a7; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the APRS settings. */\n  class APRSSettingsElement: public Element\n  {\n  protected:\n    /** Encoding of call type. */\n    enum class CallType {\n      Private = 0, Group = 1\n    };\n\n  public:\n    /** Constructor from pointer. */\n    APRSSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Returns the update interval. If set to infinity, update is disabled. */\n    virtual Interval updatePeriod();\n    /** Sets the update interval. */\n    virtual void setUpdatePeriod(Interval interval);\n\n    /** Returns @c true if the fixed location is used. */\n    virtual bool fixedLocationEnabled() const;\n    /** Returns the fixed location. */\n    virtual QGeoCoordinate fixedLocation() const;\n    /** Sets the fixed location. */\n    virtual void setFixedLocation(const QGeoCoordinate &coordinate);\n    /** Enabled fixed location. */\n    virtual void enableFixedLocation(bool enable);\n\n    /** Returns @c true if the n-th revert channel is set. */\n    virtual bool revertChannelIsCurrent(unsigned int n);\n    /** Returns the n-th revert channel index. */\n    virtual unsigned int revertChannelIndex(unsigned int n) const;\n    /** Sets the n-th revert channel index. */\n    virtual void setRevertChannelIndex(unsigned int n, unsigned int idx);\n    /** Sets the n-th revert channel to be the current channel. */\n    virtual void setRevertChannelToCurrent(unsigned int n);\n\n    /** Returns the pre-wave delay. */\n    virtual Interval preWaveDelay() const;\n    /** Sets the pre-wave delay. */\n    virtual void setPreWaveDelay(const Interval &delay);\n\n    /** Returns the call type for all revert channels. */\n    virtual DMRContact::Type callType() const;\n    /** Sets the call type for all revert channels. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns the destination ID */\n    virtual unsigned int destinationId() const;\n    /** Sets the destination ID for all revert channels. */\n    virtual void setDestinationId(unsigned int id);\n\n    /** Decodes the APRS settings. */\n    bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the APRS settings. */\n    bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the APRS settings. */\n    bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: Element::Limit {\n      /** Valid range for update period. */\n      static constexpr Range<Interval> updatePeriod() {\n        return {Interval::fromSeconds(30), Interval::fromMinutes(120) };\n      }\n      /** Number of revert channels. */\n      static constexpr unsigned int revertChannels() { return 8; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int updatePeriod()                { return 0x0001; }\n      static constexpr unsigned int enableFixedLocation()         { return 0x0002; }\n      static constexpr unsigned int fixedLocationLatitude()       { return 0x0006; }\n      static constexpr unsigned int fixedLocationLongitude()      { return 0x0010; }\n      static constexpr unsigned int revertChannelIndices()        { return 0x0020; }\n      static constexpr unsigned int betweenRevertChannelIndices() { return 0x0002; }\n      static constexpr unsigned int prewaveDelay()                { return 0x0030; }\n      static constexpr unsigned int callType()                    { return 0x0031; }\n      static constexpr unsigned int destinationId()               { return 0x0032; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the password settings. */\n  class PasswordSettingsElement: public Element\n  {\n  public:\n    /** Constructor. */\n    PasswordSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n    /** Returns @c true if the boot password is set. */\n    virtual bool bootPasswordEnabled() const;\n    /** Returns the boot password. */\n    virtual QString bootPassword() const;\n    /** Sets the boot password. */\n    virtual void setBootPassword(const QString &value);\n    /** Clears the boot password. */\n    virtual void clearBootPassword();\n\n    /** Returns @c true if the write password is set. */\n    virtual bool writePasswordEnabled() const;\n    /** Returns the write password. */\n    virtual QString writePassword() const;\n    /** Sets the write password. */\n    virtual void setWritePassword(const QString &value);\n    /** Clears the write password. */\n    virtual void clearWritePassword();\n\n    /** Returns @c true if the read password is set. */\n    virtual bool readPasswordEnabled() const;\n    /** Returns the read password. */\n    virtual QString readPassword() const;\n    /** Sets the read password. */\n    virtual void setReadPassword(const QString &value);\n    /** Clears the read password. */\n    virtual void clearReadPassword();\n\n    /** Encode password settings from given config. */\n    virtual bool encode(Context &ctx, ErrorStack err=ErrorStack());\n    /** Decode password settings and update config. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit: Element::Limit\n    {\n      /** Maximum password length. */\n      static constexpr unsigned int passwordLength() { return 8;}\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset\n    {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int enableBootPassword()                { return 0x0030; }\n      static constexpr unsigned int bootPassword()                      { return 0x0031; }\n      static constexpr unsigned int enableWritePassword()               { return 0x0039; }\n      static constexpr unsigned int enableReadPassword()                { return 0x003a; }\n      static constexpr unsigned int writePassword()                     { return 0x003b; }\n      static constexpr unsigned int readPassword()                      { return 0x0043; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the menu settings. */\n  class MenuSettingElement: public Element\n  {\n  public:\n    /** Constructor. */\n    MenuSettingElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0100; }\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit newZone()                      { return {0x0000,1}; }\n      static constexpr Bit listZones()                    { return {0x0000,0}; }\n      static constexpr Bit measurePeriod()                { return {0x0001,5}; }\n      static constexpr Bit remoteKill()                   { return {0x0001,4}; }\n      static constexpr Bit reenableRadio()                { return {0x0001,3}; }\n      static constexpr Bit remoteMonitor()                { return {0x0001,2}; }\n      static constexpr Bit radioCheck()                   { return {0x0001,1}; }\n      static constexpr Bit callAlert()                    { return {0x0001,0}; }\n      static constexpr Bit matchGroupCall()               { return {0x0002,7}; }\n      static constexpr Bit displayMode()                  { return {0x0002,6}; }\n      static constexpr Bit matchPrivateCall()             { return {0x0002,5}; }\n      static constexpr Bit languageSelect()               { return {0x0002,4}; }\n      static constexpr Bit bootDisplay()                  { return {0x0002,3}; }\n      static constexpr Bit transmitPower()                { return {0x0002,2}; }\n      static constexpr Bit alertTone()                    { return {0x0002,1}; }\n      static constexpr Bit talkaround()                   { return {0x0002,0}; }\n      static constexpr Bit record()                       { return {0x0003,6}; }\n      static constexpr Bit aprs()                         { return {0x0003,5}; }\n      static constexpr Bit gnss()                         { return {0x0003,4}; }\n      static constexpr Bit powerSave()                    { return {0x0003,3}; }\n      static constexpr Bit subChannelMode()               { return {0x0003,2}; }\n      static constexpr Bit fmBCRadio()                    { return {0x0003,1}; }\n      static constexpr Bit smsFormat()                    { return {0x0003,0}; }\n      static constexpr Bit callsignDB()                   { return {0x0004,6}; }\n      static constexpr Bit manualDial()                   { return {0x0004,5}; }\n      static constexpr Bit sendMessage()                  { return {0x0004,4}; }\n      static constexpr Bit contactFunc()                  { return {0x0004,3}; }\n      static constexpr Bit editContact()                  { return {0x0004,2}; }\n      static constexpr Bit deleteContact()                { return {0x0004,1}; }\n      static constexpr Bit addContact()                   { return {0x0004,0}; }\n      static constexpr Bit clearCallLog()                 { return {0x0005,3}; }\n      static constexpr Bit outgoingCalls()                { return {0x0005,2}; }\n      static constexpr Bit incomingCalls()                { return {0x0005,1}; }\n      static constexpr Bit missedCalls()                  { return {0x0005,0}; }\n      static constexpr Bit radioName()                    { return {0x0006,7}; }\n      static constexpr Bit radioId()                      { return {0x0006,6}; }\n      static constexpr Bit timeslot()                     { return {0x0006,5}; }\n      static constexpr Bit colorCode()                    { return {0x0006,4}; }\n      static constexpr Bit txContact()                    { return {0x0006,3}; }\n      static constexpr Bit ctcssDcs()                     { return {0x0006,2}; }\n      static constexpr Bit txFrequency()                  { return {0x0006,1}; }\n      static constexpr Bit rxFrequency()                  { return {0x0006,0}; }\n      static constexpr Bit channelName()                  { return {0x0007,4}; }\n      static constexpr Bit addChannel()                   { return {0x0007,3}; }\n      static constexpr Bit groupList()                    { return {0x0007,2}; }\n      static constexpr Bit dcdm()                         { return {0x0007,1}; }\n      static constexpr Bit channelType()                  { return {0x0007,0}; }\n      /// @endcond\n    };\n  };\n\n\n\n  /** Implementation of a single encryption key. */\n  class EncryptionKeyElement: Element\n  {\n  public:\n    /** Possible key types. */\n    enum class Type {\n      Off = 0, Custom = 1, ARC4 = 2, AES128 = 3, AES256 = 4\n    };\n\n  public:\n    /** Constructor. */\n    EncryptionKeyElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x2c; }\n\n    virtual void clear() override;\n\n    /** Returns the key id. */\n    virtual unsigned int keyId() const;\n    /** Sets the key id. */\n    virtual void setKeyId(unsigned int id);\n\n    /** Returns the key name. */\n    virtual QString name() const;\n    /** Sets the name. */\n    virtual void setName(const QString &name);\n\n    /** Returns the key type. */\n    virtual Type type() const;\n    /** Sets the key type. */\n    virtual void setType(Type type);\n\n    /** Returns the key data. */\n    virtual QByteArray key() const;\n    /** Sets the key data. */\n    virtual void setKey(const QByteArray &key);\n\n    /** Decodes the key. */\n    EncryptionKey *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the key. */\n    bool encode(const EncryptionKey *key, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits of the element. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 10; }\n      /** Maximum key length (bytes). */\n      static constexpr unsigned int keyLength() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int keyId() { return 0x0000; }\n      static constexpr unsigned int name()  { return 0x0001; }\n      static constexpr unsigned int type()  { return 0x000b; }\n      static constexpr unsigned int key()   { return 0x000c; }\n      /// @endcond\n    };\n  };\n\n\n\n  /** Implements encryption key bank. */\n  class EncryptionKeyBankElement: public Element\n  {\n  public:\n    /** Constructor from pointer to element. */\n    EncryptionKeyBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x600; }\n\n    /** Returns @c true, if the key is valid. */\n    virtual bool keyValid(unsigned int idx) const;\n    /** Returns the n-th key. */\n    virtual EncryptionKeyElement key(unsigned int idx) const;\n\n    /** Decodes all keys. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes all keys. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit: Element::Limit {\n      /** Maximum number of keys. */\n      static constexpr unsigned int keys() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int keys() { return 0x0000; }\n      static constexpr unsigned int betweenKeys() {\n        return EncryptionKeyElement::size();\n      }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  /** Default/empty constructor.\n   * Before encoding, @c encode will allocate all elements necessary to encode the codeplug.\n   * Before decoding, all elements that are allocated on the device must be allocated within the\n   * codeplug. */\n  explicit DM32UVCodeplug(QObject *parent = nullptr);\n\n  Config * preprocess(Config *config, const ErrorStack &err) const override;\n  bool postprocess(Config *config, const ErrorStack &err) const override;\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const override;\n  bool encode(Config *config, const Flags &flags, const ErrorStack &err=ErrorStack()) override;\n  bool decode(Config *config, const ErrorStack &err=ErrorStack()) override;\n\nprotected:\n  /** Decode codeplug elements. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link decoded elements. */\n  virtual bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode all elements. */\n  virtual bool encodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Decodes all channels defined. */\n  virtual bool decodeChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links all decoded channels. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes all channels. */\n  virtual bool encodeChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Decode contacts. */\n  virtual bool decodeContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Allocate and encode contacts. */\n  virtual bool encodeContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Decodes all zones defined. */\n  virtual bool decodeZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links all decoded zones. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes all zones. */\n  virtual bool encodeZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some internal limits. */\n  struct Limit: Element::Limit {\n    static constexpr unsigned int blockSize() { return 0x1000; }\n    static constexpr Range<unsigned int> channelBanks() { return {1, 49}; }\n    static constexpr Range<unsigned int> contactBanks() { return {1, 5}; }\n    static constexpr Range<unsigned int> zoneBanks()    { return {1, 8}; }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int generalSettings()       { return 0x00004000; }\n    static constexpr unsigned int aprsSettings()          { return 0x00004300; }\n    static constexpr unsigned int passwordSettings()      { return 0x00004400; }\n    static constexpr unsigned int contactIndex()          { return 0x0000b000; }\n    static constexpr unsigned int groupListBank()         { return 0x0000f000; }\n    static constexpr unsigned int extendedSettings()      { return 0x00010000; }\n    static constexpr unsigned int encryptionKeys()        { return 0x00010300; }\n    static constexpr unsigned int scanListBank()          { return 0x00011000; }\n    static constexpr unsigned int channelBanks()          { return 0x00012000; }\n    static constexpr unsigned int channelExtensionBanks() { return 0x00042000; }\n    static constexpr unsigned int contactBanks()          { return 0x00044000; }\n    static constexpr unsigned int zoneBanks()             { return 0x0005c000; }\n    static constexpr unsigned int roamingZoneBank()       { return 0x00065000; }\n    static constexpr unsigned int roamingChannelBank()    { return 0x00066000; }\n    static constexpr unsigned int radioIdBank()           { return 0x00067000; }\n    /// @endcond\n  };\n};\n\n#endif // DM32UV_CODEPLUG_HH\n"
  },
  {
    "path": "lib/dm32uv_interface.cc",
    "content": "#include \"dm32uv_interface.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n#define USB_VID 0x067b\n#define USB_PID 0x23a3\n\n#define TIMEOUT 1000\n\n\n/* ********************************************************************************************* *\n * Device detection request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::DeviceDetectionRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(DeviceDetectionRequest), TIMEOUT, err);\n}\n\n\n/* ********************************************************************************************* *\n * Device detection response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::DeviceDetectionResponse::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  quint64 n = sizeof(DeviceDetectionResponse); char *data = (char *)this;\n  if (! dev->receive(data, 1, TIMEOUT, err)) {\n    errMsg(err) << \"Cannot read response.\";\n    return false;\n  }\n  n -= 1; data += 1;\n\n  if (6 != this->result) {\n    errMsg(err) << \"Invalid response. Expected 6, got \" << Qt::hex << this->result << \".\";\n    return false;\n  }\n\n  return dev->receive(data, n, TIMEOUT, err);\n}\n\nQString\nDM32UVInterface::DeviceDetectionResponse::identifier() const {\n  return QString::fromLatin1(QByteArray(payload, 7));\n}\n\n\n/* ********************************************************************************************* *\n * Password request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::PasswordRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(PasswordRequest), TIMEOUT, err);\n}\n\n/* ********************************************************************************************* *\n * Password response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::PasswordResponse::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  qint64 n = sizeof(PasswordResponse); char *data = (char *)this;\n  if (! dev->receive(data, 1, TIMEOUT, err))\n    return false;\n\n  n -= 1; data += 1;\n  if (0x50 != this->result) {\n    errMsg(err) << \"Invalid response. Expected 50h, got \" << Qt::hex << this->code << \"h.\";\n    return false;\n  }\n\n  return dev->receive(data, n, TIMEOUT, err);\n}\n\n\n/* ********************************************************************************************* *\n * System Info Request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::SysinfoRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(SysinfoRequest), TIMEOUT, err);\n}\n\n/* ********************************************************************************************* *\n * Generic ACK Response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::ACKResponse::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  if (! dev->receive((char *)this, 1, TIMEOUT, err))\n    return false;\n\n  if (0x06 != result) {\n    errMsg(err) << \"Unexpected response \" << Qt::hex << result << \"h, expected 6h.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Value Request\n * ********************************************************************************************* */\nDM32UVInterface::ValueRequest::ValueRequest(ValueId vId)\n  : valueId((uint8_t)vId)\n{\n  // pass...\n}\n\nDM32UVInterface::ValueRequest::ValueRequest(uint8_t vId)\n  : valueId(vId)\n{\n  // pass...\n}\n\nbool\nDM32UVInterface::ValueRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(ValueRequest), TIMEOUT, err);\n}\n\n/* ********************************************************************************************* *\n * Value Response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::ValueResponse::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  char *data = (char *)this;\n  if (! dev->receive(data, 1, TIMEOUT, err))\n    return false;\n\n  if ('V' != this->response_type) {\n    errMsg(err) << \"Unexpected response \" << Qt::hex << this->response_type\n                << \"h, expected \" << Qt::hex << 'V';\n    return false;\n  }\n  data += 1;\n\n  // Receive value ID and length\n  if (! dev->receive(data, 2, TIMEOUT, err))\n    return false;\n  data += 2;\n\n  return dev->receive(data, this->length, TIMEOUT, err);\n}\n\nQByteArray\nDM32UVInterface::ValueResponse::string() const {\n  return {reinterpret_cast<const char *>(payload), length};\n}\n\nuint32_t\nDM32UVInterface::ValueResponse::lowerMemoryBound() const {\n  return qFromLittleEndian(memory.lower);\n}\n\nuint32_t\nDM32UVInterface::ValueResponse::upperMemoryBound() const {\n  return qFromLittleEndian(memory.upper);\n}\n\n\n/* ********************************************************************************************* *\n * Enter Program Mode Request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::EnterProgramModeRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(EnterProgramModeRequest), TIMEOUT, err);\n}\n\n\n/* ********************************************************************************************* *\n * Unknown 02h Request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::Unknown02Request::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(Unknown02Request), TIMEOUT, err);\n}\n\n/* ********************************************************************************************* *\n * Unknown 02h Response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::Unknown02Response::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  return dev->receive((char *)this, sizeof(Unknown02Response), TIMEOUT, err);\n}\n\n\n/* ********************************************************************************************* *\n * Ping Request\n * ********************************************************************************************* */\nbool\nDM32UVInterface::PingRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(PingRequest), TIMEOUT, err);\n}\n\n\n/* ********************************************************************************************* *\n * Read Request\n * ********************************************************************************************* */\nDM32UVInterface::ReadRequest::ReadRequest(uint32_t addr, uint16_t len)\n{\n  address[0] = (addr >>  0) & 0xff;\n  address[1] = (addr >>  8) & 0xff;\n  address[2] = (addr >> 16) & 0xff;\n  length = qToLittleEndian(len);\n}\n\nbool\nDM32UVInterface::ReadRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(ReadRequest), TIMEOUT, err);\n}\n\n/* ********************************************************************************************* *\n * Read Response\n * ********************************************************************************************* */\nbool\nDM32UVInterface::ReadResponse::receive(DM32UVInterface *dev, const ErrorStack &err) {\n  char *data = (char *)this;\n\n  if (! dev->receive(data, 1, TIMEOUT, err))\n    return false;\n  if ('W' != this->response_type) {\n    errMsg(err) << \"Unexpected response \" << Qt::hex << (uint8_t)this->response_type\n                << \"h, expected \" << Qt::hex << (uint8_t)'W' << \"h.\";\n    return false;\n  }\n  data += 1;\n\n  // Receive address (24bit) and length (16bit)\n  if (! dev->receive(data, 5, TIMEOUT, err))\n    return false;\n  data += 5;\n  // Receive payload\n  return dev->receive(data, this->length(), TIMEOUT, err);\n}\n\nuint32_t\nDM32UVInterface::ReadResponse::address() const {\n  return (((uint32_t)_address[0]) << 0)\n         | (((uint32_t)_address[1]) << 8)\n         | (((uint32_t)_address[2]) << 16);\n}\n\nuint16_t\nDM32UVInterface::ReadResponse::length() const {\n  return qFromLittleEndian(_length);\n}\n\nQByteArray\nDM32UVInterface::ReadResponse::payload() const {\n  return QByteArray((const char*)_payload, length());\n}\n\n\n/* ********************************************************************************************* *\n * Write request\n * ********************************************************************************************* */\nDM32UVInterface::WriteRequest::WriteRequest(uint32_t address, const QByteArray &payload) {\n  _address[0] = ((address >>  0) & 0xff);\n  _address[1] = ((address >>  8) & 0xff);\n  _address[2] = ((address >> 16) & 0xff);\n  uint16_t len = std::min((qsizetype)4096, payload.length());\n  _length = qToLittleEndian(len);\n  memcpy(_payload, payload.constData(), len);\n}\n\nbool\nDM32UVInterface::WriteRequest::send(DM32UVInterface *dev, const ErrorStack &err) const {\n  return dev->send((const char *)this, sizeof(WriteRequest), TIMEOUT, err);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of the DM-32UV interface.\n * ********************************************************************************************* */\nDM32UVInterface::DM32UVInterface(const USBDeviceDescriptor &descr,\n                                 const ErrorStack &err, QObject *parent)\n  : USBSerial{descr, QSerialPort::Baud115200, err, parent}, _state(State::Closed), _info()\n{\n  if (isOpen())\n    _state = State::Open;\n  else if (QSerialPort::NoError != QSerialPort::error())\n    _state = State::Error;\n\n  if (! request_identifier(err))\n    _state = State::Error;\n  _state = State::SystemInfo;\n}\n\n\nRadioInfo\nDM32UVInterface::identifier(const ErrorStack &err) {\n  // If not yet requested -> request info\n  if ((State::Open == _state) && (! _info.isValid())) {\n    if (! request_identifier(err))\n      _state = State::Error;\n    _state = State::SystemInfo;\n  }\n  // return it.\n  return _info;\n}\n\n\nbool\nDM32UVInterface::getAddressMap(DM32UV::AddressMap &map, const ErrorStack &err, void (*progress)(unsigned int)) {\n  // If not yet in program mode -> enter\n  if ((State::Program != _state) && (! enter_program_mode(err))) {\n    errMsg(err) << \"Cannot enter program mode.\";\n    _state = State::Error;\n    return false;\n  }\n  _state = State::Program;\n  logDebug() << \"Enter PROG mode.\";\n\n  // Map entire codeplug memory region\n  for (uint32_t addr=_codeplugMemory.first; addr<_codeplugMemory.second; addr += 0x1000) {\n    QThread::msleep(100);\n    ReadRequest mapReq(addr+0xfff, 1);\n    ReadResponse mapRes;\n    if (! sendReceive(mapReq, mapRes, err)) {\n      errMsg(err) << \"Cannot request codeplug memory map at address \"\n                  << Qt::hex << (addr+0xfff) << \"h.\";\n      return false;\n    }\n    // Unpack prefix\n    uint8_t prefix = (uint8_t)mapRes.payload()[0];\n    // If prefix is invalid -> do not map\n    if ((0x00 == prefix) || (0xff == prefix))\n      continue;\n    // add to address map\n    uint32_t vaddr = ((uint32_t)prefix) << 12;\n    //logDebug() << \"Map \" << Qt::hex << addr << \"h -> \" << vaddr << \"h.\";\n    map.map(addr, vaddr);\n    // Signal progress\n    if (progress) progress((100*addr)/_codeplugMemory.second);\n  }\n\n  return true;\n}\n\n\n\nbool\nDM32UVInterface::read_start(uint32_t bank, uint32_t address, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(address);\n\n  // If not yet in program mode -> enter\n  if ((State::Program != _state) && (! enter_program_mode(err))) {\n    errMsg(err) << \"Cannot enter program mode.\";\n    _state = State::Error;\n    return false;\n  }\n\n  _state = State::Program;\n  return true;\n}\n\n\nbool\nDM32UVInterface::read(uint32_t bank, uint32_t address, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  // align read with 1000h blocks\n  if (address & 0xfff) {\n    int n = std::min(nbytes, (int)(0x1000 - (address & 0xfff)));\n    ReadRequest req(address, n);\n    ReadResponse res;\n    if (! sendReceive(req, res, err)) {\n      errMsg(err) << \"Cannot read from \" << Qt::hex << address << \"h \" << n << \" bytes.\";\n      return false;\n    }\n    std::memcpy(data, res.payload().constData(), n);\n    data += n;\n    address += n;\n    nbytes -= n;\n  }\n\n  while (nbytes > 0) {\n    int n = std::min(nbytes, 0x1000);\n    ReadRequest req(address, n);\n    ReadResponse res;\n    if (! sendReceive(req, res, err)) {\n      errMsg(err) << \"Cannot read from \" << Qt::hex << address << \"h \" << n << \" bytes.\";\n      return false;\n    }\n    std::memcpy(data, res.payload().constData(), n);\n    data += n;\n    address += n;\n    nbytes -= n;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVInterface::read_finish(const ErrorStack &err) {\n  Q_UNUSED(err)\n  // Nothing to do.\n  return true;\n}\n\n\nbool\nDM32UVInterface::write_start(uint32_t bank, uint32_t address, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(address);\n\n  // If not yet in program mode -> enter\n  if ((State::Program != _state) && (! enter_program_mode(err))) {\n    errMsg(err) << \"Cannot enter program mode.\";\n    _state = State::Error;\n    return false;\n  }\n\n  _state = State::Program;\n  return true;\n}\n\n\nbool\nDM32UVInterface::write(uint32_t bank, uint32_t address, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  // align with 1000h blocks\n  if (address & 0xfff) {\n    int n = std::min(nbytes, (int)(0x1000 - (address & 0xfff)));\n    WriteRequest req(address, QByteArray((const char*)data, n));\n    ACKResponse res;\n    if (! sendReceive(req, res, err)) {\n      errMsg(err) << \"Cannot write to \" << Qt::hex << address << \"h \" << n << \" bytes.\";\n      return false;\n    }\n    data += n;\n    address += n;\n    nbytes -= n;\n  }\n\n  while (nbytes > 0) {\n    int n = std::min(nbytes, 0x1000);\n    WriteRequest req(address, QByteArray((const char*)data, n));\n    ACKResponse res;\n    if (! sendReceive(req, res, err)) {\n      errMsg(err) << \"Cannot write to \" << Qt::hex << address << \"h \" << n << \" bytes.\";\n      return false;\n    }\n    data += n;\n    address += n;\n    nbytes -= n;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVInterface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err)\n  // Nothing to do.\n  return true;\n}\n\n\nUSBDeviceInfo\nDM32UVInterface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, 0, 0, false);\n}\n\nQList<USBDeviceDescriptor>\nDM32UVInterface::detect(bool saveOnly) {\n  if (saveOnly)\n    return QList<USBDeviceDescriptor>();\n  return USBSerial::detect();\n}\n\n\nbool\nDM32UVInterface::DM32UVInterface::request_identifier(const ErrorStack &err) {\n  if (State::Open != _state) {\n    errMsg(err) << \"Cannot identify radio, interface not it OPEN state.\";\n    return false;\n  }\n\n  QThread::msleep(100);\n  DeviceDetectionRequest devDetReq;\n  DeviceDetectionResponse devDetRes;\n  if (! sendReceive(devDetReq, devDetRes, err)) {\n    errMsg(err) << \"Cannot identify device.\";\n    return false;\n  }\n\n  logDebug() << \"Got \" << devDetRes.identifier() << \".\";\n  if (\"DP570UV\" != devDetRes.identifier()) {\n    errMsg(err) << \"Unknown radio \" << devDetRes.identifier();\n    return false;\n  }\n\n  QThread::msleep(100);\n  // Formally, request password\n  PasswordRequest passReq;\n  PasswordResponse passRes;\n  if (! sendReceive(passReq, passRes, err)) {\n    errMsg(err) << \"Cannot request programming password.\";\n    return false;\n  }\n  logDebug() << \"Finished password request.\";\n\n  QThread::msleep(100);\n  // Enter System information mode\n  SysinfoRequest sysInfoReq;\n  ACKResponse sysInfoRes;\n  if (! sendReceive(sysInfoReq, sysInfoRes, err)) {\n    errMsg(err) << \"Cannot enter system info mode.\";\n    return false;\n  }\n  logDebug() << \"Finished SysInfo request.\";\n\n  QThread::msleep(100);\n  // Request FW version\n  ValueRequest fwVersionReq(ValueRequest::ValueId::FirmwareVersion);\n  ValueResponse fwVersionRes;\n  if (! sendReceive(fwVersionReq, fwVersionRes, err)) {\n    errMsg(err) << \"Cannot request firmware version.\";\n    return false;\n  }\n  _firmwareVersion = fwVersionRes.string();\n  logDebug() << \"Got firmware version \" << _firmwareVersion << \".\";\n\n  QThread::msleep(100);\n  // Request codeplug memory region\n  ValueRequest cpMemReq(ValueRequest::ValueId::MainConfigMemory);\n  ValueResponse cpMemRes;\n  if (! sendReceive(cpMemReq, cpMemRes, err)) {\n    errMsg(err) << \"Cannot request codeplug memory region.\";\n    return false;\n  }\n  _codeplugMemory = {cpMemRes.lowerMemoryBound(), cpMemRes.upperMemoryBound()};\n  logDebug() << \"Codeplug memory region: \" << Qt::hex << _codeplugMemory.first\n             << \"h - \" << Qt::hex << _codeplugMemory.second << \"h.\";\n\n  QThread::msleep(100);\n  // Request callsign memory region\n  ValueRequest callMemReq(ValueRequest::ValueId::CallSignDBMemory);\n  ValueResponse callMemRes;\n  if (! sendReceive(callMemReq, callMemRes, err)) {\n    errMsg(err) << \"Cannot request callsign memory region.\";\n    return false;\n  }\n  _callsignMemory = {callMemRes.lowerMemoryBound(), callMemRes.upperMemoryBound()};\n  logDebug() << \"Call-sign DB memory region: \" << Qt::hex << _callsignMemory.first\n             << \"h - \" << Qt::hex << _callsignMemory.second << \"h.\";\n\n  // Done\n  _info = RadioInfo::byID(RadioInfo::Radio::DM32UV);\n\n  return true;\n}\n\n\nbool\nDM32UVInterface::enter_program_mode(const ErrorStack &err) {\n  if (State::SystemInfo != _state) {\n    errMsg(err) << \"Cannot enter PROGRAM mode. Interface not in SYSINFO mode.\";\n    return false;\n  }\n\n  if (_firmwareVersion.isEmpty() && !DM32UV::supportedFirmwareVersions().contains(_firmwareVersion)) {\n    logWarn() << \"Firmware version \" << _firmwareVersion << \" is currently not supported by qdmr. \"\n              << \"Supported versions: \" << DM32UV::supportedFirmwareVersions().values().join(\", \") << \".\";\n  }\n\n  EnterProgramModeRequest progReq;\n  ACKResponse progRes;\n  if (! sendReceive(progReq, progRes, err)) {\n    errMsg(err) << \"Cannot send enter-program-mode request.\";\n    return false;\n  }\n\n  Unknown02Request uknReq;\n  Unknown02Response uknRes;\n  if (! sendReceive(uknReq, uknRes, err)) {\n    errMsg(err) << \"Cannot send unknown 02h request.\";\n    return false;\n  }\n\n  PingRequest pingReq;\n  ACKResponse pingRes;\n  if (! sendReceive(pingReq, pingRes)) {\n    errMsg(err) << \"Cannot send ping request.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVInterface::send(const char *data, qint64 n, int timeout, const ErrorStack &err) {\n  Q_UNUSED(timeout)\n\n  //logDebug() << \"Send \" << QByteArray(data, n).toHex(' ') << \".\";\n  while (n) {\n    auto k = QSerialPort::write(data, n);\n    if (0 > k) {\n      errMsg(err) << \"QSerialPort: \" << errorString();\n      errMsg(err) << \"Cannot send device detection request.\";\n      return false;\n    }\n    n -= k; data += k;\n  }\n\n  return true;\n}\n\n\nbool\nDM32UVInterface::receive(char *data, qint64 n, int timeout, const ErrorStack &err) {\n  if (bytesAvailable()) {\n    auto k = QSerialPort::read(data, n);\n    if (0 > k) {\n      errMsg(err) << \"Cannot read from serial port: \" << errorString() << \".\";\n      return false;\n    }\n    n -= k; data += k;\n  }\n\n  while (n) {\n    if (! waitForReadyRead(timeout)) {\n      errMsg(err) << \"QSerialPort: \" << errorString() << \".\";\n      errMsg(err) << \"Cannot read from serial port, timeout.\";\n      return false;\n    }\n    auto k = QSerialPort::read(data, n);\n    if (0 > k) {\n      errMsg(err) << \"Cannot read from serial port: \" << errorString() << \".\";\n      return false;\n    }\n    n -= k; data += k;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dm32uv_interface.hh",
    "content": "#ifndef DM32UVINTERFACE_HH\n#define DM32UVINTERFACE_HH\n\n#include \"usbserial.hh\"\n#include <QObject>\n#include \"dm32uv.hh\"\n\n\n/** Interface to Baofeng Dm-32UV devices.\n *\n * The protocol is documented in detail at\n * https://github.com/infamy/DM32-Protocol-Spec/blob/main/02-CONNECTION-SEQUENCE.md\n *\n * @ingroup dm32uv */\nclass DM32UVInterface : public USBSerial\n{\n  Q_OBJECT\n\nprotected:\n  /** Device detection request. */\n  struct Q_PACKED DeviceDetectionRequest {\n    /** Request content. */\n    const char payload[7] = {'P','S','E','A','R','C','H'};\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n  /** Device detection response. */\n  struct Q_PACKED DeviceDetectionResponse {\n    /** Result flag. */\n    uint8_t result;\n    /** Response payload. */\n    char payload[7];\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n    /** Extracts the identifier from the response payload. */\n    QString identifier() const;\n  };\n\n\n  /** Password request. */\n  struct Q_PACKED PasswordRequest {\n    /** Request content. */\n    const char payload[7] = {'P', 'A', 'S', 'S', 'S', 'T', 'A'};\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n  /** Passwort response. */\n  struct Q_PACKED PasswordResponse {\n    /** Response result code. */\n    uint8_t result;\n    /** Some unknown data. */\n    uint16_t code;\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n  };\n\n\n  /** System info request. */\n  struct Q_PACKED SysinfoRequest {\n    /** Request content. */\n    const char payload[7] = {'S','Y','S','I','N','F','O'};\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n  /** System info response. */\n  struct Q_PACKED ACKResponse {\n    /** Response result code. */\n    uint8_t result;\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n  };\n\n\n  /** Value request. These requests are used to read some global information like address ranges\n   * and versions. */\n  struct Q_PACKED ValueRequest {\n    /** Some possible value IDs. */\n    enum class ValueId{\n      FirmwareVersion = 0x1, BuildDate = 0x3, MainConfigMemory = 0xa, CallSignDBMemory = 0xf\n    };\n    /** Static request type. */\n    const char request_type = 'V';\n    /** Some unknown fields. Likely some flags and length field. */\n    const uint8_t unused[3] = {0x00, 0x00, 0x00};\n    /** The value ID to read. */\n    uint8_t valueId;\n\n    /** Constructor from value ID. */\n    ValueRequest(ValueId valueId);\n    /** Constructor from raw value ID. */\n    explicit ValueRequest(uint8_t valueId);\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n  /** Value response. */\n  struct Q_PACKED ValueResponse {\n    /** The received response code, should be 'V'. */\n    char response_type;\n    /** The value ID read. */\n    uint8_t valueId;\n    /** THe length of the payload. The format depends on the value being read. */\n    uint8_t length;\n    union {\n      /** A memory range. */\n      struct {\n        /** Lower bound in little endian. */\n        uint32_t lower;\n        /** Upper bound (inclusive) in little endian. */\n        uint32_t upper;\n      } memory;\n      // Raw payload.\n      uint8_t payload[255];\n    };\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n    /** Extracts a string from the response payload. */\n    QByteArray string() const;\n    /** Extracts the lower memory bound. */\n    uint32_t lowerMemoryBound() const;\n    /** Extracts the upper memory bound. */\n    uint32_t upperMemoryBound() const;\n  };\n\n\n  /** Enter program mode request. */\n  struct Q_PACKED EnterProgramModeRequest {\n    /** Request content. */\n    const char payload[12] = {\n      '\\xff', '\\xff', '\\xff', '\\xff', '\\x0c',\n      'P','R','O','G','R','A','M'\n    };\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n\n  /** Unkown 02h request */\n  struct Q_PACKED Unknown02Request {\n    /** Request content. */\n    const char payload[1] {'\\x02'};\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n   };\n\n  /** Unknown 02h response. */\n  struct Q_PACKED Unknown02Response {\n    /** Some unknown response payload. */\n    char payload[8];\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n  };\n\n\n  /** Ping request. */\n  struct Q_PACKED PingRequest {\n    /** Request content. */\n    const char payload[1] = {'\\x06'};\n\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n\n  /** Read request. */\n  struct Q_PACKED ReadRequest {\n    /** Static request type. */\n    const char request_type = 'R';\n    /** 24bit little endian address. */\n    uint8_t address[3];\n    /** 16bit little endian length. */\n    uint16_t length;\n\n    /** Constructs a read request for the given address and length. */\n    ReadRequest(uint32_t address, uint16_t length);\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n  /** Read response. */\n  struct Q_PACKED ReadResponse {\n    /** Static respone type field. */\n    const char response_type = 'W';\n    /** 24bit little endian address. */\n    uint8_t _address[3];\n    /** 16bit payload length. */\n    uint16_t _length;\n    /** Payload of upto 4096 bytes. */\n    uint8_t _payload[4096];\n\n    /** Unpacks the address. */\n    uint32_t address() const;\n    /** Unpacks the length. */\n    uint16_t length() const;\n    /** Returns the payload as byte array. */\n    QByteArray payload() const;\n\n    /** Receive response though the given interface. */\n    bool receive(DM32UVInterface *dev, const ErrorStack &err=ErrorStack());\n  };\n\n\n  /** Write request. */\n  struct Q_PACKED WriteRequest {\n    /** Static request type field. */\n    const char request_type = 'W';\n    /** Encodes the 24bit address in little endian. */\n    uint8_t _address[3];\n    /** Encodes the 16bit length in little endian. */\n    uint16_t _length;\n    /** Holds the payload. */\n    uint8_t _payload[4096];\n\n    /** Constructs a new write request to the given address with the given payload. */\n    WriteRequest(uint32_t address, const QByteArray &payload);\n    /** Send request though the given interface. */\n    bool send(DM32UVInterface *dev, const ErrorStack &err=ErrorStack()) const;\n  };\n\n\nprotected:\n  /** Possible states, the interface might be in. */\n  enum class State {\n    Closed, Open, Connected, SystemInfo, Program, Error\n  };\n\npublic:\n  /** Constructs a new DM32UV interface for the given USB descriptor. The constructor also enters\n   * the SYSINFO state and identifies the radio. */\n  explicit DM32UVInterface(const USBDeviceDescriptor &descr,\n                           const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  /** Returns the radio info, after identifying the radio. */\n  RadioInfo identifier(const ErrorStack &err=ErrorStack()) override;\n\n  /** Reads the obfuscation address map from the device. */\n  bool getAddressMap(DM32UV::AddressMap &map, const ErrorStack &err=ErrorStack(),\n                     void (*progress)(unsigned int percent)=nullptr);\n\n  bool read_start(uint32_t bank, uint32_t address, const ErrorStack &err=ErrorStack()) override;\n  bool read(uint32_t bank, uint32_t address, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) override;\n  bool read_finish(const ErrorStack &err=ErrorStack()) override;\n\n  bool write_start(uint32_t bank, uint32_t address, const ErrorStack &err=ErrorStack()) override;\n  bool write(uint32_t bank, uint32_t address, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) override;\n  bool write_finish(const ErrorStack &err=ErrorStack()) override;\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /*** Identifies the radio. */\n  bool request_identifier(const ErrorStack &err = ErrorStack());\n  /** Enters program mode. */\n  bool enter_program_mode(const ErrorStack &err = ErrorStack());\n  /** Helper function to send a request and receives the associated response. */\n  template<class Request, class Response>\n  bool sendReceive(const Request &req, Response &res, const ErrorStack &err=ErrorStack()) {\n    if (! req.send(this, err))\n      return false;\n    return res.receive(this, err);\n  }\n  /** Send some data. */\n  bool send(const char *data, qint64 n, int timeout, const ErrorStack &err=ErrorStack());\n  /** Receives some data. */\n  bool receive(char *data, qint64 n, int timeout, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Current state, the interface is in. */\n  State _state;\n  /** Radio info, identifying the radio. */\n  RadioInfo _info;\n  /** Firmware version string. */\n  QByteArray _firmwareVersion;\n  /** Codeplug memory range. */\n  QPair<uint32_t, uint32_t> _codeplugMemory;\n  /** Callsign memory range. */\n  QPair<uint32_t, uint32_t> _callsignMemory;\n};\n\n#endif // DM32UVINTERFACE_HH\n"
  },
  {
    "path": "lib/dm32uv_limits.cc",
    "content": "#include \"dm32uv_limits.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"scanlist.hh\"\n#include \"zone.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include \"dm32uv_codeplug.hh\"\n\nDM32UVLimits::DM32UVLimits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 0;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(\n          -1, DM32UVCodeplug::GeneralSettingsElement::Limit::bootMessageLength(), RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(\n          -1, DM32UVCodeplug::GeneralSettingsElement::Limit::bootMessageLength(), RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 5}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n            {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n            {\"password\", new RadioLimitPin(DM32UVCodeplug::PasswordSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(\n             1, DM32UVCodeplug::RadioIdElement::Limit::nameLength(), RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, DM32UVCodeplug::ContactBankElement::Limit::contacts(),\n          new RadioLimitObject {\n            { \"name\", new RadioLimitString(\n              1, DM32UVCodeplug::ContactElement::Limit::nameLength(), RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, DM32UVCodeplug::GroupListBankElement::Limit::groupLists(),\n        new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, -1, RadioLimitString::ASCII) }, // Name is not encoded.\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, DM32UVCodeplug::ChannelBankElement::Limit::channels(),\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(\n               1, DM32UVCodeplug::ChannelElement::Limit::nameLength(),\n               RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(\n               1, DM32UVCodeplug::ChannelElement::Limit::nameLength(),\n               RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, true)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { AMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(\n                1, DM32UVCodeplug::ChannelElement::Limit::nameLength(), RadioLimitString::ASCII) },\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(118.), Frequency::fromMHz(137.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(118.), Frequency::fromMHz(137.)}})},\n              {\"power\", new RadioLimitEnum {unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, DM32UVCodeplug::ZoneBankElement::Limit::zones(), new RadioLimitObject {\n          { \"name\", new RadioLimitString(\n            1, DM32UVCodeplug::ZoneElement::Limit::nameLength(), RadioLimitString::Unicode) },\n          { \"A\", new RadioLimitRefList(\n            0, DM32UVCodeplug::ZoneElement::Limit::channels(), Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 0, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, DM32UVCodeplug::ScanListBankElement::Limit::scanLists(),\n        new RadioLimitObject{\n          { \"name\", new RadioLimitString(\n            1, DM32UVCodeplug::ScanListElement::Limit::nameLength(), RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(\n            0, DM32UVCodeplug::ScanListBankElement::Limit::scanLists(), Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems. */\n  add(\"positioning\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/dm32uv_limits.hh",
    "content": "#ifndef DM32UVLIMITS_HH\n#define DM32UVLIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the configuration limits for the BTECH DR-1801UV.\n * @ingroup dm32uv */\nclass DM32UVLimits : public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DM32UVLimits(QObject *parent=nullptr);\n};\n\n#endif // DM32UV\n"
  },
  {
    "path": "lib/dmr6x2uv.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"userdatabase.hh\"\n#include \"dmr6x2uv.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"dmr6x2uv_limits.hh\"\n#include \"d868uv_callsigndb.hh\"\n#include \"logger.hh\"\n\nDMR6X2UV::DMR6X2UV(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"BTECH DMR-6X2UV\", device, parent), _limits(nullptr)\n{\n  _codeplug = new DMR6X2UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D868UVCallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x01:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Do not check frequency range.\";\n    _limits = new DMR6X2UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nDMR6X2UV::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nDMR6X2UV::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::DMR6X2UV, \"dmr6x2uv\", \"DMR-6X2UV\", \"BTECH\", {AnytoneGD32Interface::interfaceInfo()});\n}\n\n"
  },
  {
    "path": "lib/dmr6x2uv.hh",
    "content": "/** @defgroup dmr6x2uv BTECH DMR-6X2UV\n * Device specific classes for BTECH DMR-6X2UV.\n *\n * Although labeled BTECH (Baofeng USA), this device is basically a relabeled AnyTone AT-D868UV.\n * However, there are some minor differences in the codeplug format, hence it needs a separate\n * implementation.\n *\n * \\image html dmr6x2uv.jpg \"DMR-6X2UV\" width=200px\n * \\image latex dmr6x2uv.jpg \"DMR-6X2UV\" width=200px\n *\n * @ingroup anytone */\n\n#ifndef DMR6X2UV_HH\n#define DMR6X2UV_HH\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n\n/** Represents a BTECH DMR-6X2UV.\n * @ingroup dmr6x2uv */\nclass DMR6X2UV: public AnytoneRadio\n{\n  Q_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit DMR6X2UV(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  RadioLimits *_limits;\n};\n\n\n#endif // DMR6X2UV_HH\n"
  },
  {
    "path": "lib/dmr6x2uv2.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"userdatabase.hh\"\n#include \"dmr6x2uv2.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"dmr6x2uv_limits.hh\"\n#include \"d878uv2_callsigndb.hh\"\n#include \"logger.hh\"\n\nDMR6X2UV2::DMR6X2UV2(AnytoneInterface *device, QObject *parent)\n  : AnytoneRadio(\"BTECH DMR-6X2UV PRO\", device, parent), _limits(nullptr)\n{\n  _codeplug = new DMR6X2UVCodeplug(this);\n  _codeplug->clear();\n  _callsigns = new D878UV2CallsignDB(this);\n\n  // Get device info and determine supported TX frequency bands\n  AnytoneInterface::RadioVariant info;\n  if (_dev)\n    _dev->getInfo(info);\n\n  switch (info.bands) {\n  case 0x00:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x01:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(420.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x02:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x03:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x04:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x05:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(440.), Frequency::fromMHz(480.)} }, info.version, this);\n    break;\n  case 0x06:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x07:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(446.), Frequency::fromMHz(447.)} }, info.version, this);\n    break;\n  case 0x08:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  case 0x09:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(432.)} }, info.version, this);\n    break;\n  case 0x0a:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(148.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(450.)} }, info.version, this);\n    break;\n  case 0x0b:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(400.), Frequency::fromMHz(480.)} },\n                                 { {Frequency::fromMHz(144.), Frequency::fromMHz(146.)},\n                                   {Frequency::fromMHz(430.), Frequency::fromMHz(440.)} }, info.version, this);\n    break;\n  case 0x0c:\n    _limits = new DMR6X2UVLimits({ {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} },\n                                 { {Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                   {Frequency::fromMHz(403.), Frequency::fromMHz(470.)} }, info.version, this);\n    break;\n  default:\n    logInfo() << \"Unknown band-code\" << QString::number(int(info.bands), 16)\n              << \": Do not check frequency range.\";\n    _limits = new DMR6X2UVLimits({}, {}, info.version, this);\n    break;\n  }\n}\n\nconst RadioLimits &\nDMR6X2UV2::limits() const {\n  return *_limits;\n}\n\nRadioInfo\nDMR6X2UV2::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::DMR6X2UV2, \"dmr6x2uv2\", \"DMR-6X2UV PRO\", \"BTECH\", {AnytoneGD32Interface::interfaceInfo()});\n}\n\n"
  },
  {
    "path": "lib/dmr6x2uv2.hh",
    "content": "/** @defgroup dmr6x2uv2 BTECH DMR-6X2UV PRO\n * Device specific classes for BTECH DMR-6X2UV PRO.\n *\n * This is basically the same device as the DMR6X2 but with larger memory (larger calls-sign db),\n * FM APRS RX, Bluetooth and GPS roaming.\n *\n * \\image html dmr6x2uv.jpg \"DMR-6X2UV\" width=200px\n * \\image latex dmr6x2uv.jpg \"DMR-6X2UV\" width=200px\n *\n * @ingroup anytone */\n\n#ifndef DMR6X2UV2_HH\n#define DMR6X2UV2_HH\n\n#include \"anytone_radio.hh\"\n#include \"anytone_interface.hh\"\n\n/** Represents a BTECH DMR-6X2UV PRO.\n * @ingroup dmr6x2uv2 */\nclass DMR6X2UV2: public AnytoneRadio\n{\n  Q_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit DMR6X2UV2(AnytoneInterface *device=nullptr, QObject *parent=nullptr);\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  RadioLimits *_limits;\n};\n\n\n#endif // DMR6X2UV_HH\n"
  },
  {
    "path": "lib/dmr6x2uv2_codeplug.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"roamingchannel.hh\"\n#include \"config.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"dmr6x2uv2_codeplug.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug::GeneralSettingsElement::KeyFunction\n * ********************************************************************************************* */\nuint8_t\nDMR6X2UV2Codeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;\n  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;\n  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;\n  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;\n  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;\n  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;\n  case AnytoneKeySettingsExtension::KeyFunction::VOX:               return (uint8_t)KeyFunction::VOX;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;\n  case AnytoneKeySettingsExtension::KeyFunction::SubPTT:            return (uint8_t)KeyFunction::SubPTT;\n  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;\n  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;\n  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;\n  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;\n  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;\n  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;\n  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;\n  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;\n  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;\n  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;\n  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;\n  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;\n  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;\n  case AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater:   return (uint8_t)KeyFunction::SimplexRepeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;\n  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;\n  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;\n  case AnytoneKeySettingsExtension::KeyFunction::Squelch:           return (uint8_t)KeyFunction::Squelch;\n  case AnytoneKeySettingsExtension::KeyFunction::Roaming:           return (uint8_t)KeyFunction::Roaming;\n  case AnytoneKeySettingsExtension::KeyFunction::Zone:              return (uint8_t)KeyFunction::Zone;\n  case AnytoneKeySettingsExtension::KeyFunction::RoamingSet:        return (uint8_t)KeyFunction::RoamingSet;\n  case AnytoneKeySettingsExtension::KeyFunction::Mute:              return (uint8_t)KeyFunction::Mute;\n  case AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet:       return (uint8_t)KeyFunction::CtcssDcsSet;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch:    return (uint8_t)KeyFunction::APRSType;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSet:           return (uint8_t)KeyFunction::APRSSet;\n  case AnytoneKeySettingsExtension::KeyFunction::TBSTSend:          return (uint8_t)KeyFunction::TBSTSend;\n  case AnytoneKeySettingsExtension::KeyFunction::Bluetooth:         return (uint8_t)KeyFunction::BluetoothToggle;\n  case AnytoneKeySettingsExtension::KeyFunction::GPS:               return (uint8_t)KeyFunction::GPSToggle;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelName:       return (uint8_t)KeyFunction::ChannelName;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSend:          return (uint8_t)KeyFunction::APRSSend;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSInfo:          return (uint8_t)KeyFunction::APRSInfo;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSRoaming:        return (uint8_t)KeyFunction::GPSRoaming;\n  case AnytoneKeySettingsExtension::KeyFunction::CDTScan:           return (uint8_t)KeyFunction::CTCSSScan;\n  case AnytoneKeySettingsExtension::KeyFunction::DIMShut:           return (uint8_t)KeyFunction::DIMShut;\n  case AnytoneKeySettingsExtension::KeyFunction::SatPredict:        return (uint8_t)KeyFunction::SatellitePredict;\n  default:                                                          return (uint8_t)KeyFunction::Off;\n  }\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) {\n  switch ((KeyFunctionCode)code) {\n  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;\n  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;\n  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;\n  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;\n  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;\n  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;\n  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;\n  case KeyFunction::VOX:               return AnytoneKeySettingsExtension::KeyFunction::VOX;\n  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;\n  case KeyFunction::SubPTT:            return AnytoneKeySettingsExtension::KeyFunction::SubPTT;\n  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;\n  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;\n  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;\n  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;\n  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;\n  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;\n  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;\n  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;\n  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;\n  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;\n  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;\n  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;\n  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;\n  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;\n  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;\n  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;\n  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;\n  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;\n  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;\n  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;\n  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;\n  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;\n  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;\n  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;\n  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;\n  case KeyFunction::SimplexRepeater:   return AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater;\n  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;\n  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;\n  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;\n  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;\n  case KeyFunction::Squelch:           return AnytoneKeySettingsExtension::KeyFunction::Squelch;\n  case KeyFunction::Roaming:           return AnytoneKeySettingsExtension::KeyFunction::Roaming;\n  case KeyFunction::Zone:              return AnytoneKeySettingsExtension::KeyFunction::Zone;\n  case KeyFunction::RoamingSet:        return AnytoneKeySettingsExtension::KeyFunction::RoamingSet;\n  case KeyFunction::Mute:              return AnytoneKeySettingsExtension::KeyFunction::Mute;\n  case KeyFunction::CtcssDcsSet:       return AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet;\n  case KeyFunction::APRSType:          return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch;\n  case KeyFunction::APRSSet:           return AnytoneKeySettingsExtension::KeyFunction::APRSSet;\n  case KeyFunction::TBSTSend:          return AnytoneKeySettingsExtension::KeyFunction::TBSTSend;\n  case KeyFunction::BluetoothToggle:   return AnytoneKeySettingsExtension::KeyFunction::Bluetooth;\n  case KeyFunction::GPSToggle:         return AnytoneKeySettingsExtension::KeyFunction::GPS;\n  case KeyFunction::ChannelName:       return AnytoneKeySettingsExtension::KeyFunction::ChannelName;\n  case KeyFunction::APRSSend:          return AnytoneKeySettingsExtension::KeyFunction::APRSSend;\n  case KeyFunction::APRSInfo:          return AnytoneKeySettingsExtension::KeyFunction::APRSInfo;\n  case KeyFunction::GPSRoaming:        return AnytoneKeySettingsExtension::KeyFunction::GPSRoaming;\n  case KeyFunction::CTCSSScan:         return AnytoneKeySettingsExtension::KeyFunction::CDTScan;\n  case KeyFunction::DIMShut:           return AnytoneKeySettingsExtension::KeyFunction::DIMShut;\n  case KeyFunction::SatellitePredict:  return AnytoneKeySettingsExtension::KeyFunction::SatPredict;\n  default:                             return AnytoneKeySettingsExtension::KeyFunction::Off;\n  }\n}\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug::GeneralSettingsElement\n * ********************************************************************************************* */\nDMR6X2UV2Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : DMR6X2UVCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UV2Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : DMR6X2UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyAShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyBShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyCShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKey1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKey2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyALong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyBLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKeyCLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKey1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UV2Codeplug::GeneralSettingsElement::funcKey2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));\n}\nvoid\nDMR6X2UV2Codeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));\n}\n\n\nbool\nDMR6X2UV2Codeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  if (! DMR6X2UVCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  // apply DMR-6X2UV Pro specific settings.\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  return true;\n}\n\nbool\nDMR6X2UV2Codeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err)\n{\n  if (! DMR6X2UVCodeplug::GeneralSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  // Get or add settings extension\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension()) {\n    ext = ctx.config()->settings()->anytoneExtension();\n  } else {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug::ExtendedSettingsElement\n * ********************************************************************************************* */\nDMR6X2UV2Codeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size)\n  : DMR6X2UVCodeplug::ExtendedSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UV2Codeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)\n  : DMR6X2UVCodeplug::ExtendedSettingsElement(ptr, ExtendedSettingsElement::size())\n{\n  // pass...\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothEnabled() const {\n  return 0 != getUInt8(Offset::bluetoothEnable());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableBluetooth(bool enable) {\n  setUInt8(Offset::bluetoothEnable(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::internalMicEnabled() const {\n  return 0 != getUInt8(Offset::internalMicEnable());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableInternalMic(bool enable) {\n  setUInt8(Offset::internalMicEnable(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::internalSpeakerEnabled() const {\n  return 0 != getUInt8(Offset::internalSpeakerEnable());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableInternalSpeaker(bool enable) {\n  setUInt8(Offset::internalSpeakerEnable(), enable ? 0x01 : 0x00);\n}\n\nunsigned int\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothMicGain() const {\n  return getUInt8(Offset::bluetoothMicGain());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setBluetoothMicGain(unsigned int gain) {\n  gain = std::min(4U, gain);\n  setUInt8(Offset::bluetoothMicGain(), gain);\n}\n\nunsigned int\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothSpeakerGain() const {\n  return getUInt8(Offset::bluetoothSpeakerGain());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setBluetoothSpeakerGain(unsigned int gain) {\n  gain = std::min(4U, gain);\n  setUInt8(Offset::bluetoothSpeakerGain(), gain);\n}\n\nInterval\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothHoldDuration() const {\n  auto num = getUInt8(Offset::bluetoothHoldDuration());\n  if (num <= 30)\n    return Interval::fromSeconds(num);\n  else if (31 == num)\n    return Interval::fromMinutes(1);\n  else if (32 == num)\n    return Interval::fromMinutes(2);\n  /// @todo Implement interval=infinite.\n  return Interval();\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setBluetoothHoldDuration(const Interval &dur) {\n  if (dur.seconds() <=30)\n    setUInt8(Offset::bluetoothHoldDuration(), dur.seconds());\n  else if (dur.seconds() <=60)\n    setUInt8(Offset::bluetoothHoldDuration(), 31);\n  else if (dur.seconds() <=120)\n    setUInt8(Offset::bluetoothHoldDuration(), 32);\n  else\n    setUInt8(Offset::bluetoothHoldDuration(), 33);\n}\n\n\nInterval\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothHoldDelay() const {\n  auto num = getUInt8(Offset::bluetoothHoldDelay());\n  if (0 == num)\n    return Interval::fromMilliseconds(30);\n  return Interval::fromMilliseconds(500*num);\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setBluetoothHoldDelay(const Interval &dur) {\n  if (dur.milliseconds() <=30)\n    setUInt8(Offset::bluetoothHoldDelay(), 0);\n  else\n    setUInt8(Offset::bluetoothHoldDelay(), dur.milliseconds()/500);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothPTTLatchEnabled() const {\n  return 0 != getUInt8(Offset::bluetoothPTTLatch());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableBluetoothPTTLatch(bool enable) {\n  setUInt8(Offset::bluetoothPTTLatch(), enable ? 0x01 : 0x00);\n}\n\n\nInterval\nDMR6X2UV2Codeplug::ExtendedSettingsElement::bluetoothPTTSleepTimeout() const {\n  auto num = getUInt8(Offset::bluetoothPTTSleepTimeout());\n  return Interval::fromMinutes(num);\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setBluetoothPTTSleepTimeout(const Interval &dur) {\n  if (dur.isNull() || (4 < dur.minutes()))\n    setUInt8(Offset::bluetoothPTTSleepTimeout(), 0);\n  else\n    setUInt8(Offset::bluetoothPTTSleepTimeout(), dur.minutes());\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::fmIdleToneEnabled() const {\n  return 0x00 != getUInt8(Offset::fmIdleTone());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableFMIdleTone(bool enable) {\n  setUInt8(Offset::fmIdleTone(), enable ? 0x01 : 0x00);\n}\n\n\nLevel\nDMR6X2UV2Codeplug::ExtendedSettingsElement::fmMicGain() const {\n  return Level::fromValue(getUInt8(Offset::fmMicGain()), Limit::micGain());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setFMMicGain(Level gain) {\n  setUInt8(Offset::fmMicGain(), gain.mapTo(Limit::micGain()));\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::totWarningToneEnabled() const {\n  return 0x00 != getUInt8(Offset::totWarningTone());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableTOTWarningTone(bool enable) {\n  setUInt8(Offset::totWarningTone(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::atpcEnabled() const {\n  return 0x00 != getUInt8(Offset::atpc());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableATPC(bool enable) {\n  setUInt8(Offset::atpc(), enable ? 0x01 : 0x00);\n}\n\n\nGNSSSettings::Systems DMR6X2UV2Codeplug::ExtendedSettingsElement::gnss() const {\n  switch ((GNSS) getUInt8(Offset::gnss())) {\n  case GNSS::GPS: return GNSSSettings::System::GPS;\n  case GNSS::BeiDou: return GNSSSettings::System::Beidou;\n  case GNSS::Both: return GNSSSettings::System::GPS|GNSSSettings::System::Beidou;\n  }\n  return GNSSSettings::System::GPS;\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setGNSS(GNSSSettings::Systems gnss) {\n  if (gnss.testFlag(GNSSSettings::System::GPS))\n    setUInt8(Offset::gnss(), (unsigned int)GNSS::GPS);\n  if (gnss.testFlag(GNSSSettings::System::Beidou))\n    setUInt8(Offset::gnss(), (unsigned int)GNSS::BeiDou);\n  if (gnss.testAnyFlags(GNSSSettings::System::GPS|GNSSSettings::System::Beidou))\n    setUInt8(Offset::gnss(), (unsigned int)GNSS::Both);\n}\n\n\nDMR6X2UV2Codeplug::ExtendedSettingsElement::ChannelIndexDisplay\nDMR6X2UV2Codeplug::ExtendedSettingsElement::channelIndexDisplay() const {\n  return (ChannelIndexDisplay)getUInt8(Offset::displayChannelIndex());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setChannelIndexDisplay(ChannelIndexDisplay mode) {\n  setUInt8(Offset::displayChannelIndex(), (unsigned int)mode);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::wxAlarmEnabled() const {\n  return 0x00 != getUInt8(Offset::wxAlarm());\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::enableWXAlarm(bool enable) {\n  setUInt8(Offset::wxAlarm(), enable ? 0x01 : 0x00);\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::locationSourceGNSS() const {\n  return 0 == getUInt8(Offset::fixedLocationIndex());\n}\n\nunsigned int\nDMR6X2UV2Codeplug::ExtendedSettingsElement::fixedLocationIndex() const {\n  return getUInt8(Offset::fixedLocationIndex())-1;\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setFixedLocationIndex(unsigned int idx) {\n  setUInt8(Offset::fixedLocationIndex(), idx+1);\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setLocationSourceGNSS() {\n  setUInt8(Offset::fixedLocationIndex(), 0);\n}\n\n\nChannel::Power\nDMR6X2UV2Codeplug::ExtendedSettingsElement::satPower() const {\n  switch ((Power)getUInt8(Offset::satPower())) {\n  case Power::Low: return Channel::Power::Low;\n  case Power::Medium: return Channel::Power::Mid;\n  case Power::High: return Channel::Power::High;\n  case Power::Turbo: return Channel::Power::Max;\n  }\n  return Channel::Power::Low;\n}\n\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setSatPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt8(Offset::satPower(), (unsigned int)Power::Low);\n  break;\n  case Channel::Power::Mid:\n    setUInt8(Offset::satPower(), (unsigned int)Power::Medium);\n  break;\n  case Channel::Power::High:\n    setUInt8(Offset::satPower(), (unsigned int)Power::High);\n  break;\n  case Channel::Power::Max:\n    setUInt8(Offset::satPower(), (unsigned int)Power::Turbo);\n  break;\n  }\n}\n\n\nunsigned int\nDMR6X2UV2Codeplug::ExtendedSettingsElement::satSquelchLevel() const {\n  return 2*getUInt8(Offset::satSquelch());\n}\nvoid\nDMR6X2UV2Codeplug::ExtendedSettingsElement::setSatSquelchLevel(unsigned int level) {\n  level = std::min(10U, level);\n  if (1 == level) level = 2; // otherwise level=1 => open\n  setUInt8(Offset::satSquelch(), level/2);\n}\n\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  if (! DMR6X2UVCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode extended settings for DMR-6X2UV codeplug.\";\n    return false;\n  }\n\n  // GPS settings\n  setGNSS(ctx.config()->settings()->gnss()->systems());\n\n  // audio settings\n  if (ctx.config()->settings()->audio()->fmMicGainEnabled())\n    setFMMicGain(ctx.config()->settings()->audio()->fmMicGain());\n  else\n    setFMMicGain(ctx.config()->settings()->audio()->micGain());\n\n  // tone settings\n  enableFMIdleTone(ctx.config()->settings()->tone()->channelIdle().setFlag(Channel::Type::FM));\n\n  // Encode device specific settings\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Bluetooth settings.\n  enableBluetooth(ext->bluetoothSettings()->bluetoothEnabled());\n  enableInternalMic(ext->bluetoothSettings()->internalMicEnabled());\n  enableInternalSpeaker(ext->bluetoothSettings()->internalSpeakerEnabled());\n  setBluetoothMicGain(ext->bluetoothSettings()->micGain());\n  setBluetoothSpeakerGain(ext->bluetoothSettings()->speakerGain());\n  setBluetoothHoldDuration(ext->bluetoothSettings()->holdDuration());\n  setBluetoothHoldDelay(ext->bluetoothSettings()->holdDelay());\n  enableBluetoothPTTLatch(ext->bluetoothSettings()->pttLatch());\n  setBluetoothPTTSleepTimeout(ext->bluetoothSettings()->pttSleepTimer());\n\n  // Encode audio settings\n  enableTOTWarningTone(ext->toneSettings()->totNotification());\n  enableWXAlarm(ext->toneSettings()->wxAlarm());\n\n  // Power settings\n  enableATPC(ext->powerSaveSettings()->atpc());\n\n  // Display settings\n  setChannelIndexDisplay(ext->displaySettings()->showGlobalChannelNumber() ?\n                           ChannelIndexDisplay::GlobalIndex :\n                           ChannelIndexDisplay::IndexWithinZone);\n\n  // Sat settings\n  setSatPower(ext->satelliteSettings()->power());\n  setSatSquelchLevel(ext->satelliteSettings()->squelch());\n\n  return true;\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! DMR6X2UVCodeplug::ExtendedSettingsElement::updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode extended settings for DMR-6X2UV codeplug\";\n    return false;\n  }\n\n  // Store GPS settings\n  ctx.config()->settings()->gnss()->setSystems(gnss());\n\n  // Store audio settings\n  if (ctx.config()->settings()->audio()->micGain() == fmMicGain())\n    ctx.config()->settings()->audio()->disableFMMicGain();\n  else\n    ctx.config()->settings()->audio()->setFMMicGain(fmMicGain());\n\n  // Store tone settings\n  ctx.config()->settings()->tone()->setChannelIdle(\n    ctx.config()->settings()->tone()->channelIdle()\n      | (fmIdleToneEnabled() ? Channel::Type::FM : Channel::Type::None) );\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Bluetooth settings\n  ext->bluetoothSettings()->enableBluetooth(bluetoothEnabled());\n  ext->bluetoothSettings()->enableInternalMic(internalMicEnabled());\n  ext->bluetoothSettings()->enableInternalSpeaker(internalSpeakerEnabled());\n  ext->bluetoothSettings()->setMicGain(bluetoothMicGain());\n  ext->bluetoothSettings()->setSpeakerGain(bluetoothSpeakerGain());\n  ext->bluetoothSettings()->setHoldDuration(bluetoothHoldDuration());\n  ext->bluetoothSettings()->setHoldDelay(bluetoothHoldDelay());\n  ext->bluetoothSettings()->enablePTTLatch(bluetoothPTTLatchEnabled());\n  ext->bluetoothSettings()->setPTTSleepTimer(bluetoothPTTSleepTimeout());\n\n  ext->toneSettings()->enableTOTNotification(totWarningToneEnabled());\n  ext->toneSettings()->enableWXAlarm(wxAlarmEnabled());\n\n  // Power settings\n  ext->powerSaveSettings()->enableATPC(atpcEnabled());\n\n  // Display settings\n  ext->displaySettings()->enableShowGlobalChannelNumber(\n        ChannelIndexDisplay::GlobalIndex == channelIndexDisplay());\n\n  // Satellite settings\n  ext->satelliteSettings()->setPower(satPower());\n  ext->satelliteSettings()->setSquelch(satSquelchLevel());\n\n  return true;\n}\n\n\nbool\nDMR6X2UV2Codeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) {\n  if (! DMR6X2UVCodeplug::ExtendedSettingsElement::linkConfig(ctx, err)) {\n    errMsg(err) << \"Cannot link extended settings for DMR-6X2UV codeplug\";\n    return false;\n  }\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return false;\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug::APRSFilterElement\n * ********************************************************************************************* */\nDMR6X2UV2Codeplug::APRSFilterElement::APRSFilterElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass\n}\n\nDMR6X2UV2Codeplug::APRSFilterElement::APRSFilterElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\n\nvoid\nDMR6X2UV2Codeplug::APRSFilterElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDMR6X2UV2Codeplug::APRSFilterElement::isValid() const {\n  if (! Element::isValid())\n    return false;\n  return 0 != getUInt8(Offset::valid());\n}\n\n\nQString\nDMR6X2UV2Codeplug::APRSFilterElement::call() const {\n  return readASCII(Offset::call(), Limit::call(), 0x00);\n}\n\nvoid\nDMR6X2UV2Codeplug::APRSFilterElement::setCall(const QString &call) {\n  writeASCII(Offset::call(), call, Limit::call(), 0x00);\n}\n\n\nunsigned int\nDMR6X2UV2Codeplug::APRSFilterElement::ssid() const {\n  return getUInt8(Offset::ssid());\n}\n\nvoid\nDMR6X2UV2Codeplug::APRSFilterElement::setSSID(unsigned int ssid) {\n  setUInt8(Offset::ssid(), ssid);\n}\n\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug::GPSRoamingZoneElement\n * ********************************************************************************************* */\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::GPSRoamingZoneElement(uint8_t *ptr, size_t size)\n  : Element{ptr, size}\n{\n  // pass...\n}\n\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::GPSRoamingZoneElement(uint8_t *ptr)\n  : Element{ptr, size()}\n{\n  // pass...\n}\n\nvoid\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::isValid() const {\n  return Element::isValid() && (0 != getUInt8(Offset::valid()));\n}\n\n\nbool\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::hasRoamingZoneIndex() const {\n  return 0xff != getUInt8(Offset::zoneIndex());\n}\n\nunsigned int\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::roamingZoneIndex() const {\n  return getUInt8(Offset::zoneIndex());\n}\n\nvoid\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::setRoamingZoneIndex(unsigned int idx) {\n  setUInt8(Offset::zoneIndex(), idx);\n}\n\nvoid\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::clearRoamingZoneIndex() {\n  setUInt8(Offset::zoneIndex(), 0xff);\n}\n\n\nQGeoCoordinate\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::coordinate() const {\n  double latDeg = getUInt8(Offset::latDegrees()), latMin = getUInt8(Offset::latMinutes()),\n      latSec = getUInt8(Offset::latSeconds()),\n      lonDeg = getUInt8(Offset::lonDegrees()), lonMin = getUInt8(Offset::lonMinutes()),\n      lonSec = getUInt8(Offset::lonSeconds());\n  double lat = latDeg + (latMin + (latSec/100))/60,\n      lon = lonDeg + (lonMin + (lonSec/100))/60;\n  if (getUInt8(Offset::latHemisphere())) lat *= -1;\n  if (getUInt8(Offset::lonHemisphere())) lon *= -1;\n  return QGeoCoordinate(lat, lon);\n}\n\nvoid\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::setCoordinate(const QGeoCoordinate &coor) {\n  double lat = coor.latitude(), lon = coor.longitude();\n  setUInt8(Offset::latHemisphere(), (lat<0) ? 1 : 0); lat = std::abs(lat);\n  setUInt8(Offset::lonHemisphere(), (lon<0) ? 1 : 0); lon = std::abs(lon);\n  setUInt8(Offset::latDegrees(), lat); lat -= int(lat); lat *=60;\n  setUInt8(Offset::lonDegrees(), lon); lon -= int(lon); lon *=60;\n  setUInt8(Offset::latMinutes(), lat); lat -= int(lat); lat *=100;\n  setUInt8(Offset::lonMinutes(), lon); lon -= int(lon); lon *=100;\n  setUInt8(Offset::latSeconds(), lat);\n  setUInt8(Offset::lonSeconds(), lon);\n}\n\n\nunsigned int\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::radius() const {\n  return getUInt16_le(Offset::radius());\n}\n\nvoid\nDMR6X2UV2Codeplug::GPSRoamingZoneElement::setRadius(unsigned int radius) {\n  setUInt16_le(Offset::radius(), radius);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UV2Codeplug\n * ********************************************************************************************* */\nDMR6X2UV2Codeplug::DMR6X2UV2Codeplug(const QString &label, QObject *parent)\n  : DMR6X2UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nDMR6X2UV2Codeplug::DMR6X2UV2Codeplug(QObject *parent)\n  : DMR6X2UVCodeplug(\"BTECH DMR-6X2UV PRO\", parent)\n{\n  // pass...\n}\n\n\nvoid\nDMR6X2UV2Codeplug::allocateUpdated() {\n  image(0).addElement(Offset::aprsFilterBank(), Limit::aprsFilter()*APRSFilterElement::size());\n  image(0).addElement(Offset::gpsRoamingZones(), Limit::gpsRoamingZones()*GPSRoamingZoneElement::size());\n}\n\nvoid\nDMR6X2UV2Codeplug::allocateForEncoding() {\n  DMR6X2UVCodeplug::allocateForEncoding();\n}\n\nvoid\nDMR6X2UV2Codeplug::allocateForDecoding() {\n  DMR6X2UVCodeplug::allocateForDecoding();\n}\n\n\nbool\nDMR6X2UV2Codeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! DMR6X2UVCodeplug::encodeElements(flags, ctx, err))\n    return false;\n\n  return true;\n}\n\n\nbool\nDMR6X2UV2Codeplug::createElements(Context &ctx, const ErrorStack &err) {\n  if (! DMR6X2UVCodeplug::createElements(ctx, err))\n    return false;\n\n  return true;\n}\n\n\nvoid\nDMR6X2UV2Codeplug::allocateGeneralSettings() {\n  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());\n  image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size());\n}\n\nbool\nDMR6X2UV2Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode general settings element.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx)) {\n    errMsg(err) << \"Cannot encode extended settings element.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UV2Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings element.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx)) {\n    errMsg(err) << \"Cannot decode extended settings element.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UV2Codeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err);\n}\n\n\n"
  },
  {
    "path": "lib/dmr6x2uv2_codeplug.hh",
    "content": "#ifndef DMR6X2UV2CODEPLUG_HH\n#define DMR6X2UV2CODEPLUG_HH\n\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"d878uv_codeplug.hh\"\n\n\n/** Represents the device specific binary codeplug for BTECH DMR-6X2UV PRO radios.\n *\n * This codeplug implementation is compatible with firmware revision 1.21.\n *\n * For details, see https://dmr-tools.github.io/codeplugs\n *\n * @ingroup dmr6x2uv */\nclass DMR6X2UV2Codeplug : public DMR6X2UVCodeplug\n{\n  Q_OBJECT\n\nprotected:\npublic:\n  /** General settings element for the DMR-6X2UV PRO.\n   *\n   * Extends the @c DMR6X2UVCodeplug::GeneralSettingsElement by the device specific settings for\n   * the BTECH DMR-6X2UV PRO. */\n  class GeneralSettingsElement: public DMR6X2UVCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific encoding of the key functions. */\n    struct KeyFunction {\n    public:\n      /** Encodes key function. */\n      static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone);\n      /** Decodes key function. */\n      static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code);\n\n    protected:\n      /** Device specific key functions. */\n      typedef enum {\n        Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04,\n        Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09,\n        Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f,\n        Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14,\n        HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19,\n        WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d,\n        PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21,\n        ChannelType = 0x22, SimplexRepeater = 0x23, Ranging = 0x24, ChannelRanging = 0x25,\n        MaxVolume = 0x26, Slot = 0x27, Squelch = 0x28, Roaming = 0x29, Zone = 0x2a, RoamingSet = 0x2b,\n        Mute = 0x02c, CtcssDcsSet=0x2d, APRSType = 0x2e, APRSSet = 0x2f, TBSTSend = 0x30,\n        BluetoothToggle = 0x31, GPSToggle = 0x32, ChannelName = 0x33, APRSSend = 0x34,\n        APRSInfo = 0x35, GPSRoaming = 0x36, CTCSSScan = 0x37, DIMShut = 0x38, SatellitePredict = 0x39\n      } KeyFunctionCode;\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00e0; }\n\n    AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const;\n    void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const;\n    void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const;\n    void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const;\n    void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const;\n    void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const;\n    void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const;\n    void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const;\n    void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const;\n    void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func);\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const;\n    void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err);\n    bool updateConfig(Context &ctx, const ErrorStack &err);\n\n  protected:\n    /** Some internal used offsets within the element. */\n    struct Offset: public D868UVCodeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      /// @endcond\n    };\n\n  };\n\n\n  /** Implements some settings extension for the BTECH DMR-6X2UV PRO. */\n  class ExtendedSettingsElement: public DMR6X2UVCodeplug::ExtendedSettingsElement\n  {\n  public:\n    enum class GNSS {\n      GPS=0, BeiDou=1, Both=2\n    };\n\n    enum class ChannelIndexDisplay {\n      GlobalIndex = 0, IndexWithinZone = 1\n    };\n\n    enum class Power {\n      Low = 0, Medium = 1, High = 2, Turbo = 3\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    ExtendedSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ExtendedSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Returns @c true, if bluetooth is enabled. */\n    virtual bool bluetoothEnabled() const;\n    /** Enables/disables bluetooth. */\n    virtual void enableBluetooth(bool enable);\n\n    /** Returns @c true, if the internal mic is enabled additionally to the bluetooth input. */\n    virtual bool internalMicEnabled() const;\n    /** Enables/disables the internal mic additionally to the bluetooth input. */\n    virtual void enableInternalMic(bool enable);\n\n    /** Returns @c true, if the internal speaker is enabled additionally to the bluetooth output. */\n    virtual bool internalSpeakerEnabled() const;\n    /** Enables/disables the internal speaker additionally to the bluetooth output. */\n    virtual void enableInternalSpeaker(bool enable);\n\n    /** Returns the bluetooth mic gain. Valid values are 0,...,4. */\n    virtual unsigned int bluetoothMicGain() const;\n    /** Sets the bluetooth mic gain. Valid values are 0,...,4. */\n    virtual void setBluetoothMicGain(unsigned int gain);\n\n    /** Returns the bluetooth speaker gain. Valid values are 0,...,4. */\n    virtual unsigned int bluetoothSpeakerGain() const;\n    /** Sets the bluetooth speaker gain. Valid values are 0,...,4. */\n    virtual void setBluetoothSpeakerGain(unsigned int gain);\n\n    /** Returns the hold duration. */\n    virtual Interval bluetoothHoldDuration() const;\n    /** Sets the hold duration. */\n    virtual void setBluetoothHoldDuration(const Interval &dur);\n\n    /** Returns the hold delay. */\n    virtual Interval bluetoothHoldDelay() const;\n    /** Sets the hold duration. */\n    virtual void setBluetoothHoldDelay(const Interval &dur);\n\n    /** Returns @c true, if PTT latches. */\n    virtual bool bluetoothPTTLatchEnabled() const;\n    /** Enable/disable bluetooth PTT latch. */\n    virtual void enableBluetoothPTTLatch(bool enable);\n\n    /** Returns the bluetooth PTT sleep timeout. */\n    virtual Interval bluetoothPTTSleepTimeout() const;\n    /** Sets the bluetooth PTT sleep timeout. */\n    virtual void setBluetoothPTTSleepTimeout(const Interval &dur);\n\n    /** Returns @c true if the FM channel idle tone is enabled. */\n    virtual bool fmIdleToneEnabled() const;\n    /** Enables/disables FM channel idle tone. */\n    virtual void enableFMIdleTone(bool enable);\n\n    /** Returns the FM mic gain [1-10]. */\n    virtual Level fmMicGain() const;\n    /** Sets the FM mic gain [1-10]. */\n    virtual void setFMMicGain(Level gain);\n\n    /** Returns @c true, if transmit timeout warning tone is enabled. */\n    virtual bool totWarningToneEnabled() const;\n    /** Enables/disables transmit timeout warning tone. */\n    virtual void enableTOTWarningTone(bool enable);\n\n    /** Returns @c true, if ATPC is enabled. */\n    virtual bool atpcEnabled() const;\n    /** Enables/disables ATPC. */\n    virtual void enableATPC(bool enable);\n\n    /** Returns enabled GNSSs */\n    virtual GNSSSettings::Systems gnss() const;\n    /** Sets enabled GNSSs */\n    virtual void setGNSS(GNSSSettings::Systems gnss);\n\n    /** Returns the channel index display mode. */\n    virtual ChannelIndexDisplay channelIndexDisplay() const;\n    /** Sets the channel index display mode. */\n    virtual void setChannelIndexDisplay(ChannelIndexDisplay mode);\n\n    /** Returns @c true if the weather alarm is enabled. */\n    virtual bool wxAlarmEnabled() const;\n    /** Enables/disables the weather alarm. */\n    virtual void enableWXAlarm(bool enable);\n\n    /** Returns @c true if the location is taken from GNSS, otherwise a fixed location is used. */\n    virtual bool locationSourceGNSS() const;\n    /** Returns the fixed location index. */\n    virtual unsigned int fixedLocationIndex() const;\n    /** Sets the fixed location index. */\n    virtual void setFixedLocationIndex(unsigned int idx);\n    /** Sets the location source to GNSS. */\n    virtual void setLocationSourceGNSS();\n\n    /** Returns the power setting for satellite mode. */\n    virtual Channel::Power satPower() const;\n    /** Sets the power level for satellite mode. */\n    virtual void setSatPower(Channel::Power power);\n\n    /** Returns the squelch level for satellite mode [0,1-10], 0=open.*/\n    virtual unsigned int satSquelchLevel() const;\n    /** Sets the squelch level for satellite mode [0,1-10], 0=open. */\n    virtual void setSatSquelchLevel(unsigned int level);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n    bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: DMR6X2UVCodeplug::ExtendedSettingsElement::Limit {\n      static constexpr Range<unsigned int> micGain() { return {0,4}; }    ///< Valid mic gain settings.\n    };\n\n  protected:\n    /** Some internal offset within the codeplug element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUEMNT\n      static constexpr unsigned int bluetoothEnable()            { return 0x0016; }\n      static constexpr unsigned int internalMicEnable()          { return 0x0017; }\n      static constexpr unsigned int internalSpeakerEnable()      { return 0x0018; }\n      static constexpr unsigned int bluetoothMicGain()           { return 0x0019; }\n      static constexpr unsigned int bluetoothSpeakerGain()       { return 0x001a; }\n      static constexpr unsigned int bluetoothHoldDuration()      { return 0x001b; }\n      static constexpr unsigned int bluetoothHoldDelay()         { return 0x001c; }\n      static constexpr unsigned int bluetoothPTTLatch()          { return 0x001d; }\n      static constexpr unsigned int bluetoothPTTSleepTimeout()   { return 0x001e; }\n      static constexpr unsigned int fmIdleTone()                 { return 0x001f; }\n      static constexpr unsigned int fmMicGain()                  { return 0x0020; }\n      static constexpr unsigned int totWarningTone()             { return 0x0021; }\n      static constexpr unsigned int atpc()                       { return 0x0022; }\n      static constexpr unsigned int gnss()                       { return 0x0023; }\n      static constexpr unsigned int displayChannelIndex()        { return 0x0024; }\n      static constexpr unsigned int wxAlarm()                    { return 0x0026; }\n      static constexpr unsigned int fixedLocationIndex()         { return 0x0027; }\n      static constexpr unsigned int satPower()                   { return 0x0028; }\n      static constexpr unsigned int satSquelch()                 { return 0x0029; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a single APRS RX filter. */\n  class APRSFilterElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    APRSFilterElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    APRSFilterElement(uint8_t *ptr);\n\n    void clear() override;\n    bool isValid() const override;\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x08; }\n\n    /** Returns the call. */\n    virtual QString call() const;\n    /** Sets the callsign. */\n    virtual void setCall(const QString &call);\n\n    /** Returns the SSID. */\n    virtual unsigned int ssid() const;\n    /** Sets the SSID. */\n    virtual void setSSID(unsigned int ssid);\n\n  public:\n    /** Some limits for the element. */\n    struct Limit: public Element::Limit {\n      /** Maximum call sign length. */\n      static constexpr unsigned int call() { return 6; }\n    };\n\n  protected:\n    /** Internal Offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int valid() { return 0x0000; }\n      static constexpr unsigned int call()  { return 0x0001; }\n      static constexpr unsigned int ssid()  { return 0x0007; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a GPS roaming zone. */\n  class GPSRoamingZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GPSRoamingZoneElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GPSRoamingZoneElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    void clear() override;\n    bool isValid() const override;\n\n    /** Returns @c true, if a roaming zone is set. */\n    virtual bool hasRoamingZoneIndex() const;\n    /** Returns the roaming zone index. */\n    virtual unsigned int roamingZoneIndex() const;\n    /** Sets the roaming zone index. */\n    virtual void setRoamingZoneIndex(unsigned int idx);\n    /** Clears the roaming zone index. */\n    virtual void clearRoamingZoneIndex();\n\n    /** Returns the center of the roaming zone. */\n    virtual QGeoCoordinate coordinate() const;\n    /** Sets the center of the roaming zone. */\n    virtual void setCoordinate(const QGeoCoordinate &coor);\n\n    /** Returns the radius in unknown units. */\n    virtual unsigned int radius() const;\n    /** Sets the radius in unknown units. */\n    virtual void setRadius(unsigned int radius);\n\n  protected:\n    /** Internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int valid()                             { return 0x0000; }\n      static constexpr unsigned int zoneIndex()                         { return 0x0001; }\n      static constexpr unsigned int latDegrees()                        { return 0x0002; }\n      static constexpr unsigned int latMinutes()                        { return 0x0003; }\n      static constexpr unsigned int latSeconds()                        { return 0x0004; }\n      static constexpr unsigned int latHemisphere()                     { return 0x0005; }\n      static constexpr unsigned int lonDegrees()                        { return 0x0006; }\n      static constexpr unsigned int lonMinutes()                        { return 0x0007; }\n      static constexpr unsigned int lonSeconds()                        { return 0x0008; }\n      static constexpr unsigned int lonHemisphere()                     { return 0x0009; }\n      static constexpr unsigned int radius()                            { return 0x000c; }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  /** Hidden constructor. */\n  explicit DMR6X2UV2Codeplug(const QString &label, QObject *parent=nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit DMR6X2UV2Codeplug(QObject *parent=nullptr);\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit: public DMR6X2UVCodeplug::Limit {\n    /** Maximum number of APRS receive filters. */\n    static constexpr unsigned int aprsFilter() { return 32; }\n    /** Maximum number of GPS roaming zones. */\n    static constexpr unsigned int gpsRoamingZones() { return 32; }\n  };\n\nprotected:\n  void allocateUpdated() override;\n  void allocateForEncoding() override;\n  void allocateForDecoding() override;\n\n  bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool createElements(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\n  void allocateGeneralSettings() override;\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n  bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) override;\n\nprotected:\n  /** Some offsets within the codeplug. */\n  struct Offset: public DMR6X2UVCodeplug::Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int aprsFilterBank()      { return 0x02501800; }\n    static constexpr unsigned int gpsRoamingZones()     { return 0x02504000; }\n    /// @endcond\n  };\n};\n\n#endif // DMR6X2UV2CODEPLUG_HH\n"
  },
  {
    "path": "lib/dmr6x2uv2_limits.cc",
    "content": "#include \"dmr6x2uv2_limits.hh\"\n#include \"dmr6x2uv2_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include \"anytone_satelliteconfig.hh\"\n\n\nDMR6X2UV2Limits::DMR6X2UV2Limits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                                 const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                                 const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V100\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 500000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = true;\n  _satelliteConfigImplemented  = true;\n  _numSatellites               = AnytoneSatelliteConfig::Limit::satellites();\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(DMR6X2UV2Codeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/dmr6x2uv2_limits.hh",
    "content": "#ifndef DMR6X2UV2LIMITS_HH\n#define DMR6X2UV2LIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the BTECH DMR-6X2UV PRO.\n * @ingroup dmr6x2uv2 */\nclass DMR6X2UV2Limits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  DMR6X2UV2Limits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                  const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                  const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D878UVLIMITS_HH\n"
  },
  {
    "path": "lib/dmr6x2uv_codeplug.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"roamingchannel.hh\"\n#include \"config.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n\n\n/* ******************************************************************************************** *\n * Implementation of DMR6X2UVCodeplug::Color\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::Color::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case Orange: return AnytoneDisplaySettingsExtension::Color::Orange;\n  case Red: return AnytoneDisplaySettingsExtension::Color::Red;\n  case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow;\n  case Green: return AnytoneDisplaySettingsExtension::Color::Green;\n  case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise;\n  case Blue: return AnytoneDisplaySettingsExtension::Color::Blue;\n  case White: return AnytoneDisplaySettingsExtension::Color::White;\n  case Black: return AnytoneDisplaySettingsExtension::Color::Black;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::White;\n}\n\nuint8_t\nDMR6X2UVCodeplug::Color::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange;\n  case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow;\n  case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green;\n  case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue;\n  case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black;\n  default: break;\n  }\n  return (uint8_t) White;\n}\n\n/* ******************************************************************************************** *\n * Implementation of DMR6X2UVCodeplug::BackgroundColor\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::BackgroundColor::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case Black: return AnytoneDisplaySettingsExtension::Color::Black;\n  case Blue: return AnytoneDisplaySettingsExtension::Color::Blue;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::Black;\n}\n\nuint8_t\nDMR6X2UVCodeplug::BackgroundColor::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue;\n  default: break;\n  }\n  return (uint8_t) Black;\n}\n\n/* ******************************************************************************************** *\n * Implementation of DMR6X2UVCodeplug::FontColor\n * ******************************************************************************************** */\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::FontColor::decode(uint8_t code) {\n  switch((CodedColor) code) {\n  case Orange: return AnytoneDisplaySettingsExtension::Color::Orange;\n  case Red: return AnytoneDisplaySettingsExtension::Color::Red;\n  case Yellow: return AnytoneDisplaySettingsExtension::Color::Yellow;\n  case Green: return AnytoneDisplaySettingsExtension::Color::Green;\n  case Turquoise: return AnytoneDisplaySettingsExtension::Color::Turquoise;\n  case Blue: return AnytoneDisplaySettingsExtension::Color::Blue;\n  case White: return AnytoneDisplaySettingsExtension::Color::White;\n  case Black: return AnytoneDisplaySettingsExtension::Color::Black;\n  default: break;\n  }\n  return AnytoneDisplaySettingsExtension::Color::White;\n}\n\nuint8_t\nDMR6X2UVCodeplug::FontColor::encode(AnytoneDisplaySettingsExtension::Color color) {\n  switch(color) {\n  case AnytoneDisplaySettingsExtension::Color::Orange: return (uint8_t) Orange;\n  case AnytoneDisplaySettingsExtension::Color::Red: return (uint8_t) Red;\n  case AnytoneDisplaySettingsExtension::Color::Yellow: return (uint8_t) Yellow;\n  case AnytoneDisplaySettingsExtension::Color::Green: return (uint8_t) Green;\n  case AnytoneDisplaySettingsExtension::Color::Turquoise: return (uint8_t) Turquoise;\n  case AnytoneDisplaySettingsExtension::Color::Blue: return (uint8_t) Blue;\n  case AnytoneDisplaySettingsExtension::Color::White: return (uint8_t) White;\n  case AnytoneDisplaySettingsExtension::Color::Black: return (uint8_t) Black;\n  default: break;\n  }\n  return (uint8_t) White;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction\n * ********************************************************************************************* */\nuint8_t\nDMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction::encode(AnytoneKeySettingsExtension::KeyFunction func) {\n  switch (func) {\n  case AnytoneKeySettingsExtension::KeyFunction::Off:               return (uint8_t)KeyFunction::Off;\n  case AnytoneKeySettingsExtension::KeyFunction::Voltage:           return (uint8_t)KeyFunction::Voltage;\n  case AnytoneKeySettingsExtension::KeyFunction::Power:             return (uint8_t)KeyFunction::Power;\n  case AnytoneKeySettingsExtension::KeyFunction::Repeater:          return (uint8_t)KeyFunction::Repeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Reverse:           return (uint8_t)KeyFunction::Reverse;\n  case AnytoneKeySettingsExtension::KeyFunction::Encryption:        return (uint8_t)KeyFunction::Encryption;\n  case AnytoneKeySettingsExtension::KeyFunction::Call:              return (uint8_t)KeyFunction::Call;\n  case AnytoneKeySettingsExtension::KeyFunction::VOX:               return (uint8_t)KeyFunction::VOX;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleVFO:         return (uint8_t)KeyFunction::ToggleVFO;\n  case AnytoneKeySettingsExtension::KeyFunction::SubPTT:            return (uint8_t)KeyFunction::SubPTT;\n  case AnytoneKeySettingsExtension::KeyFunction::Scan:              return (uint8_t)KeyFunction::Scan;\n  case AnytoneKeySettingsExtension::KeyFunction::WFM:               return (uint8_t)KeyFunction::WFM;\n  case AnytoneKeySettingsExtension::KeyFunction::Alarm:             return (uint8_t)KeyFunction::Alarm;\n  case AnytoneKeySettingsExtension::KeyFunction::RecordSwitch:      return (uint8_t)KeyFunction::RecordSwitch;\n  case AnytoneKeySettingsExtension::KeyFunction::Record:            return (uint8_t)KeyFunction::Record;\n  case AnytoneKeySettingsExtension::KeyFunction::SMS:               return (uint8_t)KeyFunction::SMS;\n  case AnytoneKeySettingsExtension::KeyFunction::Dial:              return (uint8_t)KeyFunction::Dial;\n  case AnytoneKeySettingsExtension::KeyFunction::GPSInformation:    return (uint8_t)KeyFunction::GPSInformation;\n  case AnytoneKeySettingsExtension::KeyFunction::Monitor:           return (uint8_t)KeyFunction::Monitor;\n  case AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel: return (uint8_t)KeyFunction::ToggleMainChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey1:           return (uint8_t)KeyFunction::HotKey1;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey2:           return (uint8_t)KeyFunction::HotKey2;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey3:           return (uint8_t)KeyFunction::HotKey3;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey4:           return (uint8_t)KeyFunction::HotKey4;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey5:           return (uint8_t)KeyFunction::HotKey5;\n  case AnytoneKeySettingsExtension::KeyFunction::HotKey6:           return (uint8_t)KeyFunction::HotKey6;\n  case AnytoneKeySettingsExtension::KeyFunction::WorkAlone:         return (uint8_t)KeyFunction::WorkAlone;\n  case AnytoneKeySettingsExtension::KeyFunction::SkipChannel:       return (uint8_t)KeyFunction::SkipChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::DMRMonitor:        return (uint8_t)KeyFunction::DMRMonitor;\n  case AnytoneKeySettingsExtension::KeyFunction::SubChannel:        return (uint8_t)KeyFunction::SubChannel;\n  case AnytoneKeySettingsExtension::KeyFunction::PriorityZone:      return (uint8_t)KeyFunction::PriorityZone;\n  case AnytoneKeySettingsExtension::KeyFunction::VFOScan:           return (uint8_t)KeyFunction::VFOScan;\n  case AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality:   return (uint8_t)KeyFunction::MICSoundQuality;\n  case AnytoneKeySettingsExtension::KeyFunction::LastCallReply:     return (uint8_t)KeyFunction::LastCallReply;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelType:       return (uint8_t)KeyFunction::ChannelType;\n  case AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater:   return (uint8_t)KeyFunction::SimplexRepeater;\n  case AnytoneKeySettingsExtension::KeyFunction::Ranging:           return (uint8_t)KeyFunction::Ranging;\n  case AnytoneKeySettingsExtension::KeyFunction::ChannelRanging:    return (uint8_t)KeyFunction::ChannelRanging;\n  case AnytoneKeySettingsExtension::KeyFunction::MaxVolume:         return (uint8_t)KeyFunction::MaxVolume;\n  case AnytoneKeySettingsExtension::KeyFunction::Slot:              return (uint8_t)KeyFunction::Slot;\n  case AnytoneKeySettingsExtension::KeyFunction::Squelch:           return (uint8_t)KeyFunction::Squelch;\n  case AnytoneKeySettingsExtension::KeyFunction::Roaming:           return (uint8_t)KeyFunction::Roaming;\n  case AnytoneKeySettingsExtension::KeyFunction::Zone:              return (uint8_t)KeyFunction::Zone;\n  case AnytoneKeySettingsExtension::KeyFunction::RoamingSet:        return (uint8_t)KeyFunction::RoamingSet;\n  case AnytoneKeySettingsExtension::KeyFunction::Mute:              return (uint8_t)KeyFunction::Mute;\n  case AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet:       return (uint8_t)KeyFunction::CtcssDcsSet;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch:    return (uint8_t)KeyFunction::APRSType;\n  case AnytoneKeySettingsExtension::KeyFunction::APRSSet:           return (uint8_t)KeyFunction::APRSSet;\n  case AnytoneKeySettingsExtension::KeyFunction::DIMShut:           return (uint8_t)KeyFunction::DIMShut;\n  case AnytoneKeySettingsExtension::KeyFunction::SatPredict:        return (uint8_t)KeyFunction::SatPredict;\n  default:                                                          return (uint8_t)KeyFunction::Off;\n  }\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::KeyFunction::decode(uint8_t code) {\n  switch ((KeyFunctionCode)code) {\n  case KeyFunction::Off:               return AnytoneKeySettingsExtension::KeyFunction::Off;\n  case KeyFunction::Voltage:           return AnytoneKeySettingsExtension::KeyFunction::Voltage;\n  case KeyFunction::Power:             return AnytoneKeySettingsExtension::KeyFunction::Power;\n  case KeyFunction::Repeater:          return AnytoneKeySettingsExtension::KeyFunction::Repeater;\n  case KeyFunction::Reverse:           return AnytoneKeySettingsExtension::KeyFunction::Reverse;\n  case KeyFunction::Encryption:        return AnytoneKeySettingsExtension::KeyFunction::Encryption;\n  case KeyFunction::Call:              return AnytoneKeySettingsExtension::KeyFunction::Call;\n  case KeyFunction::VOX:               return AnytoneKeySettingsExtension::KeyFunction::VOX;\n  case KeyFunction::ToggleVFO:         return AnytoneKeySettingsExtension::KeyFunction::ToggleVFO;\n  case KeyFunction::SubPTT:            return AnytoneKeySettingsExtension::KeyFunction::SubPTT;\n  case KeyFunction::Scan:              return AnytoneKeySettingsExtension::KeyFunction::Scan;\n  case KeyFunction::WFM:               return AnytoneKeySettingsExtension::KeyFunction::WFM;\n  case KeyFunction::Alarm:             return AnytoneKeySettingsExtension::KeyFunction::Alarm;\n  case KeyFunction::RecordSwitch:      return AnytoneKeySettingsExtension::KeyFunction::RecordSwitch;\n  case KeyFunction::Record:            return AnytoneKeySettingsExtension::KeyFunction::Record;\n  case KeyFunction::SMS:               return AnytoneKeySettingsExtension::KeyFunction::SMS;\n  case KeyFunction::Dial:              return AnytoneKeySettingsExtension::KeyFunction::Dial;\n  case KeyFunction::GPSInformation:    return AnytoneKeySettingsExtension::KeyFunction::GPSInformation;\n  case KeyFunction::Monitor:           return AnytoneKeySettingsExtension::KeyFunction::Monitor;\n  case KeyFunction::ToggleMainChannel: return AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel;\n  case KeyFunction::HotKey1:           return AnytoneKeySettingsExtension::KeyFunction::HotKey1;\n  case KeyFunction::HotKey2:           return AnytoneKeySettingsExtension::KeyFunction::HotKey2;\n  case KeyFunction::HotKey3:           return AnytoneKeySettingsExtension::KeyFunction::HotKey3;\n  case KeyFunction::HotKey4:           return AnytoneKeySettingsExtension::KeyFunction::HotKey4;\n  case KeyFunction::HotKey5:           return AnytoneKeySettingsExtension::KeyFunction::HotKey5;\n  case KeyFunction::HotKey6:           return AnytoneKeySettingsExtension::KeyFunction::HotKey6;\n  case KeyFunction::WorkAlone:         return AnytoneKeySettingsExtension::KeyFunction::WorkAlone;\n  case KeyFunction::SkipChannel:       return AnytoneKeySettingsExtension::KeyFunction::SkipChannel;\n  case KeyFunction::DMRMonitor:        return AnytoneKeySettingsExtension::KeyFunction::DMRMonitor;\n  case KeyFunction::SubChannel:        return AnytoneKeySettingsExtension::KeyFunction::SubChannel;\n  case KeyFunction::PriorityZone:      return AnytoneKeySettingsExtension::KeyFunction::PriorityZone;\n  case KeyFunction::VFOScan:           return AnytoneKeySettingsExtension::KeyFunction::VFOScan;\n  case KeyFunction::MICSoundQuality:   return AnytoneKeySettingsExtension::KeyFunction::MICSoundQuality;\n  case KeyFunction::LastCallReply:     return AnytoneKeySettingsExtension::KeyFunction::LastCallReply;\n  case KeyFunction::ChannelType:       return AnytoneKeySettingsExtension::KeyFunction::ChannelType;\n  case KeyFunction::SimplexRepeater:   return AnytoneKeySettingsExtension::KeyFunction::SimplexRepeater;\n  case KeyFunction::Ranging:           return AnytoneKeySettingsExtension::KeyFunction::Ranging;\n  case KeyFunction::ChannelRanging:    return AnytoneKeySettingsExtension::KeyFunction::ChannelRanging;\n  case KeyFunction::MaxVolume:         return AnytoneKeySettingsExtension::KeyFunction::MaxVolume;\n  case KeyFunction::Slot:              return AnytoneKeySettingsExtension::KeyFunction::Slot;\n  case KeyFunction::Squelch:           return AnytoneKeySettingsExtension::KeyFunction::Squelch;\n  case KeyFunction::Roaming:           return AnytoneKeySettingsExtension::KeyFunction::Roaming;\n  case KeyFunction::Zone:              return AnytoneKeySettingsExtension::KeyFunction::Zone;\n  case KeyFunction::RoamingSet:        return AnytoneKeySettingsExtension::KeyFunction::RoamingSet;\n  case KeyFunction::Mute:              return AnytoneKeySettingsExtension::KeyFunction::Mute;\n  case KeyFunction::CtcssDcsSet:       return AnytoneKeySettingsExtension::KeyFunction::CtcssDcsSet;\n  case KeyFunction::APRSType:          return AnytoneKeySettingsExtension::KeyFunction::APRSTypeSwitch;\n  case KeyFunction::APRSSet:           return AnytoneKeySettingsExtension::KeyFunction::APRSSet;\n  case KeyFunction::DIMShut:           return AnytoneKeySettingsExtension::KeyFunction::DIMShut;\n  case KeyFunction::GPSToggle:         return AnytoneKeySettingsExtension::KeyFunction::GPS;\n  case KeyFunction::SatPredict:        return AnytoneKeySettingsExtension::KeyFunction::SatPredict;\n  default:                             return AnytoneKeySettingsExtension::KeyFunction::Off;\n  }\n}\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UVCodeplug::GeneralSettingsElement\n * ********************************************************************************************* */\nDMR6X2UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : D868UVCodeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : D868UVCodeplug::GeneralSettingsElement(ptr, GeneralSettingsElement::size())\n{\n  // pass...\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::idleChannelTone() const {\n  return getUInt8(Offset::idleChannelTone());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableIdleChannelTone(bool enable) {\n  return setUInt8(Offset::idleChannelTone(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::transmitTimeout() const {\n  return ((unsigned)getUInt8(Offset::transmitTimeout()))*30;\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setTransmitTimeout(unsigned tot) {\n  setUInt8(Offset::transmitTimeout(), tot/30);\n}\n\nAnytoneDisplaySettingsExtension::Language\nDMR6X2UVCodeplug::GeneralSettingsElement::language() const {\n  return (AnytoneDisplaySettingsExtension::Language)getUInt8(Offset::language());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setLanguage(AnytoneDisplaySettingsExtension::Language lang) {\n  setUInt8(Offset::language(), (unsigned)lang);\n}\n\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::vfoFrequencyStep() const {\n  switch (getUInt8(Offset::vfoFrequencyStep())) {\n  case FREQ_STEP_2_5kHz: return Frequency::fromkHz(2.5);\n  case FREQ_STEP_5kHz: return Frequency::fromkHz(5);\n  case FREQ_STEP_6_25kHz: return Frequency::fromkHz(6.25);\n  case FREQ_STEP_10kHz: return Frequency::fromkHz(10);\n  case FREQ_STEP_12_5kHz: return Frequency::fromkHz(12.5);\n  case FREQ_STEP_20kHz: return Frequency::fromkHz(20);\n  case FREQ_STEP_25kHz: return Frequency::fromkHz(25);\n  case FREQ_STEP_50kHz: return Frequency::fromkHz(50);\n  }\n  return Frequency::fromkHz(2.5);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setVFOFrequencyStep(Frequency freq) {\n  if (freq.inkHz() <= 2.5)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_2_5kHz);\n  else if (freq.inkHz() <= 5)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_5kHz);\n  else if (freq.inkHz() <= 6.25)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_6_25kHz);\n  else if (freq.inkHz() <= 10)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_10kHz);\n  else if (freq.inkHz() <= 12.5)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_12_5kHz);\n  else if (freq.inkHz() <= 20)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_20kHz);\n  else if (freq.inkHz() <= 25)\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_25kHz);\n  else\n    setUInt8(Offset::vfoFrequencyStep(), FREQ_STEP_50kHz);\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyAShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyAShort()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyAShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyBShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBShort()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyCShort() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCShort()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCShort(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKey1Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Short()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKey2Short() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Short()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Short(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyALong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyALong()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyALong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyBLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyBLong()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyBLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKeyCLong() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKeyCLong()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKeyCLong(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKey1Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1Long()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey1Long(), KeyFunction::encode(func));\n}\n\nAnytoneKeySettingsExtension::KeyFunction\nDMR6X2UVCodeplug::GeneralSettingsElement::funcKey2Long() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2Long()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) {\n  setUInt8(Offset::progFuncKey2Long(), KeyFunction::encode(func));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::vfoModeA() const {\n  return getUInt8(Offset::vfoModeA());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableVFOModeA(bool enable) {\n  setUInt8(Offset::vfoModeA(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::vfoModeB() const {\n  return getUInt8(Offset::vfoModeB());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableVFOModeB(bool enable) {\n  setUInt8(Offset::vfoModeB(), (enable ? 0x01 : 0x00));\n}\n\nAnytoneSettingsExtension::STEType\nDMR6X2UVCodeplug::GeneralSettingsElement::steType() const {\n  return (AnytoneSettingsExtension::STEType)getUInt8(Offset::steType());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setSTEType(AnytoneSettingsExtension::STEType type) {\n  setUInt8(Offset::steType(), (unsigned)type);\n}\ndouble\nDMR6X2UVCodeplug::GeneralSettingsElement::steFrequency() const {\n  switch ((STEFrequency)getUInt8(Offset::steFrequency())) {\n  case STEFrequency::Off: return 0;\n  case STEFrequency::Hz55_2: return 55.2;\n  case STEFrequency::Hz259_2: return 259.2;\n  }\n  return 0;\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setSTEFrequency(double freq) {\n  if (0 >= freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Off);\n  } else if (100 > freq) {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz55_2);\n  } else {\n    setUInt8(Offset::steFrequency(), (unsigned)STEFrequency::Hz259_2);\n  }\n}\n\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::groupCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::groupCallHangTime()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setGroupCallHangTime(Interval intv) {\n  setUInt8(Offset::groupCallHangTime(), intv.seconds());\n}\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::privateCallHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::privateCallHangTime()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setPrivateCallHangTime(Interval intv) {\n  setUInt8(Offset::privateCallHangTime(), intv.seconds());\n}\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::preWaveDelay() const {\n  return Interval::fromMilliseconds((unsigned)getUInt8(Offset::preWaveDelay())*20);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setPreWaveDelay(Interval intv) {\n  setUInt8(Offset::preWaveDelay(), intv.milliseconds()/20);\n}\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::wakeHeadPeriod() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::wakeHeadPeriod()))*20);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setWakeHeadPeriod(Interval intv) {\n  setUInt8(Offset::wakeHeadPeriod(), intv.milliseconds()/20);\n}\n\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::wfmChannelIndex() const {\n  return getUInt8(Offset::wfmChannelIndex());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setWFMChannelIndex(unsigned idx) {\n  setUInt8(Offset::wfmChannelIndex(), idx);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::wfmVFOEnabled() const {\n  return getUInt8(Offset::wfmVFOEnabled());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableWFMVFO(bool enable) {\n  setUInt8(Offset::wfmVFOEnabled(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::dtmfToneDuration() const {\n  switch (getUInt8(Offset::dtmfToneDuration())) {\n  case DTMF_DUR_50ms:  return 50;\n  case DTMF_DUR_100ms: return 100;\n  case DTMF_DUR_200ms: return 200;\n  case DTMF_DUR_300ms: return 300;\n  case DTMF_DUR_500ms: return 500;\n  }\n  return 50;\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDTMFToneDuration(unsigned ms) {\n  if (ms<=50) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_50ms);\n  } else if (ms<=100) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_100ms);\n  } else if (ms<=200) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_200ms);\n  } else if (ms<=300) {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_300ms);\n  } else {\n    setUInt8(Offset::dtmfToneDuration(), DTMF_DUR_500ms);\n  }\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::manDown() const {\n  return getUInt8(Offset::manDown());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableManDown(bool enable) {\n  setUInt8(Offset::manDown(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::wfmMonitor() const {\n  return getUInt8(Offset::wfmMonitor());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableWFMMonitor(bool enable) {\n  setUInt8(Offset::wfmMonitor(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::tbstFrequency() const {\n  switch ((TBSTFrequency)getUInt8(Offset::tbstFrequency())) {\n  case TBSTFrequency::Hz1000: return Frequency::fromHz(1000);\n  case TBSTFrequency::Hz1450: return Frequency::fromHz(1450);\n  case TBSTFrequency::Hz1750: return Frequency::fromHz(1750);\n  case TBSTFrequency::Hz2100: return Frequency::fromHz(2100);\n  }\n  return Frequency::fromHz(1750);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setTBSTFrequency(Frequency freq) {\n  if (1000 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1000);\n  } else if (1450 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1450);\n  } else if (1750 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  } else if (2100 == freq.inHz()) {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz2100);\n  } else {\n    setUInt8(Offset::tbstFrequency(), (unsigned)TBSTFrequency::Hz1750);\n  }\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::proMode() const {\n  return getUInt8(Offset::proMode());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableProMode(bool enable) {\n  setUInt8(Offset::proMode(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::filterOwnID() const {\n  return getUInt8(Offset::filterOwnID());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableFilterOwnID(bool enable) {\n  setUInt8(Offset::filterOwnID(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::keyToneEnabled() const {\n  return 0x00 != getUInt8(Offset::enableKeyTone());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableKeyTone(bool enable) {\n  setUInt8(Offset::enableKeyTone(), enable ? 0x01 : 0x00);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::remoteStunKill() const {\n  return getUInt8(Offset::remoteStunKill());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableRemoteStunKill(bool enable) {\n  setUInt8(Offset::remoteStunKill(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::remoteMonitor() const {\n  return getUInt8(Offset::remoteMonitor());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableRemoteMonitor(bool enable) {\n  setUInt8(Offset::remoteMonitor(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::selectTXContactEnabled() const {\n  return 0x01 == getUInt8(Offset::selectTXContact());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableSelectTXContact(bool enable) {\n  setUInt8(Offset::selectTXContact(), enable ? 0x01 : 0x00);\n}\n\nAnytoneDMRSettingsExtension::SlotMatch\nDMR6X2UVCodeplug::GeneralSettingsElement::monitorSlotMatch() const {\n  return (AnytoneDMRSettingsExtension::SlotMatch)getUInt8(Offset::monSlotMatch());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match) {\n  setUInt8(Offset::monSlotMatch(), (unsigned)match);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::monitorColorCodeMatch() const {\n  return getUInt8(Offset::monColorCodeMatch());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorColorCodeMatch(bool enable) {\n  setUInt8(Offset::monColorCodeMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::monitorIDMatch() const {\n  return getUInt8(Offset::monIDMatch());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorIDMatch(bool enable) {\n  setUInt8(Offset::monIDMatch(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::monitorTimeSlotHold() const {\n  return getUInt8(Offset::monTimeSlotHold());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorTimeSlotHold(bool enable) {\n  setUInt8(Offset::monTimeSlotHold(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::manDownDelay() const {\n  return Interval::fromSeconds(getUInt8(Offset::manDownDelay()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setManDownDelay(Interval sec) {\n  setUInt8(Offset::manDownDelay(), sec.seconds());\n}\n\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::fmCallHold() const {\n  return getUInt8(Offset::fmCallHold());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setFMCallHold(unsigned sec) {\n  setUInt8(Offset::fmCallHold(), sec);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::gpsMessageEnabled() const {\n  return getUInt8(Offset::enableGPSMessage());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableGPSMessage(bool enable) {\n  setUInt8(Offset::enableGPSMessage(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::maintainCallChannel() const {\n  return getUInt8(Offset::maintainCallChannel());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableMaintainCallChannel(bool enable) {\n  setUInt8(Offset::maintainCallChannel(), (enable ? 0x01 : 0x00));\n}\n\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::priorityZoneAIndex() const {\n  return getUInt8(Offset::priorityZoneA());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setPriorityZoneAIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneA(), idx);\n}\nunsigned\nDMR6X2UVCodeplug::GeneralSettingsElement::priorityZoneBIndex() const {\n  return getUInt8(Offset::priorityZoneB());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setPriorityZoneBIndex(unsigned idx) {\n  setUInt8(Offset::priorityZoneB(), idx);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::smsConfirmEnabled() const {\n  return 0x01 == getUInt8(Offset::smsConfirm());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableSMSConfirm(bool enable) {\n  setUInt8(Offset::smsConfirm(), enable ? 0x01 : 0x00);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::simplexRepeaterEnabled() const {\n  return 0x01 == getUInt8(Offset::simplexRepEnable());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableSimplexRepeater(bool enable) {\n  setUInt8(Offset::simplexRepEnable(), enable ? 0x01 : 0x00);\n}\n\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::gpsUpdatePeriod() const {\n  return Interval::fromSeconds(getUInt8(Offset::gpsUpdatePeriod()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setGPSUpdatePeriod(Interval intv) {\n  setUInt8(Offset::gpsUpdatePeriod(), intv.seconds());\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::monitorSimplexRepeaterEnabled() const {\n  return 0x01 == getUInt8(Offset::simplxRepSpeaker());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableMonitorSimplexRepeater(bool enable) {\n  setUInt8(Offset::simplxRepSpeaker(), enable ? 0x01 : 0x00);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::showCurrentContact() const {\n  return getUInt8(Offset::showContact());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableShowCurrentContact(bool enable) {\n  setUInt8(Offset::showContact(), (enable ? 0x01 : 0x00));\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::keyToneLevelAdjustable() const {\n  return keyToneLevel().isNull();\n}\nLevel\nDMR6X2UVCodeplug::GeneralSettingsElement::keyToneLevel() const {\n  return  Level::fromValue(getUInt8(Offset::keyToneLevel()), Limit::keyTone());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setKeyToneLevel(Level level) {\n  setUInt8(Offset::keyToneLevel(), level.mapTo(Limit::keyTone()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setKeyToneLevelAdjustable() {\n  setUInt8(Offset::keyToneLevel(), 0);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::knobLock() const {\n  return getBit(Offset::knobLock());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableKnobLock(bool enable) {\n  setBit(Offset::knobLock(), enable);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::keypadLock() const {\n  return getBit(Offset::keypadLock());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableKeypadLock(bool enable) {\n  setBit(Offset::keypadLock(), enable);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::sidekeysLock() const {\n  return getBit(Offset::sideKeyLock());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableSidekeysLock(bool enable) {\n  setBit(Offset::sideKeyLock(), enable);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::keyLockForced() const {\n  return getBit(Offset::forceKeyLock());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableKeyLockForced(bool enable) {\n  setBit(Offset::forceKeyLock(), enable);\n}\n\n\nAnytoneRepeaterSettingsExtension::TimeSlot\nDMR6X2UVCodeplug::GeneralSettingsElement::simplexRepeaterTimeslot() const {\n  switch ((RepeaterTimeSlot)getUInt8(Offset::simplxRepSlot())) {\n  case RepeaterTimeSlot::TS1: return AnytoneRepeaterSettingsExtension::TimeSlot::TS1;\n  case RepeaterTimeSlot::TS2: return AnytoneRepeaterSettingsExtension::TimeSlot::TS2;\n  case RepeaterTimeSlot::Channel: return AnytoneRepeaterSettingsExtension::TimeSlot::Channel;\n  }\n  return AnytoneRepeaterSettingsExtension::TimeSlot::Channel;\n}\n\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setSimplexRepeaterTimeslot(AnytoneRepeaterSettingsExtension::TimeSlot slot) {\n  switch (slot) {\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS1:\n    setUInt8(Offset::simplxRepSlot(), (unsigned int) RepeaterTimeSlot::TS1);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::TS2:\n    setUInt8(Offset::simplxRepSlot(), (unsigned int) RepeaterTimeSlot::TS2);\n    break;\n  case AnytoneRepeaterSettingsExtension::TimeSlot::Channel:\n    setUInt8(Offset::simplxRepSlot(), (unsigned int) RepeaterTimeSlot::Channel);\n    break;\n  default:\n    setUInt8(Offset::simplxRepSlot(), (unsigned int) RepeaterTimeSlot::Channel);\n    break;\n  }\n}\n\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::showLastHeard() const {\n  return getUInt8(Offset::showLastHeard());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableShowLastHeard(bool enable) {\n  setUInt8(Offset::showLastHeard(), (enable ? 0x01 : 0x00));\n}\n\n\nSMSExtension::Format\nDMR6X2UVCodeplug::GeneralSettingsElement::smsFormat() const {\n  switch ((SMSFormat) getUInt8(Offset::smsFormat())) {\n  case SMSFormat::Motorola: return SMSExtension::Format::Motorola;\n  case SMSFormat::Hytera: return SMSExtension::Format::Hytera;\n  case SMSFormat::DMR: return SMSExtension::Format::DMR;\n  }\n\n  return SMSExtension::Format::Motorola;\n}\n\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setSMSFormat(SMSExtension::Format format) {\n  switch (format) {\n  case SMSExtension::Format::Motorola:\n    setUInt8(Offset::smsFormat(), (unsigned int)SMSFormat::Motorola);\n    break;\n  case SMSExtension::Format::Hytera:\n    setUInt8(Offset::smsFormat(), (unsigned int)SMSFormat::Hytera);\n    break;\n  case SMSExtension::Format::DMR:\n    setUInt8(Offset::smsFormat(), (unsigned int)SMSFormat::DMR);\n    break;\n  }\n}\n\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::gpsUnitsImperial() const {\n  return getUInt8(Offset::gpsUnits());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableGPSUnitsImperial(bool enable) {\n  setUInt8(Offset::gpsUnits(), (enable ? 0x01 : 0x00));\n}\n\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinVHF())*10);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinVHF(), freq.inHz()/10);\n}\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyVHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxVHF())*10);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyVHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxVHF(), freq.inHz()/10);\n}\n\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMinFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMinUHF())*10);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMinFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMinUHF(), freq.inHz()/10);\n}\nFrequency\nDMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterMaxFrequencyUHF() const {\n  return Frequency::fromHz(getUInt32_le(Offset::autoRepMaxUHF())*10);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterMaxFrequencyUHF(Frequency freq) {\n  setUInt32_le(Offset::autoRepMaxUHF(), freq.inHz()/10);\n}\n\nAnytoneAutoRepeaterSettingsExtension::Direction\nDMR6X2UVCodeplug::GeneralSettingsElement::autoRepeaterDirectionB() const {\n  return (AnytoneAutoRepeaterSettingsExtension::Direction)getUInt8(Offset::autoRepeaterDirB());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) {\n  setUInt8(Offset::autoRepeaterDirB(), (uint8_t)dir);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::fmSendIDAndContact() const {\n  return 0 != getUInt8(Offset::fmSendIDAndContact());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableFMSendIDAndContact(bool enable) {\n  setUInt8(Offset::fmSendIDAndContact(), enable ? 0x01 : 0x00);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultChannel() const {\n  return 0x01 == getUInt8(Offset::defaultChannels());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableDefaultChannel(bool enable) {\n  setUInt8(Offset::defaultChannels(), enable ? 0x01 : 0x00);\n}\n\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultZoneIndexA() const {\n  return getUInt8(Offset::defaultZoneA());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexA(unsigned int index) {\n  setUInt8(Offset::defaultZoneA(), index);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelAIsVFO() const {\n  return 0xff == defaultChannelAIndex();\n}\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelAIndex() const {\n  return getUInt8(Offset::defaultChannelA());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelAIndex(unsigned int index) {\n  return setUInt8(Offset::defaultChannelA(), index);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelAToVFO() {\n  setDefaultChannelAIndex(0xff);\n}\n\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultZoneIndexB() const {\n  return getUInt8(Offset::defaultZoneB());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultZoneIndexB(unsigned int index) {\n  setUInt8(Offset::defaultZoneB(), index);\n}\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelBIsVFO() const {\n  return 0xff == defaultChannelBIndex();\n}\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::defaultChannelBIndex() const {\n  return getUInt8(Offset::defaultChannelB());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelBIndex(unsigned int index) {\n  return setUInt8(Offset::defaultChannelB(), index);\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setDefaultChannelBToVFO() {\n  setDefaultChannelBIndex(0xff);\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::keepLastCaller() const {\n  return getUInt8(Offset::keepLastCaller());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::enableKeepLastCaller(bool enable) {\n  setUInt8(Offset::keepLastCaller(), (enable ? 0x01 : 0x00));\n}\n\n\nInterval\nDMR6X2UVCodeplug::GeneralSettingsElement::rxBacklightDuration() const {\n  auto seconds = getUInt8(Offset::rxBacklightDuration());\n  if (0 == seconds)\n    return Interval::infinity();\n  return Interval::fromSeconds(seconds);\n}\n\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setRXBacklightDuration(Interval dur) {\n  if (dur.isFinite() || dur > Interval::fromSeconds(30))\n    setUInt8(Offset::rxBacklightDuration(), 0);\n  setUInt8(Offset::rxBacklightDuration(), dur.seconds());\n}\n\n\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::GeneralSettingsElement::standbyBackgroundColor() const {\n  return BackgroundColor::decode(getUInt8(Offset::standbyBackground()));\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::standbyBackground(), BackgroundColor::encode(color));\n}\n\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::manualDialedGroupCallHangTime() const {\n  return getUInt8(Offset::manGrpCallHangTime());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setManualDialedGroupCallHangTime(unsigned int dur) {\n  setUInt8(Offset::manGrpCallHangTime(), dur);\n}\nunsigned int\nDMR6X2UVCodeplug::GeneralSettingsElement::manualDialedPrivateCallHangTime() const {\n  return getUInt8(Offset::manPrvCallHangTime());\n}\nvoid\nDMR6X2UVCodeplug::GeneralSettingsElement::setManualDialedPrivateCallHangTime(unsigned int dur) {\n  setUInt8(Offset::manPrvCallHangTime(), dur);\n}\n\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  if (! D868UVCodeplug::GeneralSettingsElement::fromConfig(flags, ctx, err))\n    return false;\n\n  enableGPSUnitsImperial(GNSSSettings::Units::Archaic == ctx.config()->settings()->gnss()->units());\n  setGroupCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n  setPrivateCallHangTime(ctx.config()->settings()->dmr()->privateCallHangTime());\n  setPreWaveDelay(ctx.config()->settings()->dmr()->preamble());\n  setSMSFormat(ctx.config()->smsExtension()->format());\n\n  // Encode tone settings\n  setKeyToneLevel(ctx.config()->settings()->tone()->keyToneVolume());\n\n  // apply DMR-6X2UV specific settings.\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Encode boot settings\n  if (ext->bootSettings()->priorityZoneA()->isNull())\n    setPriorityZoneAIndex(0xff);\n  else\n    setPriorityZoneAIndex(ctx.index(ext->bootSettings()->priorityZoneA()->as<Zone>()));\n  if (ext->bootSettings()->priorityZoneB()->isNull())\n    setPriorityZoneBIndex(0xff);\n  else\n    setPriorityZoneBIndex(ctx.index(ext->bootSettings()->priorityZoneB()->as<Zone>()));\n\n  // Encode key settings\n  enableKnobLock(ext->keySettings()->knobLockEnabled());\n  enableKeypadLock(ext->keySettings()->keypadLockEnabled());\n  enableSidekeysLock(ext->keySettings()->sideKeysLockEnabled());\n  enableKeyLockForced(ext->keySettings()->forcedKeyLockEnabled());\n\n  // Encode display settings\n  setCallDisplayColor(ext->displaySettings()->callColor());\n  setStandbyBackgroundColor(ext->displaySettings()->standbyBackgroundColor());\n  setLanguage(ext->displaySettings()->language());\n  enableShowCurrentContact(ext->displaySettings()->showContact());\n  enableShowLastHeard(ext->displaySettings()->showLastHeardEnabled());\n  setRXBacklightDuration(ext->displaySettings()->backlightDurationRX());\n\n  // Encode auto-repeater settings\n  setAutoRepeaterDirectionB(ext->autoRepeaterSettings()->directionB());\n  setAutoRepeaterMinFrequencyVHF(ext->autoRepeaterSettings()->vhfMin());\n  setAutoRepeaterMaxFrequencyVHF(ext->autoRepeaterSettings()->vhfMax());\n  setAutoRepeaterMinFrequencyUHF(ext->autoRepeaterSettings()->uhfMin());\n  setAutoRepeaterMaxFrequencyUHF(ext->autoRepeaterSettings()->uhfMax());\n\n  // Encode DMR settings\n  setWakeHeadPeriod(ext->dmrSettings()->wakeHeadPeriod());\n  enableFilterOwnID(ext->dmrSettings()->filterOwnIDEnabled());\n  setMonitorSlotMatch(ext->dmrSettings()->monitorSlotMatch());\n  enableMonitorColorCodeMatch(ext->dmrSettings()->monitorColorCodeMatchEnabled());\n  enableMonitorIDMatch(ext->dmrSettings()->monitorIDMatchEnabled());\n  enableMonitorTimeSlotHold(ext->dmrSettings()->monitorTimeSlotHoldEnabled());\n\n  // Encode GPS settings.\n  setGPSTimeZone(ext->gpsSettings()->timeZone());\n  enableGPSMessage(ext->gpsSettings()->positionReportingEnabled());\n  setGPSUpdatePeriod(ext->gpsSettings()->updatePeriod());\n\n  // Encode other settings\n  enableKeepLastCaller(ext->keepLastCallerEnabled());\n  setVFOFrequencyStep(ext->vfoStep());\n  setSTEType(ext->steType());\n  setSTEFrequency(ext->steFrequency());\n  setTBSTFrequency(ext->tbstFrequency());\n  enableProMode(ext->proModeEnabled());\n  enableMaintainCallChannel(ext->maintainCallChannelEnabled());\n\n  // Apply simplex repeater settings\n  enableSimplexRepeater(ext->repeaterSettings()->enabled());\n  enableMonitorSimplexRepeater(ext->repeaterSettings()->monitorEnabled());\n  setSimplexRepeaterTimeslot(ext->repeaterSettings()->timeSlot());\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! D868UVCodeplug::GeneralSettingsElement::updateConfig(ctx, err))\n    return false;\n\n  ctx.config()->settings()->gnss()->setUnits(\n        this->gpsUnitsImperial() ? GNSSSettings::Units::Archaic :\n                                   GNSSSettings::Units::Metric);\n\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(this->groupCallHangTime());\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(this->privateCallHangTime());\n  ctx.config()->settings()->dmr()->setPreamble(this->preWaveDelay());\n  ctx.config()->smsExtension()->setFormat(this->smsFormat());\n\n  // Decode tone settings\n  ctx.config()->settings()->tone()->setKeyToneVolume(keyToneLevel());\n\n  // Get or add settings extension\n  AnytoneSettingsExtension *ext = nullptr;\n  if (ctx.config()->settings()->anytoneExtension()) {\n    ext = ctx.config()->settings()->anytoneExtension();\n  } else {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Decode key settings\n  ext->keySettings()->enableKnobLock(this->knobLock());\n  ext->keySettings()->enableKeypadLock(this->keypadLock());\n  ext->keySettings()->enableSideKeysLock(this->sidekeysLock());\n  ext->keySettings()->enableForcedKeyLock(this->keyLockForced());\n\n  // Decode display settings\n  ext->displaySettings()->setCallColor(this->callDisplayColor());\n  ext->displaySettings()->setLanguage(this->language());\n  ext->displaySettings()->enableShowContact(this->showCurrentContact());\n  ext->displaySettings()->enableShowLastHeard(this->showLastHeard());\n  ext->displaySettings()->setBacklightDurationRX(this->rxBacklightDuration());\n  ext->displaySettings()->setStandbyBackgroundColor(this->standbyBackgroundColor());\n\n  // Decode auto-repeater settings\n  ext->autoRepeaterSettings()->setDirectionB(autoRepeaterDirectionB());\n  ext->autoRepeaterSettings()->setVHFMin(this->autoRepeaterMinFrequencyVHF());\n  ext->autoRepeaterSettings()->setVHFMax(this->autoRepeaterMaxFrequencyVHF());\n  ext->autoRepeaterSettings()->setUHFMin(this->autoRepeaterMinFrequencyUHF());\n  ext->autoRepeaterSettings()->setUHFMax(this->autoRepeaterMaxFrequencyUHF());\n\n  // Encode dmr settings\n  ext->dmrSettings()->setWakeHeadPeriod(this->wakeHeadPeriod());\n  ext->dmrSettings()->enableFilterOwnID(this->filterOwnID());\n  ext->dmrSettings()->setMonitorSlotMatch(this->monitorSlotMatch());\n  ext->dmrSettings()->enableMonitorColorCodeMatch(this->monitorColorCodeMatch());\n  ext->dmrSettings()->enableMonitorIDMatch(this->monitorIDMatch());\n  ext->dmrSettings()->enableMonitorTimeSlotHold(this->monitorTimeSlotHold());\n\n  // Encode GPS settings\n  ext->gpsSettings()->setTimeZone(this->gpsTimeZone());\n  ext->gpsSettings()->enablePositionReporting(this->gpsMessageEnabled());\n  ext->gpsSettings()->setUpdatePeriod(this->gpsUpdatePeriod());\n\n  // Decode other settings\n  ext->enableKeepLastCaller(this->keepLastCaller());\n  ext->setVFOStep(this->vfoFrequencyStep());\n  ext->setSTEType(this->steType());\n  ext->setSTEFrequency(this->steFrequency());\n  ext->setTBSTFrequency(this->tbstFrequency());\n  ext->enableProMode(this->proMode());\n  ext->enableMaintainCallChannel(this->maintainCallChannel());\n\n  // Decode simplex-repeater feature.\n  ext->repeaterSettings()->enable(this->simplexRepeaterEnabled());\n  ext->repeaterSettings()->enableMonitor(this->monitorSimplexRepeaterEnabled());\n  ext->repeaterSettings()->setTimeSlot(this->simplexRepeaterTimeslot());\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UVCodeplug::ExtendedSettingsElement\n * ********************************************************************************************* */\nDMR6X2UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::ExtendedSettingsElement::ExtendedSettingsElement(uint8_t *ptr)\n  : AnytoneCodeplug::ExtendedSettingsElement(ptr, ExtendedSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::clear() {\n  Codeplug::Element::clear();\n}\n\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::sendTalkerAlias() const {\n  return 0x01 == getUInt8(Offset::sendTalkerAlias());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::enableSendTalkerAlias(bool enable) {\n  setUInt8(Offset::sendTalkerAlias(), enable ? 0x01 : 0x00);\n}\n\nAnytoneDMRSettingsExtension::TalkerAliasSource\nDMR6X2UVCodeplug::ExtendedSettingsElement::talkerAliasSource() const {\n  return (AnytoneDMRSettingsExtension::TalkerAliasSource) getUInt8(Offset::talkerAliasDisplay());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource source) {\n  setUInt8(Offset::talkerAliasDisplay(), (uint8_t)source);\n}\n\n\nDMRSettings::TalkerAliasEncoding\nDMR6X2UVCodeplug::ExtendedSettingsElement::talkerAliasEncoding() const {\n  switch ((TalkerAliasEncoding) getUInt8(Offset::talkerAliasEncoding())) {\n  case TalkerAliasEncoding::ISO7: return DMRSettings::TalkerAliasEncoding::Iso7;\n  case TalkerAliasEncoding::ISO8: return DMRSettings::TalkerAliasEncoding::Iso8;\n  case TalkerAliasEncoding::Unicode: return DMRSettings::TalkerAliasEncoding::Unicode;\n  }\n\n  return DMRSettings::TalkerAliasEncoding::Iso8;\n}\n\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding encoding) {\n  switch (encoding) {\n  case DMRSettings::TalkerAliasEncoding::Iso7:\n    setUInt8(Offset::talkerAliasEncoding(), (uint8_t)TalkerAliasEncoding::ISO7);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Iso8:\n    setUInt8(Offset::talkerAliasEncoding(), (uint8_t)TalkerAliasEncoding::ISO8);\n    break;\n  case DMRSettings::TalkerAliasEncoding::Unicode:\n    setUInt8(Offset::talkerAliasEncoding(), (uint8_t)TalkerAliasEncoding::Unicode);\n    break;\n  }\n}\n\n\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::ExtendedSettingsElement::fontColor() const {\n  return FontColor::decode(getUInt8(Offset::fontColor()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setFontColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::fontColor(), FontColor::encode(color));\n}\n\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::customChannelBackgroundEnabled() const {\n  return 0x01 == getUInt8(Offset::customChannelBackground());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::enableCustomChannelBackground(bool enable) {\n  setUInt8(Offset::customChannelBackground(), enable ? 0x01 : 0x00);\n}\n\nunsigned int\nDMR6X2UVCodeplug::ExtendedSettingsElement::defaultRoamingZoneIndex() const {\n  return getUInt8(Offset::defaultRoamingZone());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setDefaultRoamingZoneIndex(unsigned int index) {\n  setUInt8(Offset::defaultRoamingZone(), index);\n}\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamingEnabled() const {\n  return 0x01 == getUInt8(Offset::roaming());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::enableAutoRoaming(bool enable) {\n  setUInt8(Offset::roaming(), enable ? 0x01 : 0x00);\n}\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckEnabled() const {\n  return 0x01 == getUInt8(Offset::repRangeCheck());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::enableRepeaterRangeCheck(bool enable) {\n  setUInt8(Offset::repRangeCheck(), enable ? 0x01 : 0x00);\n}\nAnytoneRoamingSettingsExtension::OutOfRangeAlert\nDMR6X2UVCodeplug::ExtendedSettingsElement::repeaterOutOfRangeAlert() const {\n  return (AnytoneRoamingSettingsExtension::OutOfRangeAlert) getUInt8(Offset::repRangeAlert());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterOutOfRangeAlert(AnytoneRoamingSettingsExtension::OutOfRangeAlert alert) {\n  setUInt8(Offset::repRangeAlert(), (uint8_t)alert);\n}\nunsigned int\nDMR6X2UVCodeplug::ExtendedSettingsElement::repeaterCheckNumNotifications() const {\n  return getUInt8(Offset::repRangeReminder())+1;\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterCheckNumNotifications(unsigned int n) {\n  n = Limit::repRangeReminder().map(n);\n  setUInt8(Offset::repRangeReminder(), n-1);\n}\nInterval\nDMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckInterval() const {\n  return Interval::fromSeconds((1+getUInt8(Offset::rangeCheckInterval()))*5);\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterRangeCheckInterval(Interval intv) {\n  intv = Limit::rangeCheckInterval().map(intv);\n  setUInt8(Offset::rangeCheckInterval(), intv.seconds()/5-1);\n}\nunsigned int\nDMR6X2UVCodeplug::ExtendedSettingsElement::repeaterRangeCheckCount() const {\n  return getUInt8(Offset::rangeCheckCount())+3;\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRepeaterRangeCheckCount(unsigned int n) {\n  n = Limit::repeaterReconnections().map(n);\n  setUInt8(Offset::rangeCheckCount(), n-3);\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nDMR6X2UVCodeplug::ExtendedSettingsElement::roamingStartCondition() const {\n  return (AnytoneRoamingSettingsExtension::RoamStart) getUInt8(Offset::roamStartCondition());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond) {\n  setUInt8(Offset::roamStartCondition(), (uint8_t)cond);\n}\n\nInterval\nDMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamPeriod() const {\n  return Interval::fromMinutes(getUInt8(Offset::autoRoamPeriod()) + 1);\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setAutoRoamPeriod(Interval minutes) {\n  minutes = Limit::autoRoamingInterval().map(minutes);\n  setUInt8(Offset::autoRoamPeriod(), minutes.minutes()-1);\n}\n\nInterval\nDMR6X2UVCodeplug::ExtendedSettingsElement::autoRoamDelay() const {\n  return Interval::fromSeconds(getUInt8(Offset::autoRoamDelay()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setAutoRoamDelay(Interval sec) {\n  sec = Limit::autoRoamDelay().map(sec);\n  setUInt8(Offset::autoRoamDelay(), sec.seconds());\n}\n\nAnytoneRoamingSettingsExtension::RoamStart\nDMR6X2UVCodeplug::ExtendedSettingsElement::roamingReturnCondition() const {\n  return (AnytoneRoamingSettingsExtension::RoamStart) getUInt8(Offset::roamReturnCondition());\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setRoamingReturnCondition(AnytoneRoamingSettingsExtension::RoamStart cond) {\n  setUInt8(Offset::roamReturnCondition(), (uint8_t)cond);\n}\n\nInterval\nDMR6X2UVCodeplug::ExtendedSettingsElement::muteTimer() const {\n  return Interval::fromMinutes(getUInt8(Offset::muteDelay()) + 1);\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setMuteTimer(Interval minutes) {\n  minutes = Limit::muteTimer().map(minutes);\n  setUInt8(Offset::muteDelay(), minutes.minutes()-1);\n}\n\nAnytoneDMRSettingsExtension::EncryptionType\nDMR6X2UVCodeplug::ExtendedSettingsElement::encryptionType() const {\n\n  return (0 == getUInt8(0x0011)) ? AnytoneDMRSettingsExtension::EncryptionType::DMR :\n                                   AnytoneDMRSettingsExtension::EncryptionType::AES;\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setEncryptionType(AnytoneDMRSettingsExtension::EncryptionType type) {\n  switch (type) {\n  case AnytoneDMRSettingsExtension::EncryptionType::DMR: setUInt8(Offset::encryptionType(), 0x00); break;\n  case AnytoneDMRSettingsExtension::EncryptionType::AES: setUInt8(Offset::encryptionType(), 0x01); break;\n  }\n}\n\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::ExtendedSettingsElement::zoneANameColor() const {\n  return Color::decode(getUInt8(Offset::zoneANameColor()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setZoneANameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneANameColor(), Color::encode(color));\n}\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::ExtendedSettingsElement::zoneBNameColor() const {\n  return Color::decode(getUInt8(Offset::zoneBNameColor()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::zoneBNameColor(), Color::encode(color));\n}\n\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::ExtendedSettingsElement::channelANameColor() const {\n  return Color::decode(getUInt8(Offset::channelANameColor()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setChannelANameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelANameColor(), Color::encode(color));\n}\nAnytoneDisplaySettingsExtension::Color\nDMR6X2UVCodeplug::ExtendedSettingsElement::channelBNameColor() const {\n  return Color::decode(getUInt8(Offset::channelBNameColor()));\n}\nvoid\nDMR6X2UVCodeplug::ExtendedSettingsElement::setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color) {\n  setUInt8(Offset::channelBNameColor(), Color::encode(color));\n}\n\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  if (! AnytoneCodeplug::ExtendedSettingsElement::fromConfig(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode extended settings for DMR-6X2UV codeplug.\";\n    return false;\n  }\n\n  // By default, use strong encryption\n  setEncryptionType(AnytoneDMRSettingsExtension::EncryptionType::AES);\n\n  enableSendTalkerAlias(ctx.config()->settings()->dmr()->sendTalkerAliasEnabled());\n  setTalkerAliasEncoding(ctx.config()->settings()->dmr()->talkerAliasEncoding());\n\n  // Encode device specific settings\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  // Encode DMR settings\n  setTalkerAliasSource(ext->dmrSettings()->talkerAliasSource());\n\n  // Encode audio settings\n  setMuteTimer(ext->audioSettings()->muteDelay());\n\n  // Encode display settings\n  setChannelANameColor(ext->displaySettings()->channelNameColor());\n  setFontColor(ext->displaySettings()->standbyTextColor());\n  enableCustomChannelBackground(ext->displaySettings()->customChannelBackground());\n\n  // Encode DMR settings\n  setEncryptionType(ext->dmrSettings()->encryption());\n\n  // Encode ranging/roaming settings.\n  enableAutoRoaming(ext->roamingSettings()->autoRoam());\n  setAutoRoamPeriod(ext->roamingSettings()->autoRoamPeriod());\n  setAutoRoamDelay(ext->roamingSettings()->autoRoamDelay());\n  enableRepeaterRangeCheck(ext->roamingSettings()->repeaterRangeCheckEnabled());\n  setRepeaterRangeCheckInterval(ext->roamingSettings()->repeaterCheckInterval());\n  setRepeaterRangeCheckCount(ext->roamingSettings()->repeaterRangeCheckCount());\n  setRepeaterOutOfRangeAlert(ext->roamingSettings()->outOfRangeAlert());\n  setRoamingStartCondition(ext->roamingSettings()->roamingStartCondition());\n  setRoamingReturnCondition(ext->roamingSettings()->roamingReturnCondition());\n  setRepeaterCheckNumNotifications(ext->roamingSettings()->notificationCount());\n  if (! ext->roamingSettings()->defaultZone()->isNull())\n    setDefaultRoamingZoneIndex(\n          ctx.index(ext->roamingSettings()->defaultZone()->as<RoamingZone>()));\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::ExtendedSettingsElement::updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode extended settings for DMR-6X2UV codeplug\";\n    return false;\n  }\n\n  ctx.config()->settings()->dmr()->enableSendTalkerAlias(sendTalkerAlias());\n  ctx.config()->settings()->dmr()->setTalkerAliasEncoding(talkerAliasEncoding());\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext) {\n    ext = new AnytoneSettingsExtension();\n    ctx.config()->settings()->setAnytoneExtension(ext);\n  }\n\n  // Store DMR settings\n  ext->dmrSettings()->setTalkerAliasSource(talkerAliasSource());\n\n  // Decode audio settings\n  ext->audioSettings()->setMuteDelay(this->muteTimer());\n\n  // Decode display settings\n  ext->displaySettings()->setChannelNameColor(this->channelANameColor());\n  ext->displaySettings()->setStandbyTextColor(this->fontColor());\n  ext->displaySettings()->enableCustomChannelBackground(this->customChannelBackgroundEnabled());\n\n  // Decode DMR settings\n  ext->dmrSettings()->setEncryption(this->encryptionType());\n\n  // Decode ranging/roaming settings\n  ext->roamingSettings()->enableAutoRoam(this->autoRoamingEnabled());\n  ext->roamingSettings()->setAutoRoamPeriod(this->autoRoamPeriod());\n  ext->roamingSettings()->setAutoRoamDelay(this->autoRoamDelay());\n  ext->roamingSettings()->enableRepeaterRangeCheck(this->repeaterRangeCheckEnabled());\n  ext->roamingSettings()->setRepeaterCheckInterval(this->repeaterRangeCheckInterval());\n  ext->roamingSettings()->setRepeaterRangeCheckCount(this->repeaterRangeCheckCount());\n  ext->roamingSettings()->setOutOfRangeAlert(this->repeaterOutOfRangeAlert());\n  ext->roamingSettings()->setRoamingStartCondition(this->roamingStartCondition());\n  ext->roamingSettings()->setRoamingReturnCondition(this->roamingReturnCondition());\n  ext->roamingSettings()->setNotificationCount(this->repeaterCheckNumNotifications());\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::ExtendedSettingsElement::linkConfig(Context &ctx, const ErrorStack &err) {\n  if (! AnytoneCodeplug::ExtendedSettingsElement::linkConfig(ctx, err)) {\n    errMsg(err) << \"Cannot link extended settings for DMR-6X2UV codeplug\";\n    return false;\n  }\n\n  AnytoneSettingsExtension *ext = ctx.config()->settings()->anytoneExtension();\n  if (nullptr == ext)\n    return false;\n\n  if (ctx.has<RoamingZone>(defaultRoamingZoneIndex())) {\n    if (! ctx.has<RoamingZone>(this->defaultRoamingZoneIndex())) {\n      errMsg(err) << \"Cannot link roaming zone, index \" << this->defaultRoamingZoneIndex()\n                  << \" not defined.\";\n      return false;\n    }\n    ext->roamingSettings()->defaultZone()->set(ctx.get<RoamingZone>(this->defaultRoamingZoneIndex()));\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UVCodeplug::ChannelElement\n * ********************************************************************************************* */\nDMR6X2UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, unsigned size)\n  : AnytoneCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : AnytoneCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelElement::hasAESEncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::aesEncryptionKeyIndex());\n}\n\nunsigned int\nDMR6X2UVCodeplug::ChannelElement::aesEncryptionKeyIndex() const {\n  return getUInt8(Offset::aesEncryptionKeyIndex())-1;\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelElement::setAESEncryptionKeyIndex(unsigned int index) {\n  setUInt8(Offset::aesEncryptionKeyIndex(), index+1);\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelElement::clearAESEncryptionKeyIndex() {\n  setUInt8(Offset::aesEncryptionKeyIndex(), 0);\n}\n\n\nDMR6X2UVCodeplug::ChannelElement::DMREncryptionType\nDMR6X2UVCodeplug::ChannelElement::dmrEncryptionType() const {\n  return getBit(Offset::dmrEncryptionType()) ?\n        DMREncryptionType::Enhanced : DMREncryptionType::Basic;\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelElement::setDMREncryptionType(DMREncryptionType type) {\n  setBit(Offset::dmrEncryptionType(), DMREncryptionType::Enhanced == type);\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelElement::hasDMREncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::dmrEncryptionKeyIndex());\n}\n\nunsigned int\nDMR6X2UVCodeplug::ChannelElement::dmrEncryptionKeyIndex() const {\n  return getUInt8(Offset::dmrAPRSChannelIndex())-1;\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelElement::setDMREncryptionKeyIndex(unsigned int index) {\n  setUInt8(Offset::dmrEncryptionKeyIndex(), index+1);\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelElement::clearDMREncryptionKeyIndex() {\n  setUInt8(Offset::dmrEncryptionKeyIndex(), 0);\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelElement::hasScanListIndex() const {\n  return hasScanListIndex(0);\n}\nunsigned\nDMR6X2UVCodeplug::ChannelElement::scanListIndex() const {\n  return scanListIndex(0);\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::setScanListIndex(unsigned idx) {\n  setScanListIndex(0, idx);\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::clearScanListIndex() {\n  clearScanListIndex(0);\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::hasScanListIndex(unsigned int n) const {\n  return 0xff != scanListIndex(n);\n}\nunsigned int\nDMR6X2UVCodeplug::ChannelElement::scanListIndex(unsigned int n) const {\n  if (n >= Limit::scanListIndices())\n    return 0xff;\n  return getUInt8(Offset::scanListIndices() + n*Offset::betweenScanListIndices());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::setScanListIndex(unsigned int n, unsigned idx) {\n  if (n >= Limit::scanListIndices())\n    return;\n  setUInt8(Offset::scanListIndices() + n*Offset::betweenScanListIndices(), idx);\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::clearScanListIndex(unsigned int n) {\n  setScanListIndex(n, 0xff);\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::roamingEnabled() const {\n  // inverted\n  return ! getBit(Offset::roaming());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::enableRoaming(bool enable) {\n  // inverted\n  setBit(Offset::roaming(), !enable);\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::ranging() const {\n  return getBit(Offset::ranging());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::enableRanging(bool enable) {\n  return setBit(Offset::ranging(), enable);\n}\n\nunsigned int\nDMR6X2UVCodeplug::ChannelElement::dmrAPRSChannelIndex() const {\n  return getUInt8(Offset::dmrAPRSChannelIndex());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::setDMRAPRSChannelIndex(unsigned int idx) {\n  setUInt8(Offset::dmrAPRSChannelIndex(), std::min(APRSSettingsElement::Limit::dmrSystems(), idx));\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::dmrAPRSRXEnabled() const {\n  return getBit(Offset::dmrAPRSRXEnable());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::enableDMRARPSRX(bool enable) {\n  setBit(Offset::dmrAPRSRXEnable(), enable);\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::dmrAPRSPTTEnabled() const {\n  return getBit(Offset::dmrAPRSPTTEnable());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::enableDMRAPRSPTT(bool enable) {\n  setBit(Offset::dmrAPRSPTTEnable(), enable);\n}\n\nDMR6X2UVCodeplug::ChannelElement::FMAPRSPTTMode\nDMR6X2UVCodeplug::ChannelElement::fmAPRSPTTMode() const {\n  return (FMAPRSPTTMode)getUInt2(Offset::fmAPRSPTTMode());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::setFMAPRSPTTMode(FMAPRSPTTMode mode) {\n  setUInt2(Offset::fmAPRSPTTMode(), (unsigned int)mode);\n}\n\nDMR6X2UVCodeplug::ChannelElement::APRSType\nDMR6X2UVCodeplug::ChannelElement::aprsType() const {\n  return (APRSType) getUInt2(Offset::aprsType());\n}\nvoid\nDMR6X2UVCodeplug::ChannelElement::setAPRSType(APRSType aprstype) {\n  setUInt2(Offset::aprsType(), (unsigned int)aprstype);\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  if (! AnytoneCodeplug::ChannelElement::fromChannelObj(c, ctx))\n    return false;\n\n  if (const FMChannel *fm = c->as<FMChannel>()) {\n    if (! fm->aprsRef()->isNull()) {\n      setAPRSType(APRSType::FM);\n      if (auto ext = fm->anytoneChannelExtension()) {\n        switch (ext->aprsPTT()) {\n        case AnytoneChannelExtension::APRSPTT::Off: setFMAPRSPTTMode(FMAPRSPTTMode::Off); break;\n        case AnytoneChannelExtension::APRSPTT::Start: setFMAPRSPTTMode(FMAPRSPTTMode::Start); break;\n        case AnytoneChannelExtension::APRSPTT::End: setFMAPRSPTTMode(FMAPRSPTTMode::End); break;\n        }\n      }\n    }\n  } else if (const DMRChannel *dmr = c->as<DMRChannel>()) {\n    if (! dmr->aprsRef()->isNull()) {\n      if (dmr->aprsRef()->is<FMAPRSSystem>()) {\n        setAPRSType(APRSType::FM);\n        if (auto ext = dmr->anytoneChannelExtension()) {\n          switch (ext->aprsPTT()) {\n          case AnytoneChannelExtension::APRSPTT::Off: setFMAPRSPTTMode(FMAPRSPTTMode::Off); break;\n          case AnytoneChannelExtension::APRSPTT::Start: setFMAPRSPTTMode(FMAPRSPTTMode::Start); break;\n          case AnytoneChannelExtension::APRSPTT::End: setFMAPRSPTTMode(FMAPRSPTTMode::End); break;\n          }\n        }\n      } else if (DMRAPRSSystem *sys = dmr->aprsRef()->as<DMRAPRSSystem>()){\n        if (0 <= ctx.index(sys)) {\n          setAPRSType(APRSType::DMR);\n          setDMRAPRSChannelIndex(ctx.index(sys));\n          if (auto ext = dmr->anytoneChannelExtension())\n            enableDMRAPRSPTT(AnytoneChannelExtension::APRSPTT::Off != ext->aprsPTT());\n        }\n      }\n    }\n\n    // Handle encryption\n    clearAESEncryptionKeyIndex();\n    clearDMREncryptionKeyIndex();\n\n    if (CommercialChannelExtension *cex = dmr->commercialExtension()) {\n      // By default, we assume we have strong encryption unless otherwise set by AnyTone DMR extension.\n      bool hasStrongEncryption = (! ctx.config()->settings()->anytoneExtension()) ||\n          ( ctx.config()->settings()->anytoneExtension() &&\n            (AnytoneDMRSettingsExtension::EncryptionType::AES ==\n             ctx.config()->settings()->anytoneExtension()->dmrSettings()->encryption()) );\n      if (hasStrongEncryption && cex->encryptionKey() && cex->encryptionKey()->is<AESEncryptionKey>()) {\n        setAESEncryptionKeyIndex(ctx.index(cex->encryptionKey()));\n      } else if ((! hasStrongEncryption) && cex->encryptionKey() && cex->encryptionKey()->is<BasicEncryptionKey>()) {\n        setDMREncryptionType(DMREncryptionType::Basic);\n        setDMREncryptionKeyIndex(ctx.index(cex->encryptionKey()));\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx) const {\n  if (! AnytoneCodeplug::ChannelElement::linkChannelObj(c, ctx))\n    return false;\n  if (FMChannel *fm = c->as<FMChannel>()) {\n    auto ext = fm->anytoneChannelExtension();\n    if (nullptr == ext)\n      fm->setAnytoneChannelExtension(ext = new AnytoneFMChannelExtension());\n    if ((APRSType::FM == aprsType()) && ctx.count<FMAPRSSystem>()) {\n      switch (fmAPRSPTTMode()) {\n      case FMAPRSPTTMode::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break;\n      case FMAPRSPTTMode::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break;\n      case FMAPRSPTTMode::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break;\n      }\n      fm->setAPRS(ctx.get<FMAPRSSystem>(0));\n    }\n  } else if (DMRChannel *dmr = c->as<DMRChannel>()) {\n    auto ext = dmr->anytoneChannelExtension();\n    if (nullptr == ext)\n      dmr->setAnytoneChannelExtension(ext = new AnytoneDMRChannelExtension());\n    if ((APRSType::FM == aprsType()) && ctx.count<FMAPRSSystem>()) {\n      switch (fmAPRSPTTMode()) {\n      case FMAPRSPTTMode::Off: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Off); break;\n      case FMAPRSPTTMode::Start: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start); break;\n      case FMAPRSPTTMode::End: ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::End); break;\n      }\n      dmr->setAPRS(ctx.get<FMAPRSSystem>(0));\n    } else if ((APRSType::DMR == aprsType()) && ctx.has<DMRAPRSSystem>(dmrAPRSChannelIndex())) {\n      ext->setAPRSPTT(dmrAPRSPTTEnabled() ? AnytoneChannelExtension::APRSPTT::Start : AnytoneChannelExtension::APRSPTT::Off);\n      dmr->setAPRS(ctx.get<DMRAPRSSystem>(dmrAPRSChannelIndex()));\n    }\n\n    // By default, we assume we have strong encryption unless otherwise set by AnyTone DMR extension.\n    bool hasStrongEncryption = (! ctx.config()->settings()->anytoneExtension()) ||\n        ( ctx.config()->settings()->anytoneExtension() &&\n          (AnytoneDMRSettingsExtension::EncryptionType::AES ==\n           ctx.config()->settings()->anytoneExtension()->dmrSettings()->encryption()) );\n\n    if (hasStrongEncryption && hasAESEncryptionKeyIndex() && ctx.has<AESEncryptionKey>(aesEncryptionKeyIndex())) {\n      auto cex = dmr->commercialExtension();\n      if (nullptr == cex)\n        dmr->setCommercialExtension(cex = new CommercialChannelExtension());\n      cex->setEncryptionKey(ctx.get<AESEncryptionKey>(aesEncryptionKeyIndex()));\n    } else if ((!hasStrongEncryption) && hasDMREncryptionKeyIndex() &&\n               (DMREncryptionType::Basic == dmrEncryptionType()) &&\n               ctx.has<BasicEncryptionKey>(dmrEncryptionKeyIndex())) {\n      auto cex = dmr->commercialExtension();\n      if (nullptr == cex)\n        dmr->setCommercialExtension(cex = new CommercialChannelExtension());\n      cex->setEncryptionKey(ctx.get<BasicEncryptionKey>(dmrEncryptionKeyIndex()));\n    }\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DMR6X2UVCodeplug::ChannelExtensionElement\n * ******************************************************************************************** */\nDMR6X2UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::ChannelExtensionElement::ChannelExtensionElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelExtensionElement::clear() {\n  Element::clear();\n  memset(_data, 0, _size);\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelExtensionElement::hasARC4KeyIndex() const {\n  return 0 != getUInt8(Offset::arc4KeyIndex());\n}\n\nunsigned int\nDMR6X2UVCodeplug::ChannelExtensionElement::arc4KeyIndex() const {\n  return getUInt8(Offset::arc4KeyIndex())-1;\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelExtensionElement::setARC4KeyIndex(unsigned int idx) {\n  setUInt8(Offset::arc4KeyIndex(), idx+1);\n}\n\nvoid\nDMR6X2UVCodeplug::ChannelExtensionElement::clearARC4KeyIndex() {\n  setUInt8(Offset::arc4KeyIndex(), 0);\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelExtensionElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err) const {\n  if (auto dc = c->as<DMRChannel>()) {\n    if (! hasARC4KeyIndex())\n      return true;\n\n    if (! ctx.has<ARC4EncryptionKey>(arc4KeyIndex())) {\n      errMsg(err) << \"Cannot link channel '\" << c->name()\n                  << \"': Cannot resolve ARC4 key index \" << arc4KeyIndex() << \".\";\n      return false;\n    }\n\n    auto cc = dc->commercialExtension();\n    if (nullptr == cc)\n      dc->setCommercialExtension(cc = new CommercialChannelExtension());\n    if (cc->encryptionKeyRef()->isNull())\n      cc->setEncryptionKey(ctx.get<ARC4EncryptionKey>(arc4KeyIndex()));\n  }\n\n  return true;\n}\n\n\nbool\nDMR6X2UVCodeplug::ChannelExtensionElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (auto dc = c->as<DMRChannel>()) {\n    clearARC4KeyIndex();\n\n    if (nullptr == dc->commercialExtension())\n      return true;\n    auto cc = dc->commercialExtension();\n\n    if (cc->encryptionKeyRef()->isNull())\n      return true;\n\n    if (auto key = cc->encryptionKey()->as<ARC4EncryptionKey>())\n      setARC4KeyIndex(ctx.index(key));\n  }\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of DMR6X2UVCodeplug::AnalogAPRSSettingsElement\n * ******************************************************************************************** */\nDMR6X2UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr)\n  : Element(ptr, APRSSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::clear() {\n  memset(_data, 0x00, _size);\n  setUInt8(0x0000, 0xff);\n  setFMTXDelay(Interval::fromMilliseconds(60));\n  setUInt8(0x003d, 0x01); setUInt8(0x003e, 0x03); setUInt8(0x003f, 0xff);\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::isValid() const {\n  if (! Codeplug::Element::isValid())\n    return false;\n  return (! destination().simplified().isEmpty())\n      && (! source().simplified().isEmpty());\n}\n\nFrequency\nDMR6X2UVCodeplug::APRSSettingsElement::fmFrequency() const {\n  return Frequency::fromHz(getBCD8_be(Offset::fmFrequency())*10);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setFMFrequency(Frequency f) {\n  setBCD8_be(Offset::fmFrequency(), f.inHz()/10);\n}\n\nInterval DMR6X2UVCodeplug::APRSSettingsElement::fmTXDelay() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmTXDelay())*20));\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setFMTXDelay(const Interval intv) {\n  setUInt8(Offset::fmTXDelay(), intv.milliseconds()/20);\n}\n\nSelectiveCall\nDMR6X2UVCodeplug::APRSSettingsElement::txTone() const {\n  if ((uint8_t)SignalingType::Off ==getUInt8(Offset::fmSigType())) { // none\n    return SelectiveCall();\n  } else if ((uint8_t)SignalingType::CTCSS == getUInt8(Offset::fmSigType())) { // CTCSS\n    return CTCSS::decode(getUInt8(Offset::fmCTCSS()));\n  } else if ((uint8_t)SignalingType::DCS == getUInt8(Offset::fmSigType())) { // DCS\n    uint16_t code = getUInt16_le(Offset::fmDCS());\n    if (512 < code)\n      return SelectiveCall::fromBinaryDCS(code, false);\n    return SelectiveCall::fromBinaryDCS(code-512, true);\n  }\n\n  return SelectiveCall();\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setTXTone(const SelectiveCall &code) {\n  if (code.isInvalid()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::Off);\n  } else if (code.isCTCSS()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::CTCSS);\n    setUInt8(Offset::fmCTCSS(), CTCSS::encode(code));\n  } else if (code.isDCS()) {\n    setUInt8(Offset::fmSigType(), (uint8_t)SignalingType::DCS);\n    setUInt16_le(Offset::fmDCS(), code.binCode() + (code.isInverted() ? 512 : 0));\n  }\n}\n\nInterval\nDMR6X2UVCodeplug::APRSSettingsElement::manualTXInterval() const {\n  return Interval::fromSeconds(getUInt8(Offset::manualTXInterval()));\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setManualTXInterval(Interval sec) {\n  setUInt8(Offset::manualTXInterval(), sec.seconds());\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::autoTX() const {\n  return ! autoTXInterval().isNull();\n}\nInterval\nDMR6X2UVCodeplug::APRSSettingsElement::autoTXInterval() const {\n  return Interval::fromSeconds( ((unsigned)getUInt8(Offset::autoTXInterval()))*30 );\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setAutoTXInterval(Interval sec) {\n  setUInt8(Offset::autoTXInterval(), sec.seconds()/30);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::disableAutoTX() {\n  setAutoTXInterval(Interval::fromMilliseconds(0));\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::fixedLocationEnabled() const {\n  return getUInt8(Offset::fixedLocation());\n}\nQGeoCoordinate\nDMR6X2UVCodeplug::APRSSettingsElement::fixedLocation() const {\n  double latitude  = getUInt8(Offset::fixedLatDeg()) + double(getUInt8(Offset::fixedLatMin()))/60\n      + double(getUInt8(Offset::fixedLatSec()))/3600;\n  if (getUInt8(Offset::fixedLatSouth()))\n    latitude *= -1;\n\n  double longitude = getUInt8(Offset::fixedLonDeg()) + double(getUInt8(Offset::fixedLonMin()))/60\n      + double(getUInt8(Offset::fixedLonSec()))/3600;\n  if (getUInt8(Offset::fixedLonWest()))\n    longitude *= -1;\n\n  return QGeoCoordinate(latitude, longitude);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setFixedLocation(QGeoCoordinate &loc) {\n  double latitude = loc.latitude();\n  bool south = (0 > latitude); latitude = std::abs(latitude);\n  unsigned lat_deg = int(latitude); latitude -= lat_deg; latitude *= 60;\n  unsigned lat_min = int(latitude); latitude -= lat_min; latitude *= 60;\n  unsigned lat_sec = int(latitude);\n  double longitude = loc.longitude();\n  bool west = (0 > longitude); longitude = std::abs(longitude);\n  unsigned lon_deg = int(longitude); longitude -= lon_deg; longitude *= 60;\n  unsigned lon_min = int(longitude); longitude -= lon_min; longitude *= 60;\n  unsigned lon_sec = int(longitude);\n  setUInt8(Offset::fixedLatDeg(), lat_deg);\n  setUInt8(Offset::fixedLatMin(), lat_min);\n  setUInt8(Offset::fixedLatSec(), lat_sec);\n  setUInt8(Offset::fixedLatSouth(), (south ? 0x01 : 0x00));\n  setUInt8(Offset::fixedLonDeg(), lon_deg);\n  setUInt8(Offset::fixedLonMin(), lon_min);\n  setUInt8(Offset::fixedLonSec(), lon_sec);\n  setUInt8(Offset::fixedLonWest(), (west ? 0x01 : 0x00));\n  // enable fixed location.\n  setUInt8(Offset::fixedLocation(), 0x01);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::disableFixedLocation() {\n  setUInt8(Offset::fixedLocation(), 0x00);\n}\n\nQString\nDMR6X2UVCodeplug::APRSSettingsElement::destination() const {\n  // Terminated/padded with space\n  return readASCII(Offset::destinationCall(), 6, ' ');\n}\nunsigned\nDMR6X2UVCodeplug::APRSSettingsElement::destinationSSID() const {\n  return getUInt8(Offset::destinationSSID());\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDestination(const QString &call, unsigned ssid) {\n  // Terminated/padded with space\n  writeASCII(Offset::destinationCall(), call, 6, ' ');\n  setUInt8(Offset::destinationSSID(), ssid);\n}\nQString\nDMR6X2UVCodeplug::APRSSettingsElement::source() const {\n  // Terminated/padded with space\n  return readASCII(Offset::sourceCall(), 6, ' ');\n}\nunsigned\nDMR6X2UVCodeplug::APRSSettingsElement::sourceSSID() const {\n  return getUInt8(Offset::sourceSSID());\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setSource(const QString &call, unsigned ssid) {\n  // Terminated/padded with space\n  writeASCII(Offset::sourceCall(), call, 6, ' ');\n  setUInt8(Offset::sourceSSID(), ssid);\n}\n\nQString\nDMR6X2UVCodeplug::APRSSettingsElement::path() const {\n  return readASCII(Offset::path(), 20, 0x00);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setPath(const QString &path) {\n  writeASCII(Offset::path(), path, 20, 0x00);\n}\n\nFMAPRSSystem::Icon\nDMR6X2UVCodeplug::APRSSettingsElement::icon() const {\n  return code2aprsicon(getUInt8(Offset::symbolTable()), getUInt8(Offset::symbol()));\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setIcon(FMAPRSSystem::Icon icon) {\n  setUInt8(Offset::symbolTable(), aprsicon2tablecode(icon));\n  setUInt8(Offset::symbol(), aprsicon2iconcode(icon));\n}\n\nChannel::Power\nDMR6X2UVCodeplug::APRSSettingsElement::power() const {\n  switch (getUInt8(Offset::fmPower())) {\n  case 0: return Channel::Power::Low;\n  case 1: return Channel::Power::Mid;\n  case 2: return Channel::Power::High;\n  case 3: return Channel::Power::Max;\n  }\n  return Channel::Power::Low;\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:  setUInt8(Offset::fmPower(), 0x00); break;\n  case Channel::Power::Mid:  setUInt8(Offset::fmPower(), 0x01); break;\n  case Channel::Power::High: setUInt8(Offset::fmPower(), 0x02); break;\n  case Channel::Power::Max:  setUInt8(Offset::fmPower(), 0x03); break;\n  }\n}\n\nInterval\nDMR6X2UVCodeplug::APRSSettingsElement::fmPreWaveDelay() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::fmPrewaveDelay()))*10);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setFMPreWaveDelay(Interval ms) {\n  setUInt8(Offset::fmPrewaveDelay(), ms.milliseconds()/10);\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::dmrChannelIsSelected(unsigned n) const {\n  return 0xfa2 == dmrChannelIndex(n);\n}\nunsigned\nDMR6X2UVCodeplug::APRSSettingsElement::dmrChannelIndex(unsigned n) const {\n  return getUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices());\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRChannelIndex(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::dmrChannelIndices() + n*Offset::betweenDMRChannelIndices(), idx);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRChannelSelected(unsigned n) {\n  setDMRChannelIndex(n, 0xfa2);\n}\n\nunsigned\nDMR6X2UVCodeplug::APRSSettingsElement::dmrDestination(unsigned n) const {\n  return getBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations());\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRDestination(unsigned n, unsigned idx) {\n  setBCD8_be(Offset::dmrDestinations() + n*Offset::betweenDMRDestinations(), idx);\n}\n\nDMRContact::Type\nDMR6X2UVCodeplug::APRSSettingsElement::dmrCallType(unsigned n) const {\n  switch(getUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes())) {\n  case 0: return DMRContact::PrivateCall;\n  case 1: return DMRContact::GroupCall;\n  case 2: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRCallType(unsigned n, DMRContact::Type type) {\n  switch(type) {\n  case DMRContact::PrivateCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x00); break;\n  case DMRContact::GroupCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x01); break;\n  case DMRContact::AllCall: setUInt8(Offset::dmrCallTypes() + n*Offset::betweenDMRCallTypes(), 0x02); break;\n  }\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::dmrTimeSlotOverride(unsigned n) {\n  return 0x00 != getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots());\n}\nDMRChannel::TimeSlot\nDMR6X2UVCodeplug::APRSSettingsElement::dmrTimeSlot(unsigned n) const {\n  switch (getUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots())) {\n  case 1: return DMRChannel::TimeSlot::TS1;\n  case 2: return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x01); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0x02); break;\n  }\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::clearDMRTimeSlotOverride(unsigned n) {\n  setUInt8(Offset::dmrTimeSlots() + n*Offset::betweenDMRTimeSlots(), 0);\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::dmrRoaming() const {\n  return getUInt8(Offset::roamingSupport());\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::enableDMRRoaming(bool enable) {\n  setUInt8(Offset::roamingSupport(), (enable ? 0x01 : 0x00));\n}\n\nInterval\nDMR6X2UVCodeplug::APRSSettingsElement::dmrPreWaveDelay() const {\n  return Interval::fromMilliseconds(((unsigned)getUInt8(Offset::dmrPrewaveDelay()))*100);\n}\nvoid\nDMR6X2UVCodeplug::APRSSettingsElement::setDMRPreWaveDelay(Interval ms) {\n  setUInt8(Offset::dmrPrewaveDelay(), ms.milliseconds()/100);\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::fromFMAPRSSystem(const FMAPRSSystem *sys, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n  clear();\n\n  if (! sys->hasRevertChannel()) {\n    errMsg(err) << \"Cannot encode APRS settings: \"\n                << \"No revert channel defined for APRS system '\" << sys->name() <<\"'.\";\n    return false;\n  }\n  setFMFrequency(sys->revertChannel()->txFrequency());\n  setTXTone(sys->revertChannel()->txTone());\n  setPower(sys->revertChannel()->power());\n  setManualTXInterval(sys->period());\n  setAutoTXInterval(sys->period());\n  setDestination(sys->destination(), sys->destSSID());\n  setSource(sys->source(), sys->srcSSID());\n  setPath(sys->path());\n  setIcon(sys->icon());\n\n  AnytoneFMAPRSSettingsExtension *ext = sys->anytoneExtension();\n  if (nullptr == ext)\n    return true;\n\n  setFMPreWaveDelay(ext->preWaveDelay());\n  setFMTXDelay(ext->txDelay());\n\n  return true;\n}\n\nFMAPRSSystem *\nDMR6X2UVCodeplug::APRSSettingsElement::toFMAPRSSystem() {\n  FMAPRSSystem *sys = new FMAPRSSystem(\n        tr(\"APRS %1\").arg(destination()), nullptr,\n        destination(), destinationSSID(), source(), sourceSSID(),\n        path(), icon(), \"\", autoTXInterval());\n\n  AnytoneFMAPRSSettingsExtension *ext = new AnytoneFMAPRSSettingsExtension();\n  ext->setPreWaveDelay(fmPreWaveDelay());\n  ext->setTXDelay(fmTXDelay());\n  sys->setAnytoneExtension(ext);\n\n  return sys;\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::linkFMAPRSSystem(FMAPRSSystem *sys, Context &ctx) {\n  // First, try to find a matching analog channel in list\n  Frequency f = fmFrequency();\n  FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(f);\n  if (! ch) {\n    // If no channel is found, create one with the settings from APRS channel:\n    ch = new FMChannel();\n    ch->setName(\"APRS Channel\");\n    ch->setRXFrequency(fmFrequency());\n    ch->setTXFrequency(fmFrequency());\n    ch->setPower(power());\n    ch->setTXTone(txTone());\n    logInfo() << \"No matching APRS channel found for TX frequency \" << fmFrequency().format()\n              << \", create one as 'APRS Channel'\";\n    ctx.config()->channelList()->add(ch);\n  }\n  sys->setRevertChannel(ch);\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::fromDMRAPRSSystems(Context &ctx) {\n  unsigned int n = std::min(ctx.count<DMRAPRSSystem>(), Limit::dmrSystems());\n  for (unsigned int idx=0; idx<n; idx++)\n    fromDMRAPRSSystemObj(idx, ctx.get<DMRAPRSSystem>(idx), ctx);\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::fromDMRAPRSSystemObj(unsigned int idx, DMRAPRSSystem *sys, Context &ctx) {\n  if (sys->hasContact()) {\n    setDMRDestination(idx, sys->contact()->number());\n    setDMRCallType(idx, sys->contact()->type());\n  }\n  if (sys->hasRevertChannel()) {\n    setDMRChannelIndex(idx, ctx.index(sys->revertChannel()));\n    clearDMRTimeSlotOverride(idx);\n  } else { // no revert channel specified or \"selected channel\":\n    setDMRChannelSelected(idx);\n  }\n  return true;\n}\n\nDMRAPRSSystem *\nDMR6X2UVCodeplug::APRSSettingsElement::toDMRAPRSSystemObj(int idx) const {\n  if (0 == dmrDestination(idx))\n    return nullptr;\n  return new DMRAPRSSystem(tr(\"GPS Sys #%1\").arg(idx+1));\n}\n\nbool\nDMR6X2UVCodeplug::APRSSettingsElement::linkDMRAPRSSystem(int idx, DMRAPRSSystem *sys, Context &ctx) const {\n  // if a revert channel is defined -> link to it\n  if (dmrChannelIsSelected(idx))\n    sys->resetRevertChannel();\n  else if (ctx.has<Channel>(dmrChannelIndex(idx)) &&\n           ctx.get<Channel>(dmrChannelIndex(idx))->is<DMRChannel>())\n    sys->setRevertChannel(ctx.get<Channel>(dmrChannelIndex(idx))->as<DMRChannel>());\n\n  // Search for a matching contact in contacts\n  DMRContact *cont = nullptr;\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    if (ctx.get<DMRContact>(i)->number() == dmrDestination(idx)) {\n      cont = ctx.get<DMRContact>(i);\n      break;\n    }\n  }\n  // If no matching contact is found, create one\n  if (nullptr == cont) {\n    cont = new DMRContact(dmrCallType(idx), tr(\"GPS #%1 Contact\").arg(idx+1),\n                          dmrDestination(idx), false);\n    ctx.config()->contacts()->add(cont);\n  }\n  // link contact to GPS system.\n  sys->setContact(cont);\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMR6X2UVCodeplug\n * ********************************************************************************************* */\nDMR6X2UVCodeplug::DMR6X2UVCodeplug(const QString &label, QObject *parent)\n  : D868UVCodeplug(label, parent)\n{\n  // pass...\n}\n\nDMR6X2UVCodeplug::DMR6X2UVCodeplug(QObject *parent)\n  : D868UVCodeplug(\"BTECH DMR-6X2UV\", parent)\n{\n  // pass...\n}\n\n\nConfig *\nDMR6X2UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  // Apply base preprocessing\n  auto intermediate = AnytoneCodeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot apply preprocessing for DMR-6X2UV.\";\n    return nullptr;\n  }\n\n  // Keep 16bit DMR, ARC4 40bit, 128 bit AES and 256 bit AES keys.\n  EncryptionKeyFilterVisitor filter(\n        { EncryptionKeyFilterVisitor::Filter(BasicEncryptionKey::staticMetaObject, 16, 16),\n          EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 128, 128),\n          EncryptionKeyFilterVisitor::Filter(AESEncryptionKey::staticMetaObject, 256, 256),\n          EncryptionKeyFilterVisitor::Filter(ARC4EncryptionKey::staticMetaObject, 40, 40)});\n  if (! filter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot remove unsupported exncryption.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nDMR6X2UVCodeplug::allocateBitmaps() {\n  if (! D868UVCodeplug::allocateBitmaps())\n    return false;\n\n  // Roaming channel bitmaps\n  image(0).addElement(Offset::roamingChannelBitmap(), RoamingChannelBitmapElement::size());\n  // Roaming zone bitmaps\n  image(0).addElement(Offset::roamingZoneBitmap(), RoamingZoneBitmapElement::size());\n\n  return true;\n}\n\nvoid\nDMR6X2UVCodeplug::setBitmaps(Context& ctx)\n{\n  // First set everything common between D868UV and dmr6x2 codeplugs.\n  D868UVCodeplug::setBitmaps(ctx);\n\n  // Mark roaming zones\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  unsigned int num_roaming_zones = std::min(Limit::roamingZones(), ctx.count<RoamingZone>());\n  roaming_zone_bitmap.clear(); roaming_zone_bitmap.enableFirst(num_roaming_zones);\n\n  // Mark roaming channels\n  RoamingChannelBitmapElement roaming_ch_bitmap(data(Offset::roamingChannelBitmap()));\n  unsigned int num_roaming_channel = std::min(Limit::roamingChannels(), ctx.count<RoamingChannel>());\n  roaming_ch_bitmap.clear(); roaming_ch_bitmap.enableFirst(num_roaming_channel);\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateForEncoding() {\n  // First allocate everything common between D868UV and dmr6x2 codeplugs.\n  D868UVCodeplug::allocateForEncoding();\n\n  this->allocateRoaming();\n  this->allocateAESKeys();\n  this->allocateARC4Keys();\n}\n\nvoid\nDMR6X2UVCodeplug::allocateForDecoding() {\n  // First allocate everything common between D868UV and dmr6x2 codeplugs.\n  D868UVCodeplug::allocateForDecoding();\n\n  this->allocateRoaming();\n  this->allocateAESKeys();\n  this->allocateARC4Keys();\n}\n\nbool\nDMR6X2UVCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  // Encode everything common between d868uv and dmr6x2 radios.\n  if (! D868UVCodeplug::encodeElements(flags, ctx, err))\n    return false;\n\n  if (! this->encodeRoaming(flags, ctx, err))\n    return false;\n\n  if (! this->encodeAESKeys(flags, ctx, err))\n    return false;\n\n  if (! this->encodeARC4Keys(flags, ctx, err))\n    return false;\n\n  return true;\n}\n\n\nbool\nDMR6X2UVCodeplug::createElements(Context &ctx, const ErrorStack &err) {\n  // Create everything commong between d868uv and dmr6x2 codeplugs.\n  if (! D868UVCodeplug::createElements(ctx, err))\n    return false;\n\n  if (! this->createRoaming(ctx, err))\n    return false;\n\n  if (! this->createAESKeys(ctx, err))\n    return false;\n\n  if (! this->createARC4Keys(ctx, err))\n    return false;\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) {\n  // Link everything commong between d868uv and dmr6x2 codeplugs.\n  if (! D868UVCodeplug::linkElements(ctx, err))\n    return false;\n\n  if (! this->linkRoaming(ctx, err))\n    return false;\n\n  return true;\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateGeneralSettings() {\n  image(0).addElement(Offset::settings(), GeneralSettingsElement::size());\n  image(0).addElement(Offset::settingsExtension(), ExtendedSettingsElement::size());\n}\n\nbool\nDMR6X2UVCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).fromConfig(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode general settings element.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).fromConfig(flags, ctx)) {\n    errMsg(err) << \"Cannot encode extended settings element.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  if (! GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings element.\";\n    return false;\n  }\n\n  if (! ExtendedSettingsElement(data(Offset::settingsExtension())).updateConfig(ctx)) {\n    errMsg(err) << \"Cannot decode extended settings element.\";\n    return false;\n  }\n\n  return true;\n}\nbool\nDMR6X2UVCodeplug::linkGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).linkSettings(ctx.config()->settings(), ctx, err);\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateChannels() {\n  /* Allocate channels */\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // if disabled -> skip\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    // compute address for channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx=i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx * ChannelElement::size();\n    if (!isAllocated(addr, 0)) {\n      image(0).addElement(addr, ChannelElement::size());\n      image(0).addElement(addr+Offset::toChannelExtension(), ChannelElement::size());\n      memset(data(addr+Offset::toChannelExtension()), 0, ChannelElement::size());\n    }\n  }\n}\n\nbool\nDMR6X2UVCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  // Encode channels\n  for (unsigned int i=0; i<ctx.count<Channel>(); i++) {\n    // enable channel\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()+ idx*ChannelElement::size();\n    ChannelElement ch(data(addr));\n    if (! ch.fromChannelObj(ctx.get<Channel>(i), ctx))\n      return false;\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n    if (! ext.fromChannelObj(ctx.get<Channel>(i), ctx, err))\n      return false;\n  }\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  // Create channels\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    ChannelElement ch(data(Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n                           + idx*ChannelElement::size()));\n    if (Channel *obj = ch.toChannelObj(ctx)) {\n      ctx.config()->channelList()->add(obj);\n      ctx.add(obj, i);\n    }\n  }\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ChannelBitmapElement channel_bitmap(data(Offset::channelBitmap()));\n  // Link channel objects\n  for (uint16_t i=0; i<Limit::numChannels(); i++) {\n    // Check if channel is enabled:\n    if (! channel_bitmap.isEncoded(i))\n      continue;\n    uint16_t bank = i/Limit::channelsPerBank(), idx = i%Limit::channelsPerBank();\n    uint32_t addr = Offset::channelBanks() + bank*Offset::betweenChannelBanks()\n        + idx*ChannelElement::size();\n    ChannelElement ch(data(addr));\n    ChannelExtensionElement ext(data(addr + Offset::toChannelExtension()));\n\n    if (ctx.has<Channel>(i)) {\n      if (! ch.linkChannelObj(ctx.get<Channel>(i), ctx))\n        return false;\n      if (! ext.linkChannelObj(ctx.get<Channel>(i), ctx, err))\n        return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nDMR6X2UVCodeplug::allocateGPSSystems() {\n  // replaces D868UVCodeplug::allocateGPSSystems\n\n  // APRS settings\n  image(0).addElement(Offset::aprsSettings(), APRSSettingsElement::size());\n  image(0).addElement(Offset::fmAPRSMessage(), Size::fmAPRSMessage());\n}\n\nbool\nDMR6X2UVCodeplug::encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // replaces D868UVCodeplug::encodeGPSSystems\n\n  APRSSettingsElement aprs(data(Offset::aprsSettings())); aprs.clear();\n\n  // Encode APRS system (there can only be one)\n  if (0 < ctx.count<FMAPRSSystem>()) {\n    aprs.fromFMAPRSSystem(ctx.get<FMAPRSSystem>(0), ctx, err);\n    uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage());\n    encode_ascii(aprsmsg, ctx.get<FMAPRSSystem>(0)->message(), Limit::fmAPRSMessage(), 0x00);\n  }\n\n  // Encode GPS systems\n  if (! aprs.fromDMRAPRSSystems(ctx))\n    return false;\n  if (0 < ctx.count<DMRAPRSSystem>()) {\n    // If there is at least one GPS system defined -> set auto TX interval.\n    //  This setting might be overridden by any analog APRS system below\n    aprs.setAutoTXInterval(ctx.get<DMRAPRSSystem>(0)->period());\n    aprs.setManualTXInterval(ctx.get<DMRAPRSSystem>(0)->period());\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::createGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // replaces D868UVCodeplug::createGPSSystems\n\n  // Before creating any GPS/APRS systems, get global auto TX interval\n  APRSSettingsElement aprs(data(Offset::aprsSettings()));\n\n  // Create APRS system (if enabled)\n  uint8_t *aprsmsg = (uint8_t *)data(Offset::fmAPRSMessage());\n  if (aprs.isValid()) {\n    FMAPRSSystem *sys = aprs.toFMAPRSSystem();\n    if (nullptr == sys) {\n      errMsg(err) << \"Cannot decode positioning systems.\";\n      return false;\n    }\n    sys->setPeriod(aprs.autoTXInterval());\n    sys->setMessage(decode_ascii(aprsmsg, Limit::fmAPRSMessage(), 0x00));\n    ctx.config()->posSystems()->add(sys); ctx.add(sys,0);\n  }\n\n  // Create GPS systems\n  for (unsigned int i=0; i<APRSSettingsElement::Limit::dmrSystems(); i++) {\n    if (0 == aprs.dmrDestination(i))\n      continue;\n    if (DMRAPRSSystem *sys = aprs.toDMRAPRSSystemObj(i)) {\n      sys->setPeriod(aprs.autoTXInterval());\n      ctx.config()->posSystems()->add(sys); ctx.add(sys, i);\n    } else {\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::linkGPSSystems(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  // replaces D868UVCodeplug::linkGPSSystems\n\n  // Link APRS system\n  APRSSettingsElement aprs(data(Offset::aprsSettings()));\n  if (aprs.isValid()) {\n    aprs.linkFMAPRSSystem(ctx.get<FMAPRSSystem>(0), ctx);\n  }\n\n  // Link GPS systems\n  for (unsigned int i=0; i<APRSSettingsElement::Limit::dmrSystems(); i++) {\n    if (0 == aprs.dmrDestination(i))\n      continue;\n    aprs.linkDMRAPRSSystem(i, ctx.get<DMRAPRSSystem>(i), ctx);\n  }\n\n  return true;\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateRoaming() {\n  /* Allocate roaming channels */\n  RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap()));\n  for (uint8_t i=0; i<Limit::roamingChannels(); i++) {\n    // Get byte and bit for roaming channel\n    // if disabled -> skip\n    if (! roaming_channel_bitmap.isEncoded(i))\n      continue;\n    // Allocate roaming channel\n    uint32_t addr = Offset::roamingChannels() + i*D878UVCodeplug::RoamingChannelElement::size();\n    if (!isAllocated(addr, 0))\n      image(0).addElement(addr, D878UVCodeplug::RoamingChannelElement::size());\n  }\n\n  /* Allocate roaming zones. */\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  for (uint8_t i=0; i<Limit::roamingZones(); i++) {\n    // Get byte and bit for radio ID\n    // if disabled -> skip\n    if (! roaming_zone_bitmap.isEncoded(i))\n      continue;\n    // Allocate roaming zone\n    uint32_t addr = Offset::roamingZones() + i*D878UVCodeplug::RoamingZoneElement::size();\n    if (!isAllocated(addr, 0)) {\n      logDebug() << \"Allocate roaming zone at \" << QString::number(addr, 16);\n      image(0).addElement(addr, D878UVCodeplug::RoamingZoneElement::size());\n    }\n  }\n}\n\nbool\nDMR6X2UVCodeplug::encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  // Encode roaming channels\n  unsigned int num_roaming_channel = std::min(\n        Limit::roamingChannels(), ctx.count<RoamingChannel>());\n  for (uint8_t i=0; i<num_roaming_channel; i++) {\n    // Encode roaming channel\n    uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size();\n    RoamingChannelElement rch_elm(data(addr));\n    RoamingChannel *rch = ctx.config()->roamingChannels()->get(i)->as<RoamingChannel>();\n    rch_elm.clear();\n    rch_elm.fromChannel(rch);\n    if (! ctx.add(rch, i)) {\n      errMsg(err) << \"Cannot add index \" << i << \" for roaming channel '\"\n                  << rch->name() << \"' to codeplug context.\";\n      return false;\n    }\n  }\n\n  // Encode roaming zones\n  for (unsigned int i=0; i<ctx.count<RoamingZone>(); i++){\n    uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size();\n    RoamingZoneElement zone(data(addr));\n    logDebug() << \"Encode roaming zone \" << ctx.config()->roamingZones()->zone(i)->name()\n               << \" (\" << (i+1) << \") at \" << QString::number(addr, 16)\n               << \" with \" << ctx.config()->roamingZones()->zone(i)->count() << \" elements.\";\n    zone.fromRoamingZone(ctx.config()->roamingZones()->zone(i), ctx);\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::createRoaming(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create or find roaming channels\n  RoamingChannelBitmapElement roaming_channel_bitmap(data(Offset::roamingChannelBitmap()));\n  for (unsigned int i=0; i<Limit::roamingChannels(); i++) {\n    if (! roaming_channel_bitmap.isEncoded(i))\n      continue;\n    uint32_t addr = Offset::roamingChannels() + i*RoamingChannelElement::size();\n    RoamingChannelElement ch(data(addr));\n    RoamingChannel *digi = ch.toChannel(ctx);\n    ctx.add(digi, i);\n  }\n\n  // Create and link roaming zones\n  RoamingZoneBitmapElement roaming_zone_bitmap(data(Offset::roamingZoneBitmap()));\n  for (unsigned int i=0; i<Limit::roamingZones(); i++) {\n    if (! roaming_zone_bitmap.isEncoded(i))\n      continue;\n    uint32_t addr = Offset::roamingZones() + i*RoamingZoneElement::size();\n    RoamingZoneElement z(data(addr));\n    RoamingZone *zone = z.toRoamingZone(ctx, err);\n    ctx.config()->roamingZones()->add(zone); ctx.add(zone, i);\n    z.linkRoamingZone(zone, ctx, err);\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::linkRoaming(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  // Pass, no need to link roaming channels.\n  return true;\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateAESKeys() {\n  // Since there is no bitmap indicating, which keys are valid, we need to allocate all 255 for\n  // decoding.\n  if (! isAllocated(Offset::aesEncryptionKeys(), 0))\n    image(0).addElement(Offset::aesEncryptionKeys(),\n                        Limit::aesEncryptionKeys()*AESEncryptionKeyElement::size());\n}\n\nbool\nDMR6X2UVCodeplug::encodeAESKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(flags);\n\n  for (uint8_t i=0; i<Limit::aesEncryptionKeys(); i++) {\n    // if disabled -> skip\n    if (! ctx.has<AESEncryptionKey>(i))\n      continue;\n    AESEncryptionKeyElement key(data(Offset::aesEncryptionKeys() + i*AESEncryptionKeyElement::size()));\n    key.setIndex(i);\n    key.setKey(ctx.get<AESEncryptionKey>(i)->key());\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::createAESKeys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (uint8_t i=0; i<Limit::aesEncryptionKeys(); i++) {\n    AESEncryptionKeyElement keyEl(data(Offset::aesEncryptionKeys() + i*AESEncryptionKeyElement::size()));\n    // if disabled -> skip\n    if (! keyEl.isValid())\n      continue;\n    // Decode\n    auto key = new AESEncryptionKey();\n    key->setName(QString(\"AES Key %1\").arg(i+1));\n    if (! key->setKey(keyEl.key(), err)) {\n      delete key;\n      return false;\n    }\n    // Store\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n\n\nvoid\nDMR6X2UVCodeplug::allocateARC4Keys() {\n  // Since there is no bitmap indicating, which keys are valid, we need to allocate all 255 for\n  // decoding.\n  if (! isAllocated(Offset::arc4EncryptionKeys(), 0))\n    image(0).addElement(Offset::arc4EncryptionKeys(),\n                        Limit::arc4EncryptionKeys()*ARC4EncryptionKeyElement::size());\n}\n\nbool\nDMR6X2UVCodeplug::encodeARC4Keys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(flags);\n\n  for (uint8_t i=0; i<Limit::arc4EncryptionKeys(); i++) {\n    // if disabled -> skip\n    if (! ctx.has<ARC4EncryptionKey>(i))\n      continue;\n    ARC4EncryptionKeyElement key(data(Offset::arc4EncryptionKeys() + i*ARC4EncryptionKeyElement::size()));\n    key.setIndex(i);\n    key.setKey(ctx.get<ARC4EncryptionKey>(i)->key());\n  }\n\n  return true;\n}\n\nbool\nDMR6X2UVCodeplug::createARC4Keys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (uint8_t i=0; i<Limit::arc4EncryptionKeys(); i++) {\n    ARC4EncryptionKeyElement keyEl(data(Offset::arc4EncryptionKeys() + i*ARC4EncryptionKeyElement::size()));\n    // if disabled -> skip\n    if (! keyEl.isValid())\n      continue;\n    // Decode\n    auto key = new ARC4EncryptionKey();\n    key->setName(QString(\"ARC4 Key %1\").arg(i+1));\n    if (! key->setKey(keyEl.key(), err)) {\n      delete key;\n      return false;\n    }\n    // Store\n    ctx.add(key, i);\n    ctx.config()->commercialExtension()->encryptionKeys()->add(key);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dmr6x2uv_codeplug.hh",
    "content": "#ifndef DMR6X2UVCODEPLUG_HH\n#define DMR6X2UVCODEPLUG_HH\n\n#include \"d878uv_codeplug.hh\"\n#include \"ranges.hh\"\n\n\n/** Represents the device specific binary codeplug for BTECH DMR-6X2UV radios.\n *\n * This codeplug implementation is compatible with firmware revision 2.21. See\n * https://dmr-tools.github.io/codeplugs for details.\n *\n * @ingroup dmr6x2uv */\nclass DMR6X2UVCodeplug : public D868UVCodeplug\n{\n  Q_OBJECT\n\nprotected:\n  /** Colors supported by the DMR-6X2UV. */\n  struct Color {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      Orange=0, Red=1, Yellow=2, Green=3, Turquoise=4, Blue=5, White = 6, Black = 7\n    } CodedColor;\n  };\n\n  /** Background colors supported by the DMR-6X2UV. */\n  struct BackgroundColor {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      Black = 0, Blue = 1\n    } CodedColor;\n  };\n\n  /** Font colors supported by the DMR-6X2UV. */\n  struct FontColor {\n  public:\n    /** Maps code -> color. */\n    static AnytoneDisplaySettingsExtension::Color decode(uint8_t code);\n    /** Maps color -> code. */\n    static uint8_t encode(AnytoneDisplaySettingsExtension::Color color);\n\n  protected:\n    /** Encoding of the supported colors. */\n    typedef enum {\n      White = 0, Black = 1, Orange=2, Red=3, Yellow=4, Green=5, Turquoise=6, Blue=7\n    } CodedColor;\n  };\n\npublic:\n  /** General settings element for the DMR-6X2UV.\n   *\n   * Extends the @c AnytoneCodeplug::GeneralSettingsElement by the device specific settings for\n   * the BTECH DMR-6X2UV.\n   *\n   * Memory representation of the encoded settings element (size 0x0e0 bytes):\n   * @verbinclude dmr6x2uv_generalsettings.txt */\n  class GeneralSettingsElement: public D868UVCodeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Device specific encoding of the key functions. */\n    struct KeyFunction {\n    public:\n      /** Encodes key function. */\n      static uint8_t encode(AnytoneKeySettingsExtension::KeyFunction tone);\n      /** Decodes key function. */\n      static AnytoneKeySettingsExtension::KeyFunction decode(uint8_t code);\n\n    protected:\n      /** Device specific key functions. */\n      typedef enum {\n        Off = 0x00, Voltage = 0x01, Power = 0x02, Repeater = 0x03, Reverse = 0x04,\n        Encryption = 0x05, Call = 0x06, VOX = 0x07, ToggleVFO = 0x08, SubPTT = 0x09,\n        Scan = 0x0a, WFM = 0x0b, Alarm = 0x0c, RecordSwitch = 0x0d, Record = 0x0e, SMS = 0x0f,\n        Dial = 0x10, GPSInformation = 0x11, Monitor = 0x12, ToggleMainChannel = 0x13, HotKey1 = 0x14,\n        HotKey2 = 0x15, HotKey3 = 0x16, HotKey4 = 0x17, HotKey5 = 0x18, HotKey6 = 0x19,\n        WorkAlone = 0x1a, SkipChannel = 0x1b, DMRMonitor = 0x1c, SubChannel = 0x1d,\n        PriorityZone = 0x1e, VFOScan = 0x1f, MICSoundQuality = 0x20, LastCallReply = 0x21,\n        ChannelType = 0x22, SimplexRepeater = 0x23, Ranging = 0x24, ChannelRanging = 0x25,\n        MaxVolume = 0x26, Slot = 0x27, Squelch = 0x28, Roaming = 0x29, Zone = 0x2a, RoamingSet = 0x2b,\n        Mute = 0x02c, CtcssDcsSet=0x2d, APRSType = 0x2e, APRSSet = 0x2f, DIMShut = 0x30,\n        GPSToggle = 0x31, SatPredict = 0x32\n      } KeyFunctionCode;\n    };\n\n    /** Possible VFO frequency steps. */\n    enum FreqStep {\n      FREQ_STEP_2_5kHz = 0,             ///< 2.5kHz\n      FREQ_STEP_5kHz = 1,               ///< 5kHz\n      FREQ_STEP_6_25kHz = 2,            ///< 6.25kHz\n      FREQ_STEP_10kHz = 3,              ///< 10kHz\n      FREQ_STEP_12_5kHz = 4,            ///< 12.5kHz\n      FREQ_STEP_20kHz = 5,              ///< 20kHz\n      FREQ_STEP_25kHz = 6,              ///< 25kHz\n      FREQ_STEP_50kHz = 7               ///< 50kHz\n    };\n\n    /** DTMF signalling durations. */\n    enum DTMFDuration {\n      DTMF_DUR_50ms = 0, DTMF_DUR_100ms = 1, DTMF_DUR_200ms = 2, DTMF_DUR_300ms = 3, DTMF_DUR_500ms = 4\n    };\n\n    /** TBST (open repeater) frequencies. */\n    enum class TBSTFrequency {\n      Hz1000 = 0, Hz1450 = 1, Hz1750 = 2, Hz2100 = 3\n    };\n\n    /** All possible STE (squelch tail eliminate) frequencies. */\n    enum class STEFrequency {\n      Off = 0, Hz55_2  = 1, Hz259_2 = 2\n    };\n\n    /** Encoding of repeater timeslot. */\n    enum class RepeaterTimeSlot {\n      TS1 = 0, TS2 = 1, Channel = 2\n    };\n\n    /** Possible SMS formats. */\n    enum class SMSFormat {\n      Motorola = 0, Hytera = 1, DMR = 2\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00e0; }\n\n    bool idleChannelTone() const override;\n    void enableIdleChannelTone(bool enable) override;\n\n    /** Returns the transmit timeout in seconds. */\n    virtual unsigned transmitTimeout() const;\n    /** Sets the transmit timeout in seconds. */\n    virtual void setTransmitTimeout(unsigned tot);\n\n    /** Returns the UI language. */\n    virtual AnytoneDisplaySettingsExtension::Language language() const;\n    /** Sets the UI language. */\n    virtual void setLanguage(AnytoneDisplaySettingsExtension::Language lang);\n\n    /** Returns the VFO frequency step in kHz. */\n    virtual Frequency vfoFrequencyStep() const;\n    /** Sets the VFO frequency step in kHz. */\n    virtual void setVFOFrequencyStep(Frequency kHz);\n\n    AnytoneKeySettingsExtension::KeyFunction funcKeyAShort() const override;\n    void setFuncKeyAShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBShort() const override;\n    void setFuncKeyBShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCShort() const override;\n    void setFuncKeyCShort(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Short() const override;\n    void setFuncKey1Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Short() const override;\n    void setFuncKey2Short(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyALong() const override;\n    void setFuncKeyALong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyBLong() const override;\n    void setFuncKeyBLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKeyCLong() const override;\n    void setFuncKeyCLong(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey1Long() const override;\n    void setFuncKey1Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n    AnytoneKeySettingsExtension::KeyFunction funcKey2Long() const override;\n    void setFuncKey2Long(AnytoneKeySettingsExtension::KeyFunction func) override;\n\n    bool vfoModeA() const override;\n    void enableVFOModeA(bool enable) override;\n    bool vfoModeB() const override;\n    void enableVFOModeB(bool enable) override;\n\n    /** Returns the STE (squelch tail eliminate) type. */\n    virtual AnytoneSettingsExtension::STEType steType() const;\n    /** Sets the STE (squelch tail eliminate) type. */\n    virtual void setSTEType(AnytoneSettingsExtension::STEType type);\n    /** Returns the STE (squelch tail eliminate) frequency setting in Hz.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual double steFrequency() const;\n    /** Sets the STE (squelch tail eliminate) frequency setting.\n     * A value of 0 disables the STE. Possible values are 55.2 and 259.2 Hz. */\n    virtual void setSTEFrequency(double freq);\n\n    /** Returns the group call hang time in seconds. */\n    virtual Interval groupCallHangTime() const;\n    /** Sets the group call hang time in seconds. */\n    virtual void setGroupCallHangTime(Interval sec);\n    /** Returns the private call hang time in seconds. */\n    virtual Interval privateCallHangTime() const;\n    /** Sets the private call hang time in seconds. */\n    virtual void setPrivateCallHangTime(Interval sec);\n\n    /** Returns the pre-wave time in ms. */\n    virtual Interval preWaveDelay() const;\n    /** Sets the pre-wave time in ms. */\n    virtual void setPreWaveDelay(Interval ms);\n    /** Returns the wake head-period in ms. */\n    virtual Interval wakeHeadPeriod() const;\n    /** Sets the wake head-period in ms. */\n    virtual void setWakeHeadPeriod(Interval ms);\n\n    /** Returns the wide-FM (broadcast) channel index. */\n    virtual unsigned wfmChannelIndex() const;\n    /** Sets the wide-FM (broadcast) channel index. */\n    virtual void setWFMChannelIndex(unsigned idx);\n    /** Returns @c true if the WFM RX is in VFO mode. */\n    virtual bool wfmVFOEnabled() const;\n    /** Enables/disables VFO mode for WFM RX. */\n    virtual void enableWFMVFO(bool enable);\n\n    /** Returns the DTMF tone duration in ms. */\n    virtual unsigned dtmfToneDuration() const;\n    /** Sets the DTMF tone duration in ms. */\n    virtual void setDTMFToneDuration(unsigned ms);\n\n    /** Returns @c true if \"man down\" is enabled. */\n    virtual bool manDown() const;\n    /** Enables/disables \"man down\". */\n    virtual void enableManDown(bool enable);\n\n    /** Returns @c true if WFM monitor is enabled. */\n    virtual bool wfmMonitor() const;\n    /** Enables/disables WFM monitor. */\n    virtual void enableWFMMonitor(bool enable);\n\n    /** Returns the TBST frequency. */\n    virtual Frequency tbstFrequency() const;\n    /** Sets the TBST frequency. */\n    virtual void setTBSTFrequency(Frequency freq);\n\n    /** Returns @c true if the \"pro mode\" is enabled. */\n    virtual bool proMode() const;\n    /** Enables/disables the \"pro mode\". */\n    virtual void enableProMode(bool enable);\n\n    bool keyToneEnabled() const override;\n    void enableKeyTone(bool enable) override;\n\n    /** Returns @c true if the own ID is filtered in call lists. */\n    virtual bool filterOwnID() const;\n    /** Enables/disables filter of own ID in call lists. */\n    virtual void enableFilterOwnID(bool enable);\n\n    /** Returns @c true remote stun/kill is enabled. */\n    virtual bool remoteStunKill() const;\n    /** Enables/disables remote stun/kill. */\n    virtual void enableRemoteStunKill(bool enable);\n    /** Returns @c true remote monitor is enabled. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables remote monitor. */\n    virtual void enableRemoteMonitor(bool enable);\n\n    /** Returns @c true, if the selection of a TX contact is enabled. */\n    virtual bool selectTXContactEnabled() const;\n    /** Enables/disables selection of the TX contact. */\n    virtual void enableSelectTXContact(bool enable);\n\n    /** Returns the monitor slot match. */\n    virtual AnytoneDMRSettingsExtension::SlotMatch monitorSlotMatch() const;\n    /** Sets the monitor slot match. */\n    virtual void setMonitorSlotMatch(AnytoneDMRSettingsExtension::SlotMatch match);\n    /** Returns @c true if the monitor matches color code. */\n    virtual bool monitorColorCodeMatch() const;\n    /** Enables/disables monitor color code match. */\n    virtual void enableMonitorColorCodeMatch(bool enable);\n    /** Returns @c true if the monitor matches ID. */\n    virtual bool monitorIDMatch() const;\n    /** Enables/disables monitor ID match. */\n    virtual void enableMonitorIDMatch(bool enable);\n    /** Returns @c true if the monitor holds the time slot. */\n    virtual bool monitorTimeSlotHold() const;\n    /** Enables/disables monitor time slot hold. */\n    virtual void enableMonitorTimeSlotHold(bool enable);\n\n    /** Returns the \"man down\" delay in seconds. */\n    virtual Interval manDownDelay() const;\n    /** Sets the \"man down\" delay in seconds. */\n    virtual void setManDownDelay(Interval sec);\n\n    /** Returns the analog call hold in seconds. */\n    virtual unsigned fmCallHold() const;\n    /** Sets the analog call hold in seconds. */\n    virtual void setFMCallHold(unsigned sec);\n\n    /** Returns @c true if the GPS range reporting is enabled. */\n    virtual bool gpsMessageEnabled() const;\n    /** Enables/disables GPS range reporting. */\n    virtual void enableGPSMessage(bool enable);\n\n    /** Returns @c true if the call channel is maintained. */\n    virtual bool maintainCallChannel() const;\n    /** Enables/disables maintaining the call channel. */\n    virtual void enableMaintainCallChannel(bool enable);\n\n    /** Returns the priority Zone A index. */\n    virtual unsigned priorityZoneAIndex() const;\n    /** Sets the priority zone A index. */\n    virtual void setPriorityZoneAIndex(unsigned idx);\n    /** Returns the priority Zone B index. */\n    virtual unsigned priorityZoneBIndex() const;\n    /** Sets the priority zone B index. */\n    virtual void setPriorityZoneBIndex(unsigned idx);\n\n    /** Returns @c true, if a SMS confirmation is sent. */\n    virtual bool smsConfirmEnabled() const;\n    /** Enables/disables SMS confirmation. */\n    virtual void enableSMSConfirm(bool enable);\n\n    /** Returns @c true if the simplex repeater feature is enabled. */\n    virtual bool simplexRepeaterEnabled() const;\n    /** Enables disables the simplex repeater feature. */\n    virtual void enableSimplexRepeater(bool enable);\n\n    Interval gpsUpdatePeriod() const override;\n    void setGPSUpdatePeriod(Interval sec) override;\n\n    /** Returns @c true if the speaker is switched on during RX in simplex repeater mode,\n     * see @c simplexRepeaterEnabled. */\n    virtual bool monitorSimplexRepeaterEnabled() const;\n    /** Enables/disables the speaker during RX in simplex repeater mode. */\n    virtual void enableMonitorSimplexRepeater(bool enable);\n\n    bool showCurrentContact() const override;\n    void enableShowCurrentContact(bool enable) override;\n\n    bool keyToneLevelAdjustable() const override;\n    Level keyToneLevel() const override;\n    void setKeyToneLevel(Level level) override;\n    void setKeyToneLevelAdjustable() override;\n\n    bool knobLock() const override;\n    void enableKnobLock(bool enable) override;\n    bool keypadLock() const override;\n    void enableKeypadLock(bool enable) override;\n    bool sidekeysLock() const override;\n    void enableSidekeysLock(bool enable) override;\n    bool keyLockForced() const override;\n    void enableKeyLockForced(bool enable) override;\n\n    /** Returns the time-slot in simplex repeater mode. */\n    virtual AnytoneRepeaterSettingsExtension::TimeSlot simplexRepeaterTimeslot() const;\n    /** Sets the time-slot in simplex repeater mode. */\n    virtual void setSimplexRepeaterTimeslot(AnytoneRepeaterSettingsExtension::TimeSlot slot);\n\n    bool showLastHeard() const override;\n    void enableShowLastHeard(bool enable) override;\n\n    /** Returns the SMS format. */\n    virtual SMSExtension::Format smsFormat() const;\n    /** Sets the SMS format. */\n    virtual void setSMSFormat(SMSExtension::Format fmt);\n\n    bool gpsUnitsImperial() const override;\n    void enableGPSUnitsImperial(bool enable) override;\n\n    Frequency autoRepeaterMinFrequencyVHF() const override;\n    void setAutoRepeaterMinFrequencyVHF(Frequency Hz) override;\n    Frequency autoRepeaterMaxFrequencyVHF() const override;\n    void setAutoRepeaterMaxFrequencyVHF(Frequency Hz) override;\n    Frequency autoRepeaterMinFrequencyUHF() const override;\n    void setAutoRepeaterMinFrequencyUHF(Frequency Hz) override;\n    Frequency autoRepeaterMaxFrequencyUHF() const override;\n    void setAutoRepeaterMaxFrequencyUHF(Frequency Hz) override;\n\n    AnytoneAutoRepeaterSettingsExtension::Direction autoRepeaterDirectionB() const override;\n    void setAutoRepeaterDirectionB(AnytoneAutoRepeaterSettingsExtension::Direction dir) override;\n\n    /** If enabled, the FM ID is sent together with selected contact. */\n    virtual bool fmSendIDAndContact() const;\n    /** Enables/disables sending contact with FM ID. */\n    virtual void enableFMSendIDAndContact(bool enable);\n\n    bool defaultChannel() const override;\n    void enableDefaultChannel(bool enable) override;\n    unsigned defaultZoneIndexA() const override;\n    void setDefaultZoneIndexA(unsigned idx) override;\n    unsigned defaultZoneIndexB() const override;\n    void setDefaultZoneIndexB(unsigned idx) override;\n    bool defaultChannelAIsVFO() const override;\n    unsigned defaultChannelAIndex() const override;\n    void setDefaultChannelAIndex(unsigned idx) override;\n    void setDefaultChannelAToVFO() override;\n    bool defaultChannelBIsVFO() const override;\n    unsigned defaultChannelBIndex() const override;\n    void setDefaultChannelBIndex(unsigned idx) override;\n    void setDefaultChannelBToVFO() override;\n\n    bool keepLastCaller() const override;\n    void enableKeepLastCaller(bool enable) override;\n\n    /** Returns backlight duration during RX. */\n    virtual Interval rxBacklightDuration() const;\n    /** Sets the backlight duration during RX. */\n    virtual void setRXBacklightDuration(Interval sec);\n\n    /** Returns the stand-by background color. */\n    virtual AnytoneDisplaySettingsExtension::Color standbyBackgroundColor() const;\n    /** Sets the stand-by background color. */\n    virtual void setStandbyBackgroundColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns the group-call hang time, if group call was dialed manually. */\n    virtual unsigned int manualDialedGroupCallHangTime() const;\n    /** Sets the group-call hang time, if the group call was dialed manually. */\n    virtual void setManualDialedGroupCallHangTime(unsigned int dur);\n    /** Returns the private-call hang time, if private call was dialed manually. */\n    virtual unsigned int manualDialedPrivateCallHangTime() const;\n    /** Sets the private-call hang time, if the private call was dialed manually. */\n    virtual void setManualDialedPrivateCallHangTime(unsigned int dur);\n\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err) override;\n    bool updateConfig(Context &ctx, const ErrorStack &err) override;\n\n  protected:\n    /** Some internal used offsets within the element. */\n    struct Offset: D868UVCodeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int idleChannelTone()     { return 0x0000; }\n      static constexpr unsigned int transmitTimeout()     { return 0x0004; }\n      static constexpr unsigned int language()            { return 0x0005; }\n      static constexpr unsigned int vfoFrequencyStep()    { return 0x0008; }\n      static constexpr unsigned int vfoModeB()            { return 0x0015; }\n      static constexpr unsigned int vfoModeA()            { return 0x0016; }\n      static constexpr unsigned int steType()             { return 0x0017; }\n      static constexpr unsigned int steFrequency()        { return 0x0018; }\n      static constexpr unsigned int groupCallHangTime()   { return 0x0019; }\n      static constexpr unsigned int privateCallHangTime() { return 0x001a; }\n      static constexpr unsigned int preWaveDelay()        { return 0x001b; }\n      static constexpr unsigned int wakeHeadPeriod()      { return 0x001c; }\n      static constexpr unsigned int wfmChannelIndex()     { return 0x001d; }\n      static constexpr unsigned int wfmVFOEnabled()       { return 0x001e; }\n      static constexpr unsigned int dtmfToneDuration()    { return 0x0023; }\n      static constexpr unsigned int manDown()             { return 0x0024; }\n      static constexpr unsigned int wfmMonitor()          { return 0x002b; }\n      static constexpr unsigned int tbstFrequency()       { return 0x002e; }\n      static constexpr unsigned int proMode()             { return 0x0034; }\n      static constexpr unsigned int enableKeyTone()       { return 0x0036; }\n      static constexpr unsigned int filterOwnID()         { return 0x0038; }\n      static constexpr unsigned int remoteStunKill()      { return 0x003c; }\n      static constexpr unsigned int remoteMonitor()       { return 0x003e; }\n      static constexpr unsigned int selectTXContact()     { return 0x0040; }\n      static constexpr unsigned int monSlotMatch()        { return 0x0049; }\n      static constexpr unsigned int monColorCodeMatch()   { return 0x004a; }\n      static constexpr unsigned int monIDMatch()          { return 0x004b; }\n      static constexpr unsigned int monTimeSlotHold()     { return 0x004c; }\n      static constexpr unsigned int manDownDelay()        { return 0x004f; }\n      static constexpr unsigned int fmCallHold()          { return 0x0050; }\n      static constexpr unsigned int enableGPSMessage()    { return 0x0053; }\n      static constexpr unsigned int maintainCallChannel() { return 0x006e; }\n      static constexpr unsigned int priorityZoneA()       { return 0x006f; }\n      static constexpr unsigned int priorityZoneB()       { return 0x0070; }\n      static constexpr unsigned int smsConfirm()          { return 0x0071; }\n      static constexpr unsigned int simplexRepEnable()    { return 0x00b1; }\n      static constexpr unsigned int gpsUpdatePeriod()     { return 0x00b2; }\n      static constexpr unsigned int simplxRepSpeaker()    { return 0x00b3; }\n      static constexpr unsigned int showContact()         { return 0x00b4; }\n      static constexpr unsigned int keyToneLevel()        { return 0x00b5; }\n      static constexpr Bit knobLock()                     { return {0x00b6, 0}; }\n      static constexpr Bit keypadLock()                   { return {0x00b6, 1}; }\n      static constexpr Bit sideKeyLock()                  { return {0x00b6, 3}; }\n      static constexpr Bit forceKeyLock()                 { return {0x00b6, 4}; }\n      static constexpr unsigned int simplxRepSlot()       { return 0x00b7; }\n      static constexpr unsigned int showLastHeard()       { return 0x00b8; }\n      static constexpr unsigned int smsFormat()           { return 0x00b9; }\n      static constexpr unsigned int gpsUnits()            { return 0x00ba; }\n      static constexpr unsigned int autoRepMinVHF()       { return 0x00bc; }\n      static constexpr unsigned int autoRepMaxVHF()       { return 0x00c0; }\n      static constexpr unsigned int autoRepMinUHF()       { return 0x00c4; }\n      static constexpr unsigned int autoRepMaxUHF()       { return 0x00c8; }\n      static constexpr unsigned int autoRepeaterDirB()    { return 0x00cc; }\n      static constexpr unsigned int fmSendIDAndContact()  { return 0x00cd; }\n      static constexpr unsigned int defaultChannels()     { return 0x00ce; }\n      static constexpr unsigned int defaultZoneA()        { return 0x00cf; }\n      static constexpr unsigned int defaultZoneB()        { return 0x00d0; }\n      static constexpr unsigned int defaultChannelA()     { return 0x00d1; }\n      static constexpr unsigned int defaultChannelB()     { return 0x00d2; }\n      static constexpr unsigned int keepLastCaller()      { return 0x00d3; }\n      static constexpr unsigned int rxBacklightDuration() { return 0x00d4; }\n      static constexpr unsigned int standbyBackground()   { return 0x00d5; }\n      static constexpr unsigned int manGrpCallHangTime()  { return 0x00d6; }\n      static constexpr unsigned int manPrvCallHangTime()  { return 0x00d7; }\n      /// @endcond\n    };\n\n  };\n\n  /** Implements some settings extension for the BTECH DMR-6X2UV.\n   *\n   * Memory representation of the encoded settings element (size 0x0e0 bytes):\n   * @verbinclude dmr6x2uv_settingsextension.txt */\n  class ExtendedSettingsElement: public AnytoneCodeplug::ExtendedSettingsElement\n  {\n  protected:\n    /** Talker alias encoding. */\n    enum class TalkerAliasEncoding {\n      ISO8 = 0, ISO7 = 1, Unicode = 2,\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    ExtendedSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ExtendedSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the general settings. */\n    void clear();\n\n    /** Returns @c true if the talker alias is sent. */\n    virtual bool sendTalkerAlias() const;\n    /** Enables/disables sending the talker alias. */\n    virtual void enableSendTalkerAlias(bool enable);\n\n    /** Returns the talker alias source. */\n    virtual AnytoneDMRSettingsExtension::TalkerAliasSource talkerAliasSource() const;\n    /** Sets the talker alias source. */\n    virtual void setTalkerAliasSource(AnytoneDMRSettingsExtension::TalkerAliasSource mode);\n\n    /** Returns the talker alias encoding. */\n    virtual DMRSettings::TalkerAliasEncoding talkerAliasEncoding() const;\n    /** Sets the talker alias encoding. */\n    virtual void setTalkerAliasEncoding(DMRSettings::TalkerAliasEncoding encoding);\n\n    /** Returns the font color. */\n    virtual AnytoneDisplaySettingsExtension::Color fontColor() const;\n    /** Sets the font color. */\n    virtual void setFontColor(AnytoneDisplaySettingsExtension::Color color);\n\n    /** Returns @c true if the custom channel background is enabled. */\n    virtual bool customChannelBackgroundEnabled() const;\n    /** Enables/disables the custom channel background. */\n    virtual void enableCustomChannelBackground(bool enable);\n\n    /** Returns @c true if auto roaming is enabled. */\n    virtual bool autoRoamingEnabled() const;\n    /** Enables/disables auto roaming. */\n    virtual void enableAutoRoaming(bool enable);\n\n    /** Returns @c true if repeater check is enabled. */\n    virtual bool repeaterRangeCheckEnabled() const;\n    /** Enables/disables repeater check. */\n    virtual void enableRepeaterRangeCheck(bool enable);\n    /** Returns the number of times, the repeater out-of-range reminder is shown (1-10). */\n    virtual unsigned int repeaterCheckNumNotifications() const;\n    /** Sets the number of times, the repeater out-of-range reminder is shown (1-10). */\n    virtual void setRepeaterCheckNumNotifications(unsigned int n);\n    /** Returns the repeater check interval in seconds (5-50s). */\n    virtual Interval repeaterRangeCheckInterval() const;\n    /** Sets the repeater check interval in seconds (5-50s). */\n    virtual void setRepeaterRangeCheckInterval(Interval intv);\n    /** Returns the repeater out-of-range alert type. */\n    virtual AnytoneRoamingSettingsExtension::OutOfRangeAlert repeaterOutOfRangeAlert() const;\n    /** Sets the repeater out-of-range alert type. */\n    virtual void setRepeaterOutOfRangeAlert(AnytoneRoamingSettingsExtension::OutOfRangeAlert alert);\n    /** Returns the number of times, a repeater reconnection is tried (3-5). */\n    virtual unsigned int repeaterRangeCheckCount() const;\n    /** Sets the number of times, a repeater reconnection is tried (3-5). */\n    virtual void setRepeaterRangeCheckCount(unsigned int n);\n\n    /** Returns the roaming zone index. */\n    virtual unsigned int defaultRoamingZoneIndex() const;\n    /** Sets the roaming zone index. */\n    virtual void setDefaultRoamingZoneIndex(unsigned int index);\n    /** Returns the condition to start roaming. */\n    virtual AnytoneRoamingSettingsExtension::RoamStart roamingStartCondition() const;\n    /** Sets the condition to start roaming. */\n    virtual void setRoamingStartCondition(AnytoneRoamingSettingsExtension::RoamStart cond);\n    /** Returns the auto-roaming interval in minutes (1-256). */\n    virtual Interval autoRoamPeriod() const;\n    /** Sets the auto-roaming interval in minutes (1-256). */\n    virtual void setAutoRoamPeriod(Interval minutes);\n    /** Returns the effective roaming waiting time in seconds (0-30s). */\n    virtual Interval autoRoamDelay() const;\n    /** Sets the effective roaming waiting time in seconds (0-30s). */\n    virtual void setAutoRoamDelay(Interval sec);\n    /** Returns the roaming return condition. */\n    virtual AnytoneRoamingSettingsExtension::RoamStart roamingReturnCondition() const;\n    /** Sets the roaming return condition. */\n    virtual void setRoamingReturnCondition(AnytoneRoamingSettingsExtension::RoamStart cond);\n\n    /** Returns the mute timer in minutes. */\n    virtual Interval muteTimer() const;\n    /** Sets the mute timer in minutes. */\n    virtual void setMuteTimer(Interval minutes);\n\n    /** Returns the encryption type. */\n    virtual AnytoneDMRSettingsExtension::EncryptionType encryptionType() const;\n    /** Sets the encryption type. */\n    virtual void setEncryptionType(AnytoneDMRSettingsExtension::EncryptionType type);\n\n    AnytoneDisplaySettingsExtension::Color zoneANameColor() const;\n    void setZoneANameColor(AnytoneDisplaySettingsExtension::Color color);\n    AnytoneDisplaySettingsExtension::Color zoneBNameColor() const;\n    void setZoneBNameColor(AnytoneDisplaySettingsExtension::Color color);\n    /** Returns the name color for channel A. */\n    virtual AnytoneDisplaySettingsExtension::Color channelANameColor() const;\n    /** Sets the name color for channel A. */\n    virtual void setChannelANameColor(AnytoneDisplaySettingsExtension::Color color);\n    AnytoneDisplaySettingsExtension::Color channelBNameColor() const;\n    void setChannelBNameColor(AnytoneDisplaySettingsExtension::Color color);\n\n    bool fromConfig(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n    bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    bool linkConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for entries. */\n    struct Limit: AnytoneCodeplug::ExtendedSettingsElement::Limit {\n      /// Out of range reminder count limits.\n      static constexpr IntRange repRangeReminder()    { return {1, 10}; }\n      /// Repeater range check interval limits.\n      static constexpr TimeRange rangeCheckInterval() {\n        return TimeRange{Interval::fromSeconds(1), Interval::fromSeconds(50)};\n      }\n      /// Repeater reconnection count limits.\n      static constexpr IntRange repeaterReconnections() { return {3,5}; }\n      /// Auto-roaming interval limits.\n      static constexpr TimeRange autoRoamingInterval() {\n        return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(256)};\n      }\n      /// Auto-roaming delay limits.\n      static constexpr TimeRange autoRoamDelay() {\n        return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(30)};\n      }\n      /// Mute-timer limits.\n      static constexpr TimeRange muteTimer() {\n        return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(256)};\n      }\n    };\n\n  protected:\n    /** Some internal offset within the codeplug element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUEMNT\n      static constexpr unsigned int sendTalkerAlias()              { return 0x0000; }\n      static constexpr unsigned int talkerAliasDisplay()           { return 0x0001; }\n      static constexpr unsigned int talkerAliasEncoding()          { return 0x0002; }\n      static constexpr unsigned int fontColor()                    { return 0x0003; }\n      static constexpr unsigned int customChannelBackground()      { return 0x0004; }\n      static constexpr unsigned int defaultRoamingZone()           { return 0x0005; }\n      static constexpr unsigned int roaming()                      { return 0x0006; }\n      static constexpr unsigned int repRangeCheck()                { return 0x0007; }\n      static constexpr unsigned int repRangeAlert()                { return 0x0008; }\n      static constexpr unsigned int repRangeReminder()             { return 0x0009; }\n      static constexpr unsigned int rangeCheckInterval()           { return 0x000a; }\n      static constexpr unsigned int rangeCheckCount()              { return 0x000b; }\n      static constexpr unsigned int roamStartCondition()           { return 0x000c; }\n      static constexpr unsigned int autoRoamPeriod()               { return 0x000d; }\n      static constexpr unsigned int autoRoamDelay()                { return 0x000e; }\n      static constexpr unsigned int roamReturnCondition()          { return 0x000f; }\n      static constexpr unsigned int muteDelay()                    { return 0x0010; }\n      static constexpr unsigned int encryptionType()               { return 0x0011; }\n      static constexpr unsigned int zoneANameColor()               { return 0x0012; }\n      static constexpr unsigned int zoneBNameColor()               { return 0x0013; }\n      static constexpr unsigned int channelANameColor()            { return 0x0014; }\n      static constexpr unsigned int channelBNameColor()            { return 0x0015; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the channel element for the BTECH DMR-6X2UV.\n   *  Extends the AnytoneCodeplug::ChannelElement by the device specific features, like multiple\n   *  scan lists associated with the channel.\n   *\n   *  Memory representation of the encoded channel element (size 0x040 bytes):\n   *  @verbinclude dmr6x2uv_channel.txt */\n  class ChannelElement: public AnytoneCodeplug::ChannelElement\n  {\n  public:\n    /** Possible PTT modes for FM APRS. */\n    enum class FMAPRSPTTMode {\n      Off = 0, Start = 1, End = 2\n    };\n\n    /** Possible APRS report types. */\n    enum class APRSType{\n      Off = 0, FM = 1, DMR = 2\n    };\n\n    /** Possible encryption types. */\n    enum class DMREncryptionType {\n      Basic = 0, Enhanced = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns @c true, if an AES encryption key index is set. */\n    bool hasAESEncryptionKeyIndex() const;\n    /** Returns the AES encryption key index.\n     * The index is 0-based. */\n    unsigned int aesEncryptionKeyIndex() const;\n    /** Sets the AES encryption key index. */\n    void setAESEncryptionKeyIndex(unsigned int index);\n    /** Clears the AES encryption key index. */\n    void clearAESEncryptionKeyIndex();\n\n    /** Returns the DMR encryption type. */\n    DMREncryptionType dmrEncryptionType() const;\n    /** Sets the DMR encryption type. */\n    void setDMREncryptionType(DMREncryptionType type);\n\n    /** Returns @c true, if an DMR encryption key index is set. */\n    bool hasDMREncryptionKeyIndex() const;\n    /** Returns the DMR encryption key index.\n     * The index is 0-based. */\n    unsigned int dmrEncryptionKeyIndex() const;\n    /** Sets the DMR encryption key index. */\n    void setDMREncryptionKeyIndex(unsigned int index);\n    /** Clears the DMR encryption key index. */\n    void clearDMREncryptionKeyIndex();\n\n    /** Returns @c true, if the first scan list index is set. */\n    bool hasScanListIndex() const;\n    /** Returns the first scan list index (0-based). */\n    unsigned scanListIndex() const;\n    /** Sets the first scan list index (0-based). */\n    void setScanListIndex(unsigned idx);\n    /** Clears the first scan list index. */\n    void clearScanListIndex();\n\n    /** Returns @c true, if the n-th scan list index is set (n=0,...,7). */\n    virtual bool hasScanListIndex(unsigned int n) const;\n    /** Returns the n-th scan list index (0-based, n=0,...,7). */\n    virtual unsigned scanListIndex(unsigned int n) const;\n    /** Sets the n-th scan list index (0-based, n=0,...,7). */\n    virtual void setScanListIndex(unsigned int n, unsigned idx);\n    /** Clears the n-th scan list index (n=0,...,7). */\n    virtual void clearScanListIndex(unsigned int n);\n\n    /** Returns @c true if roaming is enabled for this channel. */\n    virtual bool roamingEnabled() const;\n    /** Enables/disables roaming. */\n    virtual void enableRoaming(bool enable);\n\n    /** Returns @c true, if ranging is enabled. */\n    virtual bool ranging() const;\n    /** Enables/disables ranging. */\n    virtual void enableRanging(bool enable);\n\n    /** Returns the DMR APRS report channel index. */\n    virtual unsigned int dmrAPRSChannelIndex() const;\n    /** Sets the DMR APRS report channel index. */\n    virtual void setDMRAPRSChannelIndex(unsigned int idx);\n\n    /** Returns @c true, if the reception of DMR APRS messages is enabled. */\n    virtual bool dmrAPRSRXEnabled() const;\n    /** Enables/disables the reception of DMR APRS messages. */\n    virtual void enableDMRARPSRX(bool enable);\n\n    /** Returns true, if the position is reported via DMR APRS on PTT. */\n    virtual bool dmrAPRSPTTEnabled() const;\n    /** Enables/disables reporting the position via DMR APRS on PTT. */\n    virtual void enableDMRAPRSPTT(bool enable);\n\n    /** Returns the FM APRS PTT mode. */\n    virtual FMAPRSPTTMode fmAPRSPTTMode() const;\n    /** Sets the FM APRS PTT mode. */\n    virtual void setFMAPRSPTTMode(FMAPRSPTTMode mode);\n\n    /** Returns the APRS type. */\n    virtual APRSType aprsType() const;\n    /** Sets the APRS type. */\n    virtual void setAPRSType(APRSType aprstype);\n\n    bool linkChannelObj(Channel *c, Context &ctx) const;\n    bool fromChannelObj(const Channel *c, Context &ctx);\n\npublic:\n    /** Some limits of this element. */\n    struct Limit {\n      /// Maximum number of scan list indices.\n      static constexpr unsigned int scanListIndices() { return 8; }\n    };\n\nprotected:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public AnytoneCodeplug::ChannelElement::Offset {\n      static constexpr unsigned int aesEncryptionKeyIndex()        { return 0x0013; }\n      static constexpr Bit roaming()                               { return {0x001b, 2}; }\n      static constexpr Bit ranging()                               { return {0x001b, 0}; }\n      static constexpr Bit dmrEncryptionType()                     { return {0x0021, 6}; }\n      static constexpr unsigned int dmrEncryptionKeyIndex()        { return 0x0022; }\n      static constexpr unsigned int scanListIndices()              { return 0x0036; }\n      static constexpr unsigned int betweenScanListIndices()       { return 0x0001; }\n      static constexpr unsigned int dmrAPRSChannelIndex()          { return 0x003e; }\n      static constexpr Bit dmrAPRSRXEnable()                       { return {0x003f, 5}; }\n      static constexpr Bit dmrAPRSPTTEnable()                      { return {0x003f, 4}; }\n      static constexpr Bit fmAPRSPTTMode()                         { return {0x003f, 2}; }\n      static constexpr Bit aprsType()                              { return {0x003f, 0}; }\n    };\n    /// @endcond\n  };\n\n  /** Starting from FW version 2.21, all devices encode an channel settings extension element\n   * at an offset 0x2000 to the original channel. Also the size of the extension element is\n   * identical to the channel element itself. This is weird. Anyway. This class encodes/decodes\n   * these settings.\n   *\n   * This implementation is compatible with firmware version 2.21 */\n  class ChannelExtensionElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelExtensionElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Default constructor. */\n    ChannelExtensionElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return ChannelElement::size(); }\n\n    /** Resets the channel extension. */\n    void clear();\n\n    /** Returns @c true, if the channel has an ARC4 key index assigned. */\n    virtual bool hasARC4KeyIndex() const;\n    /** Returns the 0-based ARC4 key index. */\n    virtual unsigned int arc4KeyIndex() const;\n    /** Sets the ARC4 key index. */\n    virtual void setARC4KeyIndex(unsigned int idx);\n    /** Clears the ARC4 key index. */\n    virtual void clearARC4KeyIndex();\n\n    /** Links a previously created channel object. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the given channel object. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some internal used offsets. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int arc4KeyIndex() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the APRS settings within the binary DMR-6X2UV codeplug.\n   *\n   * Memory layout of APRS settings (size 0x00a0 bytes):\n   * @verbinclude dmr6x2uv_aprssetting.txt\n   */\n  class APRSSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    APRSSettingsElement(uint8_t *ptr, unsigned size);\n\n    /** Possible settings for the FM APRS subtone type. */\n    enum class SignalingType {\n      Off=0, CTCSS=1, DCS=2\n    };\n\n  public:\n    /** Constructor. */\n    explicit APRSSettingsElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00a0; }\n\n    /** Resets the settings. */\n    void clear();\n    bool isValid() const;\n\n    /** Returns the FM APRS frequency. */\n    virtual Frequency fmFrequency() const;\n    /** Sets the FM APRS frequency. */\n    virtual void setFMFrequency(Frequency f);\n\n    /** Returns the TX delay in ms. */\n    virtual Interval fmTXDelay() const;\n    /** Sets the TX delay in ms. */\n    virtual void setFMTXDelay(const Interval intv);\n\n    /** Returns the sub tone settings. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the sub tone settings. */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns the manual TX interval in seconds. */\n    virtual Interval manualTXInterval() const;\n    /** Sets the manual TX interval in seconds. */\n    virtual void setManualTXInterval(Interval sec);\n\n    /** Returns @c true if the auto transmit is enabled. */\n    virtual bool autoTX() const;\n    /** Returns the auto TX interval in seconds. */\n    virtual Interval autoTXInterval() const;\n    /** Sets the auto TX interval in seconds. */\n    virtual void setAutoTXInterval(Interval sec);\n    /** Disables auto tx. */\n    virtual void disableAutoTX();\n\n    /** Returns @c true if a fixed location is send. */\n    virtual bool fixedLocationEnabled() const;\n    /** Returns the fixed location send. */\n    virtual QGeoCoordinate fixedLocation() const;\n    /** Sets the fixed location to send. */\n    virtual void setFixedLocation(QGeoCoordinate &loc);\n    /** Disables sending a fixed location. */\n    virtual void disableFixedLocation();\n\n    /** Returns the destination call. */\n    virtual QString destination() const;\n    /** Returns the destination SSID. */\n    virtual unsigned destinationSSID() const;\n    /** Sets the destination call & SSID. */\n    virtual void setDestination(const QString &call, unsigned ssid);\n    /** Returns the source call. */\n    virtual QString source() const;\n    /** Returns the source SSID. */\n    virtual unsigned sourceSSID() const;\n    /** Sets the source call & SSID. */\n    virtual void setSource(const QString &call, unsigned ssid);\n\n    /** Returns the path string. */\n    virtual QString path() const;\n    /** Sets the path string. */\n    virtual void setPath(const QString &path);\n\n    /** Returns the APRS icon. */\n    virtual FMAPRSSystem::Icon icon() const;\n    /** Sets the APRS icon. */\n    virtual void setIcon(FMAPRSSystem::Icon icon);\n\n    /** Returns the transmit power. */\n    virtual Channel::Power power() const;\n    /** Sets the transmit power. */\n    virtual void setPower(Channel::Power power);\n\n    /** Returns the pre-wave delay in ms. */\n    virtual Interval fmPreWaveDelay() const;\n    /** Sets the pre-wave delay in ms. */\n    virtual void setFMPreWaveDelay(Interval ms);\n\n    /** Returns @c true if the channel points to the current/selected channel. */\n    virtual bool dmrChannelIsSelected(unsigned n) const;\n    /** Returns the digital channel index for the n-th system. */\n    virtual unsigned dmrChannelIndex(unsigned n) const;\n    /** Sets the digital channel index for the n-th system. */\n    virtual void setDMRChannelIndex(unsigned n, unsigned idx);\n    /** Sets the channel to the current/selected channel. */\n    virtual void setDMRChannelSelected(unsigned n);\n\n    /** Returns the destination contact for the n-th system. */\n    virtual unsigned dmrDestination(unsigned n) const;\n    /** Sets the destination contact for the n-th system. */\n    virtual void setDMRDestination(unsigned n, unsigned idx);\n\n    /** Returns the call type for the n-th system. */\n    virtual DMRContact::Type dmrCallType(unsigned n) const;\n    /** Sets the call type for the n-th system. */\n    virtual void setDMRCallType(unsigned n, DMRContact::Type type);\n\n    /** Returns @c true if the n-th system overrides the channel time-slot. */\n    virtual bool dmrTimeSlotOverride(unsigned n);\n    /** Returns the time slot if overridden (only valid if @c timeSlot returns true). */\n    virtual DMRChannel::TimeSlot dmrTimeSlot(unsigned n) const;\n    /** Overrides the time slot of the n-th selected channel. */\n    virtual void setDMRTimeSlot(unsigned n, DMRChannel::TimeSlot ts);\n    /** Clears the time-slot override. */\n    virtual void clearDMRTimeSlotOverride(unsigned n);\n\n    /** Returns @c true if the roaming is enabled. */\n    virtual bool dmrRoaming() const;\n    /** Enables/disables roaming. */\n    virtual void enableDMRRoaming(bool enable);\n\n    /** Returns the repeater activation delay in ms. */\n    virtual Interval dmrPreWaveDelay() const;\n    /** Sets the repeater activation delay in ms. */\n    virtual void setDMRPreWaveDelay(Interval ms);\n\n    /** Configures this APRS system from the given generic config. */\n    virtual bool fromFMAPRSSystem(const FMAPRSSystem *sys, Context &ctx,\n                                  const ErrorStack &err=ErrorStack());\n    /** Constructs a generic APRS system configuration from this APRS system. */\n    virtual FMAPRSSystem *toFMAPRSSystem();\n    /** Links the transmit channel within the generic APRS system based on the transmit frequency\n     * defined within this APRS system. */\n    virtual bool linkFMAPRSSystem(FMAPRSSystem *sys, Context &ctx);\n\n    /** Constructs all GPS system from the generic configuration. */\n    virtual bool fromDMRAPRSSystems(Context &ctx);\n    /** Encodes the given GPS system. */\n    virtual bool fromDMRAPRSSystemObj(unsigned int idx, DMRAPRSSystem *sys, Context &ctx);\n    /** Constructs a generic GPS system from the idx-th encoded GPS system. */\n    virtual DMRAPRSSystem *toDMRAPRSSystemObj(int idx) const;\n    /** Links the specified generic GPS system. */\n    virtual bool linkDMRAPRSSystem(int idx, DMRAPRSSystem *sys, Context &ctx) const;\n\n  public:\n    /** Some static limits for this element. */\n    struct Limit {\n      /// Maximum length of call signs.\n      static constexpr unsigned int callLength()                           { return 0x0006; }\n      /// Maximum length of the repeater path string.\n      static constexpr unsigned int pathLength()                           { return 0x0020; }\n      /// Maximum number of DMR APRS systems.\n      static constexpr unsigned int dmrSystems()                           { return 0x0008; }\n    };\n\n  protected:\n    /** Internal used offsets within the codeplug element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int fmFrequency()                          { return 0x0001; }\n      static constexpr unsigned int fmTXDelay()                            { return 0x0005; }\n      static constexpr unsigned int fmSigType()                            { return 0x0006; }\n      static constexpr unsigned int fmCTCSS()                              { return 0x0007; }\n      static constexpr unsigned int fmDCS()                                { return 0x0008; }\n      static constexpr unsigned int manualTXInterval()                     { return 0x000a; }\n      static constexpr unsigned int autoTXInterval()                       { return 0x000b; }\n      static constexpr unsigned int fmTXMonitor()                          { return 0x000c; }\n      static constexpr unsigned int fixedLocation()                        { return 0x000d; }\n      static constexpr unsigned int fixedLatDeg()                          { return 0x000e; }\n      static constexpr unsigned int fixedLatMin()                          { return 0x000f; }\n      static constexpr unsigned int fixedLatSec()                          { return 0x0010; }\n      static constexpr unsigned int fixedLatSouth()                        { return 0x0011; }\n      static constexpr unsigned int fixedLonDeg()                          { return 0x0012; }\n      static constexpr unsigned int fixedLonMin()                          { return 0x0013; }\n      static constexpr unsigned int fixedLonSec()                          { return 0x0014; }\n      static constexpr unsigned int fixedLonWest()                         { return 0x0015; }\n      static constexpr unsigned int destinationCall()                      { return 0x0016; }\n      static constexpr unsigned int destinationSSID()                      { return 0x001c; }\n      static constexpr unsigned int sourceCall()                           { return 0x001d; }\n      static constexpr unsigned int sourceSSID()                           { return 0x0023; }\n      static constexpr unsigned int path()                                 { return 0x0024; }\n      static constexpr unsigned int symbolTable()                          { return 0x0039; }\n      static constexpr unsigned int symbol()                               { return 0x003a; }\n      static constexpr unsigned int fmPower()                              { return 0x003b; }\n      static constexpr unsigned int fmPrewaveDelay()                       { return 0x003c; }\n      static constexpr unsigned int dmrChannelIndices()                    { return 0x0040; }\n      static constexpr unsigned int betweenDMRChannelIndices()             { return 0x0002; }\n      static constexpr unsigned int dmrDestinations()                      { return 0x0050; }\n      static constexpr unsigned int betweenDMRDestinations()               { return 0x0004; }\n      static constexpr unsigned int dmrCallTypes()                         { return 0x0070; }\n      static constexpr unsigned int betweenDMRCallTypes()                  { return 0x0001; }\n      static constexpr unsigned int roamingSupport()                       { return 0x0078; }\n      static constexpr unsigned int dmrTimeSlots()                         { return 0x0079; }\n      static constexpr unsigned int betweenDMRTimeSlots()                  { return 0x0001; }\n      static constexpr unsigned int dmrPrewaveDelay()                      { return 0x0081; }\n      /// @endcond\n    };\n  };\n\n  /** Reuse roaming channel bitmap from D878UV. */\n  typedef D878UVCodeplug::RoamingChannelBitmapElement RoamingChannelBitmapElement ;\n  /** Reuse roaming channel from D878UV. */\n  typedef D878UVCodeplug::RoamingChannelElement RoamingChannelElement;\n  /** Reuse roaming zone bitmap from D878UV. */\n  typedef D878UVCodeplug::RoamingZoneBitmapElement RoamingZoneBitmapElement;\n  /** Reuse roaming zone from D878UV. */\n  typedef D878UVCodeplug::RoamingZoneElement RoamingZoneElement;\n\n  /** Reuse AES encryption key from D878UV. */\n  typedef D878UVCodeplug::AESEncryptionKeyElement AESEncryptionKeyElement;\n  /** Reuse ARC4 encryption key from D878UV. */\n  typedef D878UVCodeplug::ARC4EncryptionKeyElement ARC4EncryptionKeyElement;\n\npublic:\n  /** Hidden constructor. */\n  explicit DMR6X2UVCodeplug(const QString &label, QObject *parent=nullptr);\n\npublic:\n  /** Empty constructor. */\n  explicit DMR6X2UVCodeplug(QObject *parent=nullptr);\n\n  Config *preprocess(Config *config, const ErrorStack &err) const;\n\nprotected:\n  bool allocateBitmaps();\n  void setBitmaps(Context &ctx);\n  void allocateForDecoding();\n  void allocateForEncoding();\n\n  bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void allocateGPSSystems();\n  bool encodeGPSSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGPSSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to store all roaming channels and zones. */\n  virtual void allocateRoaming();\n  /** Encodes the roaming channels and zones. */\n  virtual bool encodeRoaming(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Creates roaming channels and zones from codeplug. */\n  virtual bool createRoaming(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links roaming channels and zones. */\n  virtual bool linkRoaming(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to encode/decode AES keys. */\n  virtual void allocateAESKeys();\n  /** Encode all AES keys. */\n  virtual bool encodeAESKeys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decode AES keys from the codeplug. */\n  virtual bool createAESKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Allocates memory to encode/decode ARC4 keys. */\n  virtual void allocateARC4Keys();\n  /** Encode all ARC4 keys. */\n  virtual bool encodeARC4Keys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Decode ARC4 keys from the codeplug. */\n  virtual bool createARC4Keys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit : public D868UVCodeplug::Limit {\n    /// Maximum length of the FM APRS message\n    static constexpr unsigned int fmAPRSMessage()                 { return 60; }\n    /// Maximum number of roaming channels.\n    static constexpr unsigned int roamingChannels()               { return 250; }\n    /// Maximum number of roaming zones.\n    static constexpr unsigned int roamingZones()                  { return 64; }\n    /// Maximum number of AES encryption keys.\n    static constexpr unsigned int aesEncryptionKeys()             { return 254; }\n    /// Maximum number of ARC4 encryption keys.\n    static constexpr unsigned int arc4EncryptionKeys()            { return 254; }\n  };\n\nprotected:\n  /** Some internal used offsets within the codeplug. */\n  struct Offset: public D868UVCodeplug::Offset {\n    ///@cond DO_NOT_DOCUMENT\n    static constexpr unsigned int toChannelExtension()            { return 0x00002000; }\n    static constexpr unsigned int roamingChannelBitmap()          { return 0x01042000; }\n    static constexpr unsigned int roamingChannels()               { return 0x01040000; }\n    static constexpr unsigned int roamingZoneBitmap()             { return 0x01042080; }\n    static constexpr unsigned int roamingZones()                  { return 0x01043000; }\n\n    static constexpr unsigned int fmAPRSMessage()                 { return 0x02501200; }\n    static constexpr unsigned int fmAPRSFrequencyNames()          { return 0x02502000; }\n    static constexpr unsigned int settingsExtension()             { return 0x02501400; }\n\n    static constexpr unsigned int aesEncryptionKeys()             { return 0x025c1000; }\n    static constexpr unsigned int arc4EncryptionKeys()            { return 0x025c5000; }\n    /// @endcond\n  };\n\n  /** Some internal used sizes. */\n  struct Size: public D868UVCodeplug::Size {\n    ///@cond DO_NOT_DOCUMENT\n    static constexpr unsigned int fmAPRSMessage()                 { return 0x00000040; }\n    /// @endcond\n  };\n};\n\n#endif // DMR6X2UVCODEPLUG_HH\n"
  },
  {
    "path": "lib/dmr6x2uv_limits.cc",
    "content": "#include \"dmr6x2uv_limits.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include \"anytone_satelliteconfig.hh\"\n\n\nDMR6X2UVLimits::DMR6X2UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                               const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                               const QString &hardwareRevision, QObject *parent)\n  : AnytoneLimits(hardwareRevision, \"V102\", true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 200000;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = true;\n  _satelliteConfigImplemented  = true;\n  _numSatellites               = AnytoneSatelliteConfig::Limit::satellites();\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 14, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(DMR6X2UVCodeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 250, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 64) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 4000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies(rxFreqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(txFreqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false)},\n              {\"aprs\", new RadioLimitObjRef(PositionReportingSystem::staticMetaObject, true)},\n              {\"roaming\", new RadioLimitObjRef(RoamingZone::staticMetaObject, true) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          250, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Handle positioning systems. */\n  add(\"positioning\", new RadioLimitList{\n        { DMRAPRSSystem::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, 1, new RadioLimitObject {\n            { \"name\", new RadioLimitStringIgnored() },\n            { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n            { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, FMChannel::staticMetaObject}, false) },\n            { \"icon\", new RadioLimitEnum{} },\n            { \"message\", new RadioLimitString(0, 60, RadioLimitString::ASCII) }\n            ///@todo extend APRSSystem to expose other settings as properties.\n          }} } );\n\n  /* Handle roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(RoamingZone::staticMetaObject, 0, 64,\n                         new RadioLimitObject {\n                           { \"name\", new RadioLimitStringIgnored() },\n                           { \"channels\", new RadioLimitRefList(0, 64, DMRChannel::staticMetaObject) }\n                         } ) );\n}\n"
  },
  {
    "path": "lib/dmr6x2uv_limits.hh",
    "content": "#ifndef DMR6X2UVLIMITS_HH\n#define DMR6X2UVLIMITS_HH\n\n#include \"anytone_limits.hh\"\n\n/** Implements the limits for the BTECH DMR-6X2UV.\n * @ingroup dmr6x2uv */\nclass DMR6X2UVLimits: public AnytoneLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  DMR6X2UVLimits(const std::initializer_list<std::pair<Frequency, Frequency> > &rxFreqRanges,\n                 const std::initializer_list<std::pair<Frequency, Frequency> > &txFreqRanges,\n                 const QString &hardwareRevision, QObject *parent=nullptr);\n};\n\n#endif // D878UVLIMITS_HH\n"
  },
  {
    "path": "lib/dmrsettings.cc",
    "content": "#include \"dmrsettings.hh\"\n\n\nDMRSettings::DMRSettings(QObject *parent)\n  : ConfigExtension{parent}, _privateCallMatch(true), _groupCallMatch(true),\n    _privateCallHangTime(Interval::fromSeconds(5)), _groupCallHangTime(Interval::fromSeconds(3)),\n    _sendTalkerAlias(false), _talkerAliasEncoding(TalkerAliasEncoding::Iso8),\n    _preamble(Interval::fromMilliseconds(100))\n{\n  // pass...\n}\n\n\nvoid\nDMRSettings::clear() {\n  ConfigExtension::clear();\n\n  _privateCallMatch = true;\n  _groupCallMatch = true;\n  _privateCallHangTime = Interval::fromSeconds(5);\n  _groupCallHangTime = Interval::fromSeconds(3);\n  _sendTalkerAlias = false;\n  _talkerAliasEncoding = TalkerAliasEncoding::Iso8;\n}\n\n\nConfigItem *\nDMRSettings::clone() const {\n  auto obj = new DMRSettings();\n  if (! obj->copy(*this)) {\n    delete obj;\n    return nullptr;\n  }\n  return obj;\n}\n\n\nbool\nDMRSettings::privateCallMatchEnabled() const {\n  return _privateCallMatch;\n}\n\nvoid\nDMRSettings::enablePrivateCallMatch(bool enable) {\n  if (_privateCallMatch == enable)\n    return;\n  _privateCallMatch = enable;\n  emit modified(this);\n}\n\n\nbool DMRSettings::groupCallMatchEnabled() const {\n  return _groupCallMatch;\n}\n\nvoid\nDMRSettings::enableGroupCallMatch(bool enable) {\n  if (_groupCallMatch == enable)\n    return;\n  _groupCallMatch = enable;\n  emit modified(this);\n}\n\n\nInterval\nDMRSettings::privateCallHangTime() const {\n  return _privateCallHangTime;\n}\n\nvoid\nDMRSettings::setPrivateCallHangTime(const Interval &dur) {\n  if (_privateCallHangTime == dur)\n    return;\n  _privateCallHangTime = dur;\n  emit modified(this);\n}\n\n\nInterval\nDMRSettings::groupCallHangTime() const {\n  return _groupCallHangTime;\n}\n\nvoid\nDMRSettings::setGroupCallHangTime(const Interval &dur) {\n  if (_groupCallHangTime == dur)\n    return;\n  _groupCallHangTime = dur;\n  emit modified(this);\n}\n\n\nbool\nDMRSettings::sendTalkerAliasEnabled() const {\n  return _sendTalkerAlias;\n}\n\nvoid\nDMRSettings::enableSendTalkerAlias(bool enable) {\n  if (_sendTalkerAlias == enable)\n    return;\n  _sendTalkerAlias = enable;\n  emit modified(this);\n}\n\n\nDMRSettings::TalkerAliasEncoding\nDMRSettings::talkerAliasEncoding() const {\n  return _talkerAliasEncoding;\n}\n\nvoid\nDMRSettings::setTalkerAliasEncoding(TalkerAliasEncoding encoding) {\n  if (_talkerAliasEncoding == encoding)\n    return;\n  _talkerAliasEncoding = encoding;\n  emit modified(this);\n}\n\n\nInterval\nDMRSettings::preamble() const {\n  return _preamble;\n}\n\nvoid\nDMRSettings::setPreamble(const Interval &dur) {\n  if (_preamble == dur)\n    return;\n  _preamble = dur;\n  emit modified(this);\n}\n\n"
  },
  {
    "path": "lib/dmrsettings.hh",
    "content": "#ifndef DMRSETTINGS_HH\n#define DMRSETTINGS_HH\n\n#include \"interval.hh\"\n#include \"configobject.hh\"\n\n/** Implements some common DMR settings present in many devices.\n * @ingroup config */\nclass DMRSettings : public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"Common DMR settings.\")\n  Q_CLASSINFO(\"privateCallMatchDescription\", \"If enabled, private calls are only received, \"\n                                             \"if they are for you.\")\n  Q_PROPERTY(bool privateCallMatch READ privateCallMatchEnabled WRITE enablePrivateCallMatch FINAL)\n  Q_CLASSINFO(\"groupCallMatchDescription\", \"If enabled, group calls are only received, \"\n                                           \"if they are on the group list.\")\n  Q_PROPERTY(bool groupCallMatch READ groupCallMatchEnabled WRITE enableGroupCallMatch FINAL)\n  Q_CLASSINFO(\"privateCallHangTimeDescription\", \"Time span during which a direct answer for a \"\n                                                \"private call is possible.\")\n  Q_PROPERTY(Interval privateCallHangTime READ privateCallHangTime WRITE setPrivateCallHangTime FINAL)\n  Q_CLASSINFO(\"groupCallHangTimeDescription\", \"Time span during which a direct answer for a \"\n                                              \"group call is possible.\")\n  Q_PROPERTY(Interval groupCallHangTime READ groupCallHangTime WRITE setGroupCallHangTime FINAL)\n  Q_CLASSINFO(\"sendTalkerAliasDescription\", \"If enabled, the talker alias is send.\")\n  Q_PROPERTY(bool sendTalkerAlias READ sendTalkerAliasEnabled WRITE enableSendTalkerAlias FINAL)\n  Q_CLASSINFO(\"talkerAliasEncodingDescription\", \"Specifies the encoding of the talker alias.\")\n  Q_PROPERTY(TalkerAliasEncoding talkerAliasEncoding READ talkerAliasEncoding WRITE setTalkerAliasEncoding FINAL)\n  Q_CLASSINFO(\"preambleDescription\", \"Specifies preamble duration. Usually 100ms.\")\n  Q_PROPERTY(Interval preamble READ preamble WRITE setPreamble FINAL)\n\npublic:\n  /** Possible talker alias encodings. */\n  enum class TalkerAliasEncoding {\n    Iso7, Iso8, Unicode\n  };\n  Q_ENUM(TalkerAliasEncoding)\n\npublic:\n  /** Default constructor. */\n  explicit DMRSettings(QObject *parent = nullptr);\n\n  void clear() override;\n  ConfigItem *clone() const override;\n\n  /** Returns @c true if the private call must match. */\n  bool privateCallMatchEnabled() const;\n  /** Enables private call match. */\n  void enablePrivateCallMatch(bool enable);\n\n  /** Returns @c true if the group call must match. */\n  bool groupCallMatchEnabled() const;\n  /** Enables group call match. */\n  void enableGroupCallMatch(bool enable);\n\n  /** Returns the private call hang time. */\n  Interval privateCallHangTime() const;\n  /** Sets the private call hang time. */\n  void setPrivateCallHangTime(const Interval &dur);\n\n  /** Returns the group call hang time. */\n  Interval groupCallHangTime() const;\n  /** Sets the group call hang time. */\n  void setGroupCallHangTime(const Interval &dur);\n\n  /** Retunrs @c true if the talker alias is send. */\n  bool sendTalkerAliasEnabled() const;\n  /** Enables sending talker alias. */\n  void enableSendTalkerAlias(bool enable);\n\n  /** Returns the talker alias encoding. */\n  TalkerAliasEncoding talkerAliasEncoding() const;\n  /** Sets the talker alias encoding. */\n  void setTalkerAliasEncoding(TalkerAliasEncoding encoding);\n\n  /** Returns the preamble duration. */\n  Interval preamble() const;\n  /** Sets the preamble duration. */\n  void setPreamble(const Interval &dur);\n\nprotected:\n  /** Enables private call match. */\n  bool _privateCallMatch;\n  /** Enables group call match. */\n  bool _groupCallMatch;\n  /** The private call hang time. */\n  Interval _privateCallHangTime;\n  /** The group call hang time. */\n  Interval _groupCallHangTime;\n  /** Enables sending the talker alias. */\n  bool _sendTalkerAlias;\n  /** The talker alias encoding. */\n  TalkerAliasEncoding _talkerAliasEncoding;\n  /** The preamble duration. */\n  Interval _preamble;\n};\n\n#endif // DMRSETTINGS_HH\n"
  },
  {
    "path": "lib/dr1801uv.cc",
    "content": "#include \"dr1801uv.hh\"\n#include \"dr1801uv_interface.hh\"\n#include \"logger.hh\"\n\n\nRadioLimits * DR1801UV::_limits = nullptr;\n\nDR1801UV::DR1801UV(DR1801UVInterface *device, QObject *parent)\n  : Radio(parent), _device(device), _name(\"Baofeng DR-1801UV\")\n{\n  // Check if device is open\n  if ((nullptr==_device) || (! _device->isOpen())) {\n    _task = StatusError;\n    return;\n  }\n}\n\nRadioInfo\nDR1801UV::defaultRadioInfo() {\n  return RadioInfo(RadioInfo::DR1801UV, \"dr1801uv\",\n                   \"DR-1801UV\", \"Baofeng\",\n                   {DR1801UVInterface::interfaceInfo()});\n}\n\n\nconst QString &\nDR1801UV::name() const {\n  return _name;\n}\n\nconst Codeplug &\nDR1801UV::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nDR1801UV::codeplug() {\n  return _codeplug;\n}\n\nconst RadioLimits &\nDR1801UV::limits() const {\n  if (nullptr == _limits)\n    _limits = new DR1801UVLimits();\n  return *_limits;\n}\n\nbool\nDR1801UV::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_device && _device->isOpen())\n    _device->moveToThread(this);\n\n  start();\n\n  return true;\n}\n\nbool\nDR1801UV::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (! (_config = config))\n    return false;\n  _codeplugFlags = flags;\n\n  _task = StatusUpload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_device && _device->isOpen())\n    _device->moveToThread(this);\n  start();\n  return true;\n}\n\nbool\nDR1801UV::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(selection);\n  errMsg(err) << \"This device does not support a call-sign DB.\";\n  return false;\n}\n\n\nbool\nDR1801UV::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(flags);\n\n  errMsg(err) << \"Satellite config upload is not implemented yet.\";\n\n  return false;\n}\n\n\nvoid\nDR1801UV::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_device) || (! _device->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    emit downloadStarted();\n\n    if (! download()) {\n      _device->read_finish();\n      _device->reboot();\n      _device->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _device->reboot();\n    _device->close();\n    emit downloadFinished(this, &codeplug());\n    //_config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_device) || (! _device->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    emit uploadStarted();\n\n    if (! upload()) {\n      _device->write_finish();\n      _device->reboot();\n      _device->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _device->write_finish();\n    _device->reboot();\n    _device->close();\n    _task = StatusIdle;\n\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    // Not implemented.\n    emit uploadError(this);\n    return;\n  }\n}\n\nbool\nDR1801UV::download() {\n  if (! _device->readCodeplug(_codeplug, [this](unsigned int n, unsigned int total){\n                              emit downloadProgress(float(n*100)/total); }, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot read codeplug from device.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UV::upload() {\n  // First, read codeplug from the device\n  if (! _device->readCodeplug(_codeplug, [this](unsigned int n, unsigned int total) {\n                              emit uploadProgress(float(n*50)/total); }, _errorStack))\n  {\n    errMsg(_errorStack) << \"Cannot read codeplug.\";\n    return false;\n  }\n\n  // Encode config into codeplug\n  _codeplug.encode(_config, _codeplugFlags);\n  _codeplug.data(0x304)[0] = 0;\n\n  // Write codeplug back to the device\n  if (! _device->writeCodeplug(_codeplug, [this](unsigned int n, unsigned int total) {\n                               emit uploadProgress(50+float(n*50)/total); }, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot write codeplug to the device.\";\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dr1801uv.hh",
    "content": "/** @defgroup dr1801uv BTECH DR-1801 UV\n *\n * This module collects all classes implementing the codeplug and communication protocol\n * for the BTECH DR-1801UV. This device is also known as BF-1801 A6 and is a completely different\n * device than the well known DM-1801. The former uses the Auctu A6 radio-on-a-chip.\n *\n * @ingroup auctus */\n#ifndef DR1801UV_HH\n#define DR1801UV_HH\n\n#include \"radio.hh\"\n#include \"dr1801uv_interface.hh\"\n#include \"dr1801uv_codeplug.hh\"\n#include \"dr1801uv_limits.hh\"\n\n/** Implements the BTECH DR-1801UV (BF-1801 A6).\n *\n * @ingroup dr1801uv */\nclass DR1801UV : public Radio\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new instance representig a DR-1801UV. */\n  explicit DR1801UV(DR1801UVInterface *device=nullptr, QObject *parent=nullptr);\n\npublic:\n  /** Returns the default radio info. */\n  static RadioInfo defaultRadioInfo();\n\n  const QString &name() const;\n\n  const RadioLimits &limits() const;\n\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err);\n  bool startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err);\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err);\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err);\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n  void run();\n\nprivate:\n  virtual bool download();\n  virtual bool upload();\n\nprotected:\n  /** Owns the interface to the device. */\n  DR1801UVInterface *_device;\n  /** Holds the device name, once it got identified. */\n  QString _name;\n  /** The binary codeplug. */\n  DR1801UVCodeplug _codeplug;\n  /** The generic configuration. */\n  Config *_config;\n  /** Some codeplug flags. */\n  Codeplug::Flags _codeplugFlags;\n\nprivate:\n  /** Holds the singleton instance of the radio limits. */\n  static RadioLimits *_limits;\n};\n\n#endif // DR1801UV_HH\n"
  },
  {
    "path": "lib/dr1801uv_codeplug.cc",
    "content": "#include \"dr1801uv_codeplug.hh\"\n#include \"logger.hh\"\n#include \"zone.hh\"\n#include \"config.hh\"\n#include \"intermediaterepresentation.hh\"\n\n#include <QRegularExpression>\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ChannelBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr)\n  : Element(ptr, ChannelBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ChannelBankElement::clear() {\n  memset(_data, 0, ChannelBankElement::size());\n}\n\nunsigned int\nDR1801UVCodeplug::ChannelBankElement::channelCount() const {\n  return getUInt16_le(Offset::channelCount());\n}\nvoid\nDR1801UVCodeplug::ChannelBankElement::setChannelCount(unsigned int count) {\n  count = std::min(Limit::channelCount(), count);\n  setUInt16_le(Offset::channelCount(), count);\n}\n\nDR1801UVCodeplug::ChannelElement\nDR1801UVCodeplug::ChannelBankElement::channel(unsigned int index) const {\n  return ChannelElement(_data + Offset::channel() + index*ChannelElement::size());\n}\n\nQString\nDR1801UVCodeplug::ChannelBankElement::channelName(unsigned int index) const {\n  return readASCII(Offset::channelName() + index*Limit::channelNameLength(), Limit::channelNameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::ChannelBankElement::setChannelName(unsigned int index, const QString &name) {\n  writeASCII(Offset::channelName() + index*Limit::channelNameLength(), name, Limit::channelNameLength(), 0x00);\n}\n\nbool\nDR1801UVCodeplug::ChannelBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::channelCount() && j<channelCount(); i++) {\n    ChannelElement ch = channel(i);\n    if (! ch.isValid())\n      continue;\n    j++;\n    Channel *obj = ch.toChannelObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode channel at index \" << i << \".\";\n      return false;\n    }\n    // Set channel name\n    obj->setName(channelName(i));\n    // Add channel to context and config\n    ctx.add(obj, ch.index());\n    ctx.config()->channelList()->add(obj);\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ChannelBankElement::link(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::channelCount() && j<channelCount(); i++) {\n    ChannelElement ch = channel(i);\n    if (! ch.isValid())\n      continue;\n    j++;\n    if (! ctx.has<Channel>(ch.index())) {\n      errMsg(err) << \"Cannot link channel at index \" << i\n                  << \". Channel not defined.\";\n      return false;\n    }\n    Channel *obj = ctx.get<Channel>(ch.index());\n    if (! ch.linkChannelObj(obj, ctx, err)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ChannelBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int n = std::min(Limit::channelCount(), ctx.count<Channel>());\n  setChannelCount(n);\n\n  for (unsigned int i=0; i<n; i++) {\n    ChannelElement ch = channel(i);\n    if (! ch.encode(ctx.get<Channel>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode channel '\" << ctx.get<Channel>(i)\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n    ch.setIndex(i);\n    setChannelName(i, ctx.get<Channel>(i)->name());\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ChannelElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, ChannelElement::size())\n{\n  // pass...\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::isValid() const {\n  return 0xffff != index();\n}\n\nvoid\nDR1801UVCodeplug::ChannelElement::clear() {\n  memset(_data, 0, _size);\n  setIndex(0xffff);\n}\n\nunsigned int\nDR1801UVCodeplug::ChannelElement::index() const {\n  return getUInt16_le(Offset::index());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setIndex(unsigned int idx) {\n  setUInt16_le(Offset::index(), idx);\n}\n\nDR1801UVCodeplug::ChannelElement::Type\nDR1801UVCodeplug::ChannelElement::channelType() const {\n  return (Type)getUInt8(Offset::channelType());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setChannelType(Type type) {\n  setUInt8(Offset::channelType(), (uint8_t)type);\n}\n\nChannel::Power\nDR1801UVCodeplug::ChannelElement::power() const {\n  switch ((Power)getUInt8(Offset::power())) {\n  case Power::Low: return Channel::Power::Low;\n  case Power::High: return Channel::Power::High;\n  }\n  return Channel::Power::Low;\n}\n\nvoid\nDR1801UVCodeplug::ChannelElement::setPower(Channel::Power pwr) {\n  switch (pwr) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt8(Offset::power(), (uint8_t)Power::Low);\n    break;\n  case Channel::Power::Mid:\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setUInt8(Offset::power(), (uint8_t)Power::High);\n    break;\n  }\n}\n\nFrequency\nDR1801UVCodeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(getUInt32_le(Offset::rxFrequency()));\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setRXFrequency(Frequency MHz) {\n  setUInt32_le(Offset::rxFrequency(), MHz.inHz());\n}\nFrequency\nDR1801UVCodeplug::ChannelElement::txFrequency() const {\n  return Frequency::fromHz(getUInt32_le(Offset::txFrequency()));\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setTXFrequency(Frequency MHz) {\n  setUInt32_le(Offset::txFrequency(), MHz.inHz());\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::hasTransmitContact() const {\n  return 0 != getUInt16_le(Offset::transmitContactIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::transmitContactIndex() const {\n  return getUInt16_le(Offset::transmitContactIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setTransmitContactIndex(unsigned int index) {\n  setUInt16_le(Offset::transmitContactIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearTransmitContactIndex() {\n  setUInt16_le(Offset::transmitContactIndex(), 0);\n}\n\nDR1801UVCodeplug::ChannelElement::Admit\nDR1801UVCodeplug::ChannelElement::admitCriterion() const {\n  return (Admit) getUInt8(Offset::admitCriterion());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setAdmitCriterion(Admit admit) {\n  setUInt8(Offset::admitCriterion(), (uint8_t)admit);\n}\n\nunsigned int\nDR1801UVCodeplug::ChannelElement::colorCode() const {\n  return getUInt8(Offset::colorCode());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setColorCode(unsigned int cc) {\n  setUInt8(Offset::colorCode(), cc);\n}\n\nDMRChannel::TimeSlot\nDR1801UVCodeplug::ChannelElement::timeSlot() const {\n  switch ((TimeSlot)getUInt8(Offset::timeSlot())) {\n  case TimeSlot::TS1: return DMRChannel::TimeSlot::TS1;\n  case TimeSlot::TS2: return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeSlot(), (uint8_t)TimeSlot::TS1); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeSlot(), (uint8_t)TimeSlot::TS2); break;\n  }\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::hasEncryptionKey() const {\n  return 0 != getUInt8(Offset::encryptionKeyIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::encryptionKeyIndex() const {\n  return getUInt8(Offset::encryptionKeyIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setEncryptionKeyIndex(unsigned int index) {\n  setUInt8(Offset::encryptionKeyIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearEncryptionKeyIndex() {\n  setUInt8(Offset::encryptionKeyIndex(), 0);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::dcdm() const {\n  return getBit(Offset::dcdm().byte, Offset::dcdm().bit);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::enableDCDM(bool enable) {\n  setBit(Offset::dcdm().byte, Offset::dcdm().bit, enable);\n}\nbool\nDR1801UVCodeplug::ChannelElement::confirmPrivateCall() const {\n  return getBit(Offset::confirmPivateCall().byte, Offset::confirmPivateCall().bit);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::enablePrivateCallConfirmation(bool enable) {\n  setBit(Offset::confirmPivateCall().byte, Offset::confirmPivateCall().bit, enable);\n}\n\nDR1801UVCodeplug::ChannelElement::SignalingMode\nDR1801UVCodeplug::ChannelElement::signalingMode() const {\n  return (SignalingMode) getUInt8(Offset::signalingMode());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setSignalingMode(SignalingMode mode) {\n  setUInt8(Offset::signalingMode(), (uint8_t)mode);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::hasAlarmSystem() const {\n  return 0 != getUInt8(Offset::alarmSystemIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::alarmSystemIndex() const {\n  return getUInt8(Offset::alarmSystemIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setAlarmSystemIndex(unsigned int index) {\n  setUInt8(Offset::alarmSystemIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearAlarmSystemIndex() {\n  setUInt8(Offset::alarmSystemIndex(), 0);\n}\n\nFMChannel::Bandwidth\nDR1801UVCodeplug::ChannelElement::bandwidth() const {\n  switch ((Bandwidth)getUInt8(Offset::bandwidth())) {\n  case Bandwidth::Narrow: return FMChannel::Bandwidth::Narrow;\n  case Bandwidth::Wide: return FMChannel::Bandwidth::Wide;\n  }\n  return FMChannel::Bandwidth::Narrow;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  switch (bw){\n  case FMChannel::Bandwidth::Narrow: setUInt8(Offset::bandwidth(), (uint8_t)Bandwidth::Narrow); break;\n  case FMChannel::Bandwidth::Wide:   setUInt8(Offset::bandwidth(), (uint8_t)Bandwidth::Wide); break;\n  }\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::autoScanEnabled() const {\n  return 0x01 == getUInt8(Offset::autoScan());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::enableAutoScan(bool enable) {\n  setUInt8(Offset::autoScan(), enable ? 0x01 : 0x00);\n}\nbool\nDR1801UVCodeplug::ChannelElement::hasScanList() const {\n  return 0x00 != getUInt8(Offset::scanListIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(Offset::scanListIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setScanListIndex(unsigned int index) {\n  setUInt8(Offset::scanListIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearScanListIndex() {\n  setUInt8(Offset::scanListIndex(), 0);\n}\n\nSelectiveCall\nDR1801UVCodeplug::ChannelElement::rxTone() const {\n  uint16_t ctcss_dcs = getUInt16_le(Offset::rxSubtoneCode());\n  SubToneType type = (SubToneType)getUInt8(Offset::rxSubtoneType());\n  DCSMode dcsMode = (DCSMode)getUInt8(Offset::rxDCSMode());\n  switch (type) {\n  case SubToneType::None: return SelectiveCall();\n  case SubToneType::CTCSS: return SelectiveCall(float(ctcss_dcs)/10);\n  case SubToneType::DCS: return SelectiveCall(ctcss_dcs, DCSMode::Inverted == dcsMode);\n  }\n  return SelectiveCall();\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) {\n  uint16_t ctcss_dcs = 0;\n  SubToneType type = SubToneType::None;\n  DCSMode dcsMode = DCSMode::Normal;\n  if (code.isCTCSS()) {\n    type = SubToneType::CTCSS;\n    ctcss_dcs = code.Hz()*10;\n  } else if (code.isDCS()) {\n    type = SubToneType::DCS;\n    ctcss_dcs = code.octalCode();\n    dcsMode = code.isInverted() ? DCSMode::Inverted : DCSMode::Normal;\n  }\n  setUInt16_le(Offset::rxSubtoneCode(), ctcss_dcs);\n  setUInt8(Offset::rxSubtoneType(), (uint8_t)type);\n  setUInt8(Offset::rxDCSMode(), (uint8_t)dcsMode);\n}\n\nSelectiveCall\nDR1801UVCodeplug::ChannelElement::txTone() const {\n  uint16_t ctcss_dcs = getUInt16_le(Offset::txSubtoneCode());\n  SubToneType type = (SubToneType)getUInt8(Offset::txSubtoneType());\n  DCSMode dcsMode = (DCSMode)getUInt8(Offset::txDCSMode());\n  switch (type) {\n  case SubToneType::None: return SelectiveCall();\n  case SubToneType::CTCSS: return SelectiveCall(float(ctcss_dcs)/10);\n  case SubToneType::DCS: return SelectiveCall(ctcss_dcs, DCSMode::Inverted == dcsMode);\n  }\n  return SelectiveCall();\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) {\n  uint16_t ctcss_dcs = 0;\n  SubToneType type = SubToneType::None;\n  DCSMode dcsMode = DCSMode::Normal;\n  if (code.isCTCSS()) {\n    type = SubToneType::CTCSS;\n    ctcss_dcs = code.Hz()*10;\n  } else if (code.isDCS()) {\n    type = SubToneType::DCS;\n    ctcss_dcs = code.octalCode();\n    dcsMode = code.isInverted() ? DCSMode::Inverted : DCSMode::Normal;\n  }\n  setUInt16_le(Offset::txSubtoneCode(), ctcss_dcs);\n  setUInt8(Offset::txSubtoneType(), (uint8_t)type);\n  setUInt8(Offset::txDCSMode(), (uint8_t)dcsMode);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::talkaround() const {\n  return getBit(Offset::talkaround(), 7);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::enableTalkaround(bool enable) {\n  setBit(Offset::talkaround(), 7, enable);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::hasPTTID() const {\n  return 0 != getUInt8(Offset::pttIDIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::pttIDIndex() const {\n  return getUInt8(Offset::pttIDIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setPTTIDIndex(unsigned int idx) {\n  setUInt8(Offset::pttIDIndex(), idx+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearPTTID() {\n  setUInt8(Offset::pttIDIndex(), 0);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::hasGroupList() const {\n  return 0 != getUInt8(Offset::groupListIndex());\n}\nunsigned int\nDR1801UVCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupListIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ChannelElement::setGroupListIndex(unsigned int index) {\n  setUInt8(Offset::groupListIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ChannelElement::clearGroupListIndex() {\n  setUInt8(Offset::groupListIndex(), 0);\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::loneWorker() const {\n  return 0x01 == getUInt8(Offset::loneWorker());\n}\nvoid\nDR1801UVCodeplug::ChannelElement::enableLoneWorker(bool enable) {\n  setUInt8(Offset::loneWorker(), enable ? 0x01 : 0x00);\n}\n\n\nChannel *\nDR1801UVCodeplug::ChannelElement::toChannelObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx);\n\n  Channel *ch = nullptr;\n  if (Type::FM == channelType()) {\n    FMChannel *fm = new FMChannel(); ch = fm;\n    switch (admitCriterion()) {\n    case Admit::Always: fm->setAdmit(FMChannel::Admit::Always); break;\n    case Admit::ColorCode_or_Tone: fm->setAdmit(FMChannel::Admit::Tone); break;\n    case Admit::ChannelFree: fm->setAdmit(FMChannel::Admit::Free); break;\n    }\n    fm->setBandwidth(bandwidth());\n    fm->setRXTone(rxTone());\n    fm->setTXTone(txTone());\n    fm->extended()->enableTalkaround(talkaround());\n  } else if (Type::DMR == channelType()) {\n    DMRChannel *dmr = new DMRChannel(); ch = dmr;\n    switch (admitCriterion()) {\n    case Admit::Always: dmr->setAdmit(DMRChannel::Admit::Always); break;\n    case Admit::ColorCode_or_Tone: dmr->setAdmit(DMRChannel::Admit::ColorCode); break;\n    case Admit::ChannelFree: dmr->setAdmit(DMRChannel::Admit::Free); break;\n    }\n    dmr->setColorCode(colorCode());\n    dmr->setTimeSlot(timeSlot());\n    dmr->extended()->enableTalkaround(talkaround());\n    dmr->extended()->enableDCDM(dcdm());\n    dmr->extended()->enablePrivateCallConfirm(confirmPrivateCall());\n    dmr->extended()->enableLoneWorker(loneWorker());\n  } else {\n    errMsg(err) <<  \"Unknown channel type \" << (uint8_t)channelType() << \".\";\n    return nullptr;\n  }\n\n  ch->setPower(power());\n  ch->setRXFrequency(rxFrequency());\n  ch->setTXFrequency(txFrequency());\n\n  return ch;\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::linkChannelObj(Channel *channel, Context &ctx, const ErrorStack &err) const {\n\n  // Common references\n  if (hasScanList()) {\n    if (! ctx.has<ScanList>(scanListIndex())) {\n      errMsg(err) << \"Scanlist with index \" << scanListIndex() << \" not known.\";\n      return false;\n    }\n    channel->setScanList(ctx.get<ScanList>(scanListIndex()));\n  }\n\n  // Handle DMR specific references\n  if (DMRChannel *dmr = channel->as<DMRChannel>()) {\n    if (hasTransmitContact()) {\n      if (! ctx.has<DMRContact>(transmitContactIndex())) {\n        errMsg(err) << \"DMR contact with index \" << transmitContactIndex() << \" not known.\";\n        return false;\n      }\n      dmr->setContact(ctx.get<DMRContact>(transmitContactIndex()));\n    }\n    if (hasGroupList()) {\n      if (! ctx.has<RXGroupList>(groupListIndex())) {\n        errMsg(err) << \"Group list with index \" << groupListIndex() << \" not known.\";\n        return false;\n      }\n      dmr->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ChannelElement::encode(Channel *channel, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // Encode common properties\n  setPower(channel->power());\n  setRXFrequency(channel->rxFrequency());\n  setTXFrequency(channel->txFrequency());\n  if (channel->scanList())\n    setScanListIndex(ctx.index(channel->scanList()));\n  else\n    clearScanListIndex();\n\n  // Encode type specific settings\n  if (channel->is<FMChannel>()) {\n    FMChannel *fm = channel->as<FMChannel>();\n    setChannelType(Type::FM);\n    setBandwidth(fm->bandwidth());\n    setRXTone(fm->rxTone());\n    setTXTone(fm->txTone());\n    switch (fm->admit()) {\n    case FMChannel::Admit::Always: setAdmitCriterion(Admit::Always); break;\n    case FMChannel::Admit::Free:   setAdmitCriterion(Admit::ChannelFree); break;\n    case FMChannel::Admit::Tone:   setAdmitCriterion(Admit::ColorCode_or_Tone); break;\n    }\n    enableTalkaround(fm->extended()->talkaround());\n  } else if (channel->is<DMRChannel>()) {\n    DMRChannel *dmr = channel->as<DMRChannel>();\n    setChannelType(Type::DMR);\n    setBandwidth(FMChannel::Bandwidth::Narrow);\n    if (dmr->contact())\n      setTransmitContactIndex(ctx.index(dmr->contact()));\n    else\n      clearTransmitContactIndex();\n    switch (dmr->admit()) {\n    case DMRChannel::Admit::Always:    setAdmitCriterion(Admit::Always); break;\n    case DMRChannel::Admit::Free:      setAdmitCriterion(Admit::ChannelFree); break;\n    case DMRChannel::Admit::ColorCode: setAdmitCriterion(Admit::ColorCode_or_Tone); break;\n    }\n    setColorCode(dmr->colorCode());\n    setTimeSlot(dmr->timeSlot());\n    if (dmr->commercialExtension() && dmr->commercialExtension()->encryptionKey())\n      setEncryptionKeyIndex(ctx.index(dmr->commercialExtension()->encryptionKey()));\n    else\n      clearEncryptionKeyIndex();\n    if (dmr->groupList())\n      setGroupListIndex(ctx.index(dmr->groupList()));\n    else\n      clearGroupListIndex();\n    enableTalkaround(dmr->extended()->talkaround());\n    enableDCDM(dmr->extended()->dcdm());\n    enablePrivateCallConfirmation(dmr->extended()->privateCallConfirm());\n    enableLoneWorker(dmr->extended()->loneWorker());\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ContactBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr)\n  : Element(ptr, ContactBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ContactBankElement::clear() {\n  memset(_data, 0, ContactBankElement::size());\n}\n\nunsigned int\nDR1801UVCodeplug::ContactBankElement::contactCount() const {\n  return getUInt16_le(Offset::contactCount());\n}\nvoid\nDR1801UVCodeplug::ContactBankElement::setContactCount(unsigned int count) {\n  count = std::min(Limit::contactCount(), count);\n  setUInt16_le(Offset::contactCount(), count);\n}\n\nunsigned int\nDR1801UVCodeplug::ContactBankElement::firstIndex() const {\n  return getUInt16_le(Offset::firstIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ContactBankElement::setFirstIndex(unsigned int index) {\n  setUInt16_le(Offset::firstIndex(), index+1);\n}\n\nDR1801UVCodeplug::ContactElement\nDR1801UVCodeplug::ContactBankElement::contact(unsigned int index) const {\n  return ContactElement(_data + Offset::contacts() + index*ContactElement::size());\n}\n\nbool\nDR1801UVCodeplug::ContactBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  // Get first element in list\n  unsigned int currentIndex = firstIndex();\n  ContactElement currentContact = contact(currentIndex);\n\n  for (unsigned int i=0; i<contactCount(); i++) {\n    DMRContact *obj = currentContact.toContactObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode contact element at index \" << currentIndex;\n      return false;\n    }\n    // Add to context and config\n    ctx.add(obj, currentIndex);\n    ctx.config()->contacts()->add(obj);\n    // continue with successor\n    if (currentContact.hasSuccessor()) {\n      currentIndex = currentContact.successorIndex();\n      currentContact = contact(currentIndex);\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ContactBankElement::link(Context &ctx, const ErrorStack &err) const {\n  if (0 == contactCount())\n    return true;\n\n  unsigned int currentIndex = firstIndex();\n  for (unsigned int i=0; i<contactCount(); i++) {\n    ContactElement currentContact = contact(currentIndex);\n    if (! ctx.has<DMRContact>(currentIndex)) {\n      errMsg(err) << \"Cannot link contact at index \" << currentIndex << \", not defined.\";\n    }\n    DMRContact *obj = ctx.get<DMRContact>(currentIndex);\n    if (! currentContact.linkContactObj(obj, ctx, err)) {\n      errMsg(err) << \"Cannot link contact element at index \" << currentIndex;\n      return false;\n    }\n    currentIndex = currentContact.successorIndex();\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ContactBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int n = std::min(Limit::contactCount(), ctx.count<DigitalContact>());\n  setContactCount(n); setFirstIndex(0);\n  for (unsigned int i=0; i<n; i++) {\n    ContactElement contact = this->contact(i);\n    if (! contact.encode(ctx.get<DMRContact>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode contact '\" << ctx.get<DMRContact>(i)->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n    if ((i+1) < n)\n      contact.setSuccessorIndex(i+1);\n    else\n      contact.clearSuccessorIndex();\n  }\n  return true;\n}\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ContactElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, ContactElement::size())\n{\n  // pass...\n}\n\nbool\nDR1801UVCodeplug::ContactElement::isValid() const {\n  return 0 != getUInt8(Offset::nameLength()) &&  // has name\n      0 != getUInt24_le(0x0004) && // has DMR ID\n      0 != getUInt8(0x0007);       // has call type\n}\n\nvoid\nDR1801UVCodeplug::ContactElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::ContactElement::hasSuccessor() const {\n  return 0x0000 != getUInt16_le(Offset::successorIndex());\n}\nuint16_t\nDR1801UVCodeplug::ContactElement::successorIndex() const {\n  return getUInt16_le(Offset::successorIndex())-1;\n}\nvoid\nDR1801UVCodeplug::ContactElement::setSuccessorIndex(uint16_t index) {\n  setUInt16_le(Offset::successorIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::ContactElement::clearSuccessorIndex() {\n  setUInt16_le(Offset::successorIndex(), 0);\n}\n\nuint32_t\nDR1801UVCodeplug::ContactElement::dmrID() const {\n  if (DMRContact::AllCall == type())\n    return 0xffffff;\n  return getUInt24_le(Offset::dmrID());\n}\nvoid\nDR1801UVCodeplug::ContactElement::setDMRID(uint32_t id) {\n  setUInt24_le(Offset::dmrID(), id);\n}\n\nDMRContact::Type\nDR1801UVCodeplug::ContactElement::type() const {\n  switch ((CallType)getUInt8(Offset::callType())) {\n  case CallType::AllCall: return DMRContact::AllCall;\n  case CallType::PrivateCall: return DMRContact::PrivateCall;\n  case CallType::GroupCall: return DMRContact::GroupCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nDR1801UVCodeplug::ContactElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::AllCall:\n    setUInt8(Offset::callType(), (uint8_t)CallType::AllCall);\n    setDMRID(0xffffff);\n    break;\n  case DMRContact::PrivateCall: setUInt8(Offset::callType(), (uint8_t)CallType::PrivateCall); break;\n  case DMRContact::GroupCall: setUInt8(Offset::callType(), (uint8_t)CallType::GroupCall); break;\n  }\n}\n\nQString\nDR1801UVCodeplug::ContactElement::name() const {\n  return readASCII(Offset::name(), getUInt8(Offset::nameLength()), 0x00);\n}\nvoid\nDR1801UVCodeplug::ContactElement::setName(const QString &name) {\n  uint8_t len = std::min(Limit::nameLength(), (unsigned int)name.size());\n  setUInt8(Offset::nameLength(), len);\n  writeASCII(Offset::name(), name, len, 0x00);\n}\n\nDMRContact *\nDR1801UVCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  return new DMRContact(type(), name(), dmrID());\n}\n\nbool\nDR1801UVCodeplug::ContactElement::linkContactObj(DMRContact *contact, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(contact); Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ContactElement::encode(DMRContact *contact, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  setName(contact->name());\n  setCallType(contact->type());\n  setDMRID(contact->number());\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::GroupListBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : Element(ptr, GroupListBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::GroupListBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::GroupListBankElement::groupListCount() const {\n  return getUInt8(Offset::groupListCount());\n}\nvoid\nDR1801UVCodeplug::GroupListBankElement::setGroupListCount(unsigned int count) {\n  count = std::min(Limit::groupListCount(), count);\n  setUInt8(Offset::groupListCount(), count);\n}\n\nDR1801UVCodeplug::GroupListElement\nDR1801UVCodeplug::GroupListBankElement::groupList(unsigned int index) const {\n  return GroupListElement(_data + Offset::groupLists() + index*GroupListElement::size());\n}\n\nbool\nDR1801UVCodeplug::GroupListBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::groupListCount() && j<groupListCount(); i++) {\n    GroupListElement gl = groupList(i);\n    if (! gl.isValid())\n      continue;\n    j++;\n    RXGroupList *obj = gl.toGroupListObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode group list at index \" << i << \".\";\n      return false;\n    }\n    // Add group list to context and config\n    ctx.add(obj, gl.index());\n    ctx.config()->rxGroupLists()->add(obj);\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::GroupListBankElement::link(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::groupListCount() && j<groupListCount(); i++) {\n    GroupListElement gl = groupList(i);\n    if (! gl.isValid())\n      continue;\n    j++;\n    if (! ctx.has<RXGroupList>(gl.index())) {\n      errMsg(err) << \"Cannot link group list at index \" << i\n                  << \". Group list not defined.\";\n      return false;\n    }\n    if (! gl.linkGroupListObj(ctx.get<RXGroupList>(gl.index()), ctx, err)) {\n      errMsg(err) << \"Cannot link group list at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int n = std::min(Limit::groupListCount(), ctx.count<RXGroupList>());\n  setGroupListCount(n);\n  for (unsigned int i=0; i<n; i++) {\n    GroupListElement lst = groupList(i);\n    if (! lst.encode(ctx.get<RXGroupList>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode group list '\" << ctx.get<RXGroupList>(i)->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n    lst.setIndex(i);\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::GroupListElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element(ptr, GroupListElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::GroupListElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::GroupListElement::isValid() const {\n  return 0 != getUInt16_le(Offset::index());\n}\n\nunsigned int\nDR1801UVCodeplug::GroupListElement::index() const {\n  return getUInt16_le(Offset::index())-1;\n}\nvoid\nDR1801UVCodeplug::GroupListElement::setIndex(unsigned int index) {\n  setUInt16_le(Offset::index(), index+1);\n}\n\nunsigned int\nDR1801UVCodeplug::GroupListElement::count() const {\n  return getUInt16_le(Offset::count());\n}\nvoid\nDR1801UVCodeplug::GroupListElement::setCount(unsigned int n) {\n  n = std::min(Limit::members(), n);\n  setUInt16_le(Offset::count(), n);\n}\n\nbool\nDR1801UVCodeplug::GroupListElement::hasMemberIndex(unsigned int n) const {\n  return 0 != getUInt16_le(Offset::members() + n*0x02);\n}\nunsigned int DR1801UVCodeplug::GroupListElement::memberIndex(unsigned int n) const {\n  return getUInt16_le(Offset::members() + n*0x02) - 1;\n}\nvoid\nDR1801UVCodeplug::GroupListElement::setMemberIndex(unsigned int n, unsigned int index) {\n  setUInt16_le(Offset::members() + n*0x02, index+1);\n}\nvoid\nDR1801UVCodeplug::GroupListElement::clearMemberIndex(unsigned int n) {\n  setUInt16_le(Offset::members() + n*0x02, 0);\n}\n\nRXGroupList *\nDR1801UVCodeplug::GroupListElement::toGroupListObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  // Simply derive name from index\n  return new RXGroupList(QString(\"Group List %1\").arg(index()+1));\n}\n\nbool\nDR1801UVCodeplug::GroupListElement::linkGroupListObj(RXGroupList *list, Context &ctx, const ErrorStack &err) const {\n  if (! isValid())\n    return false;\n\n  for (unsigned int i=0; i<count(); i++) {\n    if (! hasMemberIndex(i))\n      continue;\n    if (! ctx.has<DMRContact>(memberIndex(i))) {\n      errMsg(err) << \"Member index \" << memberIndex(i) << \" is not known.\";\n      return false;\n    }\n    list->addContact(ctx.get<DMRContact>(memberIndex(i)));\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::GroupListElement::encode(RXGroupList *list, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  unsigned int n = std::min(Limit::members(), (unsigned int)list->count());\n  setCount(n);\n  for (unsigned int i=0; i<n; i++) {\n    setMemberIndex(i, ctx.index(list->contact(i)));\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ZoneBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr)\n  : Element(ptr, ZoneBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ZoneBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::ZoneBankElement::zoneCount() const {\n  return getUInt8(Offset::zoneCount());\n}\nvoid\nDR1801UVCodeplug::ZoneBankElement::setZoneCount(unsigned int count) {\n  count = std::min(Limit::zoneCount(), count);\n  setUInt8(Offset::zoneCount(), count);\n}\n\nunsigned int\nDR1801UVCodeplug::ZoneBankElement::upZoneIndex() const {\n  return getUInt16_le(Offset::upZoneIndex());\n}\nvoid\nDR1801UVCodeplug::ZoneBankElement::setUpZoneIndex(unsigned int index) {\n  setUInt16_le(Offset::upZoneIndex(), index);\n}\n\nunsigned int\nDR1801UVCodeplug::ZoneBankElement::downZoneIndex() const {\n  return getUInt16_le(Offset::downZoneIndex());\n}\nvoid\nDR1801UVCodeplug::ZoneBankElement::setDownZoneIndex(unsigned int index) {\n  setUInt16_le(Offset::downZoneIndex(), index);\n}\n\nDR1801UVCodeplug::ZoneElement\nDR1801UVCodeplug::ZoneBankElement::zone(unsigned int index) const {\n  return ZoneElement(_data + Offset::zones() + index*ZoneElement::size());\n}\n\nbool\nDR1801UVCodeplug::ZoneBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::zoneCount() && j<zoneCount(); i++) {\n    ZoneElement zone(this->zone(i));\n    if (! zone.isValid())\n      continue;\n    j++;\n\n    Zone *obj = zone.toZoneObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot create zone at index \" << i << \".\";\n      return false;\n    }\n\n    // Store zone in context\n    ctx.add(obj, zone.index());\n    // Store zone in config\n    ctx.config()->zones()->add(obj);\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ZoneBankElement::link(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0, j=0; i<Limit::zoneCount() && j<zoneCount(); i++) {\n    ZoneElement zone(this->zone(i));\n    if (! zone.isValid())\n      continue;\n    j++;\n\n    if (! ctx.has<Zone>(zone.index())) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \", not defined.\";\n      return false;\n    }\n\n    if (! zone.linkZoneObj(ctx.get<Zone>(zone.index()), ctx, err)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) {\n  setZoneCount(ctx.count<Zone>());\n  for (unsigned int i=0; i<ctx.count<Zone>(); i++) {\n    ZoneElement zone = this->zone(i);\n    if (! zone.encode(ctx.get<Zone>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode zone bank.\";\n      return false;\n    }\n    zone.setIndex(i);\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ZoneElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element(ptr, ZoneElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ZoneElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::ZoneElement::isValid() const {\n  // Read name-length and channel count\n  return (0 != getUInt8(Offset::nameLength())) && (0 != getUInt8(Offset::numEntries()));\n}\n\nQString\nDR1801UVCodeplug::ZoneElement::name() const {\n  uint8_t n = getUInt8(Offset::nameLength());\n  return readASCII(Offset::name(), n, 0x00);\n}\nvoid\nDR1801UVCodeplug::ZoneElement::setName(const QString &name) {\n  uint8_t n = std::min(qsizetype(32), name.length());\n  setUInt8(Offset::nameLength(), n);\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nunsigned int\nDR1801UVCodeplug::ZoneElement::index() const {\n  return getUInt16_le(Offset::index());\n}\nvoid\nDR1801UVCodeplug::ZoneElement::setIndex(unsigned int index) {\n  setUInt16_le(Offset::index(), index);\n}\n\nunsigned int\nDR1801UVCodeplug::ZoneElement::numEntries() const {\n  return getUInt8(Offset::numEntries());\n}\nunsigned int\nDR1801UVCodeplug::ZoneElement::entryIndex(unsigned int n) {\n  n = std::min(Limit::memberCount(), n);\n  return getUInt16_le(Offset::members()+2*n);\n}\nvoid\nDR1801UVCodeplug::ZoneElement::setEntryIndex(unsigned int n, unsigned int index) {\n  n = std::min(Limit::memberCount(), n);\n  setUInt16_le(Offset::members() + 2*n, index);\n}\n\nZone *\nDR1801UVCodeplug::ZoneElement::toZoneObj(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot create zone from invalid zone element.\";\n    return nullptr;\n  }\n\n  return new Zone(name());\n}\n\nbool\nDR1801UVCodeplug::ZoneElement::linkZoneObj(Zone *obj, Context &ctx, const ErrorStack &err) {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link zone using invalid zone element.\";\n    return false;\n  }\n\n  for (unsigned int i=0; i<numEntries(); i++) {\n    if (! ctx.has<Channel>(entryIndex(i))) {\n      errMsg(err) << \"Channel with index \" << entryIndex(i) << \" not defined.\";\n      return false;\n    }\n    obj->A()->add(ctx.get<Channel>(entryIndex(i)));\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ZoneElement::encode(Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  setName(zone->name());\n  unsigned int n = std::min(Limit::memberCount(), (unsigned int)zone->A()->count());\n  setUInt8(Offset::numEntries(), n);\n  for (unsigned int i=0; i<n; i++) {\n    setEntryIndex(i, ctx.index(zone->A()->get(i)->as<Channel>()));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::SettingsElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::SettingsElement::SettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::SettingsElement::SettingsElement(uint8_t *ptr)\n  : Element(ptr, SettingsElement::size())\n{\n  // psas...\n}\n\nvoid\nDR1801UVCodeplug::SettingsElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::SettingsElement::dmrID() const {\n  return getUInt24_le(Offset::dmrID());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setDMRID(unsigned int id) {\n  setUInt24_le(Offset::dmrID(), id);\n}\n\nDR1801UVCodeplug::SettingsElement::PowerSaveMode\nDR1801UVCodeplug::SettingsElement::powerSaveMode() const {\n  if (0x00 == getUInt8(Offset::powerSaveEnabled())) {\n    // Power save disabled\n    return PowerSaveMode::Off;\n  }\n  return (PowerSaveMode)getUInt8(Offset::powerSaveMode());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setPowerSaveMode(PowerSaveMode mode) {\n  setUInt8(Offset::powerSaveEnabled(), PowerSaveMode::Off != mode ? 0x01 : 0x00);\n  setUInt8(Offset::powerSaveMode(), (uint8_t) mode);\n}\n\n\nLevel\nDR1801UVCodeplug::SettingsElement::voxSensitivity() const {\n  if (0x00 == getUInt8(Offset::voxEnabled())) {\n    return Level::null();\n  }\n  return Level::fromValue(getUInt8(Offset::voxSensitivity()), Limit::vox());\n}\n\nvoid\nDR1801UVCodeplug::SettingsElement::setVOXSensitivity(Level sens) {\n  if (sens.isNull()) {\n    setUInt8(Offset::voxEnabled(), 0x00);\n  } else {\n    setUInt8(Offset::voxEnabled(), 0x01);\n    setUInt8(Offset::voxSensitivity(), sens.mapTo(Limit::vox()));\n  }\n}\n\n\nInterval\nDR1801UVCodeplug::SettingsElement::voxDelay() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::voxDelay())*500);\n}\n\nvoid\nDR1801UVCodeplug::SettingsElement::setVOXDelay(Interval delay) {\n  if (! delay.isFinite())\n    setUInt8(Offset::voxDelay(), 0);\n  setUInt8(Offset::voxDelay(), delay.milliseconds()/500);\n}\n\n\nbool\nDR1801UVCodeplug::SettingsElement::encryptionEnabled() const {\n  return 0x01 == getUInt8(Offset::encryptionEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableEncryption(bool enable) {\n  setUInt8(Offset::encryptionEnabled(), enable ? 0x01 : 0x00);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::keyLockEnabled() const {\n  return 0x01 == getUInt8(Offset::keyLockEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableKeyLock(bool enable) {\n  setUInt8(Offset::keyLockEnabled(), enable ? 0x01 : 0x00);\n}\nunsigned int\nDR1801UVCodeplug::SettingsElement::keyLockDelay() const {\n  return getUInt8(Offset::keyLockDelay());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setKeyLockDelay(unsigned int sec) {\n  setUInt8(Offset::keyLockDelay(), sec);\n}\nbool\nDR1801UVCodeplug::SettingsElement::lockSideKey1() const {\n  return getBit(Offset::lockSideKey1().byte, Offset::lockSideKey1().bit);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableLockSideKey1(bool enable) {\n  setBit(Offset::lockSideKey1().byte, Offset::lockSideKey1().bit, enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::lockSideKey2() const {\n  return getBit(Offset::lockSideKey2().byte, Offset::lockSideKey2().bit);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableLockSideKey2(bool enable) {\n  setBit(Offset::lockSideKey2().byte, Offset::lockSideKey2().bit, enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::lockPTT() const {\n  return getBit(Offset::lockPTT().byte, Offset::lockPTT().bit);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableLockPTT(bool enable) {\n  setBit(Offset::lockPTT().byte, Offset::lockPTT().bit, enable);\n}\n\nDR1801UVCodeplug::SettingsElement::Language\nDR1801UVCodeplug::SettingsElement::language() const {\n  return (Language) getUInt8(Offset::language());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setLanguage(Language lang) {\n  setUInt8(Offset::language(), (uint8_t)lang);\n}\n\nDR1801UVCodeplug::SettingsElement::SquelchMode\nDR1801UVCodeplug::SettingsElement::squelchMode() const {\n  return (SquelchMode) getUInt8(Offset::squelchMode());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setSquelchMode(SquelchMode mode) {\n  setUInt8(Offset::squelchMode(), (uint8_t)mode);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::rogerTonesEnabled() const {\n  return 0x01 == getUInt8(Offset::rogerTonesEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableRogerTones(bool enable) {\n  setUInt8(Offset::rogerTonesEnabled(), enable ? 0x01 : 0x00);\n}\nbool\nDR1801UVCodeplug::SettingsElement::dmrCallOutToneEnabled() const {\n  return getBit(Offset::dmrCallOutToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableDMRCallOutTone(bool enable) {\n  setBit(Offset::dmrCallOutToneEnabled(), enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::fmCallOutToneEnabled() const {\n  return getBit(Offset::fmCallOutToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableFMCallOutTone(bool enable) {\n  setBit(Offset::fmCallOutToneEnabled(), enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::dmrVoiceEndToneEnabled() const {\n  return getBit(Offset::dmrVoiceEndToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableDMRVoiceEndTone(bool enable) {\n  setBit(Offset::dmrVoiceEndToneEnabled(), enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::fmVoiceEndToneEnabled() const {\n  return getBit(Offset::fmVoiceEndToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableFMVoiceEndTone(bool enable) {\n  setBit(Offset::fmVoiceEndToneEnabled(), enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::dmrCallEndToneEnabled() const {\n  return getBit(Offset::dmrCallEndToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableDMRCallEndTone(bool enable) {\n  setBit(Offset::dmrCallEndToneEnabled(), enable);\n}\nbool\nDR1801UVCodeplug::SettingsElement::messageToneEnabled() const {\n  return getBit(Offset::messageToneEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableMessageTone(bool enable) {\n  setBit(Offset::messageToneEnabled(), enable);\n}\n\nDR1801UVCodeplug::SettingsElement::RingTone\nDR1801UVCodeplug::SettingsElement::ringTone() const {\n  return (RingTone) getUInt8(Offset::ringTone());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setRingTone(RingTone tone) {\n  setUInt8(Offset::ringTone(), (uint8_t)tone);\n}\n\nQString\nDR1801UVCodeplug::SettingsElement::radioName() const {\n  return readASCII(Offset::radioName(), Limit::radioNameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setRadioName(const QString &name) {\n  writeASCII(Offset::radioName(), name, Limit::radioNameLength(), 0x00);\n}\n\nfloat\nDR1801UVCodeplug::SettingsElement::reverseBurstFrequency() const {\n  return float(getUInt16_le(Offset::reverseBurstFrequency()))/10;\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setReverseBurstFrequency(float Hz) {\n  setUInt16_le(Offset::reverseBurstFrequency(), Hz*10);\n}\n\nDR1801UVCodeplug::SettingsElement::BacklightTime\nDR1801UVCodeplug::SettingsElement::backlightTime() const {\n  return (BacklightTime) getUInt8(Offset::backlightTime());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setBacklightTime(BacklightTime time) {\n  setUInt8(Offset::backlightTime(), (uint8_t)time);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::campandingEnabled() const {\n  return 0x01 == getUInt8(Offset::campandingEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableCampanding(bool enable) {\n  setUInt8(Offset::campandingEnabled(), enable ? 0x01 : 0x00);\n}\n\nDR1801UVCodeplug::SettingsElement::TuningMode\nDR1801UVCodeplug::SettingsElement::tuningModeUp() const {\n  return (TuningMode) getUInt8(Offset::tuningModeUp());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setTuningModeUp(TuningMode mode) {\n  setUInt8(Offset::tuningModeUp(), (uint8_t)mode);\n}\nDR1801UVCodeplug::SettingsElement::TuningMode\nDR1801UVCodeplug::SettingsElement::tuningModeDown() const {\n  return (TuningMode) getUInt8(Offset::tunigModeDown());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setTuningModeDown(TuningMode mode) {\n  setUInt8(Offset::tunigModeDown(), (uint8_t)mode);\n}\n\nDR1801UVCodeplug::SettingsElement::DisplayMode\nDR1801UVCodeplug::SettingsElement::displayMode() const {\n  return (DisplayMode)getUInt8(Offset::displayMode());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setDisplayMode(DisplayMode mode) {\n  setUInt8(Offset::displayMode(), (uint8_t)mode);\n}\n\nDR1801UVCodeplug::SettingsElement::DualWatchMode\nDR1801UVCodeplug::SettingsElement::dualWatchMode() const {\n  return (DualWatchMode) getUInt8(Offset::dualWatchMode());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setDualWatchMode(DualWatchMode mode) {\n  setUInt8(Offset::dualWatchMode(), (uint8_t)mode);\n}\n\nDR1801UVCodeplug::SettingsElement::ScanMode\nDR1801UVCodeplug::SettingsElement::scanMode() const {\n  return (ScanMode) getUInt8(Offset::scanMode());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setScanMode(ScanMode mode) {\n  setUInt8(Offset::scanMode(), (uint8_t)mode);\n}\n\n\nBootSettings::BootDisplay\nDR1801UVCodeplug::SettingsElement::bootScreen() const {\n  switch ((BootScreen) getUInt8(Offset::bootScreen())) {\n  case BootScreen::Text: return BootSettings::BootDisplay::Text;\n  case BootScreen::Picture: return BootSettings::BootDisplay::Image;\n  }\n  return BootSettings::BootDisplay::Logo;\n}\n\nvoid\nDR1801UVCodeplug::SettingsElement::setBootScreen(BootSettings::BootDisplay mode) {\n  switch (mode) {\n  case BootSettings::BootDisplay::Text: setUInt8(Offset::bootScreen(), (uint8_t)BootScreen::Text); break;\n  case BootSettings::BootDisplay::Logo:\n  case BootSettings::BootDisplay::Image: setUInt8(Offset::bootScreen(), (uint8_t)BootScreen::Picture); break;\n  }\n}\n\n\nQString\nDR1801UVCodeplug::SettingsElement::bootLine1() const {\n  return readASCII(Offset::bootLine1(), Limit::bootLineLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setBootLine1(const QString &line) {\n  writeASCII(Offset::bootLine1(), line, Limit::bootLineLength(), 0x00);\n}\nQString\nDR1801UVCodeplug::SettingsElement::bootLine2() const {\n  return readASCII(Offset::bootLine2(), Limit::bootLineLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setBootLine2(const QString &line) {\n  writeASCII(Offset::bootLine2(), line, Limit::bootLineLength(), 0x00);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::ledEnabled() const {\n  return 0x01 == getUInt8(Offset::ledEnabled());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::enableLED(bool enabled) {\n  setUInt8(Offset::ledEnabled(), enabled ? 0x01 : 0x00);\n}\n\nunsigned int\nDR1801UVCodeplug::SettingsElement::loneWorkerResponseTime() const {\n  return getUInt8(Offset::loneWorkerResponseTime());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setLoneWorkerResponseTime(unsigned int sec) {\n  setUInt8(Offset::loneWorkerResponseTime(), sec);\n}\nunsigned int\nDR1801UVCodeplug::SettingsElement::loneWorkerReminderTime() const {\n  return getUInt8(Offset::loneWorkerReminderTime());\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setLoneWorkerReminderTime(unsigned int sec) {\n  setUInt8(Offset::loneWorkerReminderTime(), sec);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::bootPasswordEnabled() const {\n  return getBit(Offset::bootPasswordEnabled());\n}\nQString\nDR1801UVCodeplug::SettingsElement::bootPassword() const {\n  return readASCII(Offset::bootPassword(), Limit::bootPasswordLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setBootPassword(const QString &passwd) {\n  setBit(Offset::bootPasswordEnabled(), true);\n  setUInt8(Offset::boolPasswordLength(),\n           std::min(Limit::bootPasswordLength(), (unsigned int)passwd.length()));\n  writeASCII(Offset::bootPassword(), passwd, Limit::bootPasswordLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::clearBootPassword() {\n  setBit(Offset::bootPasswordEnabled(), false);\n  setUInt8(Offset::boolPasswordLength(), 0);\n  writeASCII(Offset::bootPassword(), \"\", Limit::bootPasswordLength(), 0x00);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::progPasswordEnabled() const {\n  return getBit(Offset::progPasswordEnabled());\n}\nQString\nDR1801UVCodeplug::SettingsElement::progPassword() const {\n  return readASCII(Offset::progPassword(), Limit::progPasswordLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::setProgPassword(const QString &passwd) {\n  setBit(Offset::progPasswordEnabled(), true);\n  setUInt8(Offset::progPasswordLength(),\n           std::min(Limit::progPasswordLength(), (unsigned int)passwd.length()));\n  writeASCII(Offset::progPassword(), passwd, Limit::progPasswordLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::SettingsElement::clearProgPassword() {\n  setBit(Offset::progPasswordEnabled(), false);\n  setUInt8(Offset::progPasswordLength(), 0);\n  writeASCII(Offset::progPassword(), \"\", Limit::progPasswordLength(), 0x00);\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::decode(Config *config, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // Store radio ID\n  auto idx = config->radioIDs()->add(new DMRRadioID(radioName(), dmrID()));\n  config->settings()->setDefaultId(config->radioIDs()->get(idx)->as<DMRRadioID>());\n\n  // Handle VOX settings.\n  config->settings()->audio()->setVox(voxSensitivity());\n  config->settings()->audio()->setVOXDelay(voxDelay());\n\n  // Handle tone settings\n  config->settings()->tone()->enableRingtone(RingTone::Off != ringTone());\n  config->settings()->tone()->enableSMSTone(messageToneEnabled());\n\n  // Handle boot settings\n  config->settings()->boot()->setBootDisplay(bootScreen());\n  config->settings()->boot()->setMessage1(bootLine1());\n  config->settings()->boot()->setMessage2(bootLine2());\n  config->settings()->boot()->enableBootPassword(bootPasswordEnabled());\n  config->settings()->boot()->setBootPassword(bootPassword());\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::SettingsElement::encode(Config *config, const ErrorStack &err) {\n  // Store radio ID\n  DMRRadioID *id = config->settings()->defaultId();\n  if (nullptr == id) {\n    errMsg(err) << \"Cannot encode radio ID and name. No default DMR radio ID.\";\n    return false;\n  }\n  setRadioName(id->name());\n  setDMRID(id->number());\n\n  setVOXSensitivity(config->settings()->audio()->vox());\n  setVOXDelay(config->settings()->audio()->voxDelay());\n\n  // Handle tone settings\n  setRingTone(config->settings()->tone()->ringtoneEnabled() ? RingTone::RingTone1 : RingTone::Off);\n  enableMessageTone(config->settings()->tone()->smsToneEnabled());\n\n  // Encode boot settings\n  setBootScreen(config->settings()->boot()->bootDisplay());\n  setBootLine1(config->settings()->boot()->message1());\n  setBootLine2(config->settings()->boot()->message2());\n  if (config->settings()->boot()->bootPasswordEnabled())\n    setBootPassword(config->settings()->boot()->bootPassword());\n  else\n    clearBootPassword();\n\n  return true;\n}\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ScanListBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr)\n  : Element(ptr, ScanListBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ScanListBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::ScanListBankElement::scanListCount() const {\n  return getUInt8(Offset::scanListCount());\n}\nvoid\nDR1801UVCodeplug::ScanListBankElement::setScanListCount(unsigned int count) {\n  count = std::min(Limit::scanListCount(), count);\n  setUInt8(Offset::scanListCount(), count);\n}\n\nDR1801UVCodeplug::ScanListElement\nDR1801UVCodeplug::ScanListBankElement::scanList(unsigned int index) const {\n  return ScanListElement(_data + Offset::scanLists() + index*ScanListElement::size());\n}\n\nbool\nDR1801UVCodeplug::ScanListBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0, j=0; i<Limit::scanListCount() && j<scanListCount(); i++) {\n    ScanListElement sl = scanList(i);\n    if (! sl.isValid())\n      continue;\n    j++;\n\n    ScanList *obj = sl.toScanListObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode scan list at index \" << i << \".\";\n      return false;\n    }\n    // Add scan list to ctx and config\n    ctx.add(obj, sl.index());\n    ctx.config()->scanlists()->add(obj);\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ScanListBankElement::link(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0,j=0; i<Limit::scanListCount() && j<scanListCount(); i++) {\n    ScanListElement sl = scanList(i);\n    if (! sl.isValid())\n      continue;\n    j++;\n\n    if (! ctx.has<ScanList>(sl.index())) {\n      errMsg(err) << \"Cannot link scan list at index \" << i\n                  << \". Scan list not defined.\";\n      return false;\n    }\n    if (! sl.linkScanListObj(ctx.get<ScanList>(sl.index()), ctx, err)) {\n      errMsg(err) << \"Cannot link scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ScanListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int n = std::min(Limit::scanListCount(), ctx.count<ScanList>());\n  setScanListCount(n);\n  for (unsigned int i=0; i<n; i++) {\n    ScanListElement scan = this->scanList(i);\n    if (! scan.encode(ctx.get<ScanList>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode scan list '\" << ctx.get<ScanList>(i)->name()\n                  << \" at index \" << i << \".\";\n      return false;\n    }\n    scan.setIndex(i);\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::ScanListElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element(ptr, ScanListElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::ScanListElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::ScanListElement::isValid() const {\n  return 0 != getUInt8(Offset::index()); // Check index\n}\n\nunsigned int\nDR1801UVCodeplug::ScanListElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setIndex(unsigned int idx) {\n  setUInt8(Offset::index(), idx+1);\n}\n\nunsigned int\nDR1801UVCodeplug::ScanListElement::entryCount() const {\n  return getUInt8(Offset::memberCount());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setEntryCount(unsigned int num) {\n  setUInt8(Offset::memberCount(), num);\n}\n\nDR1801UVCodeplug::ScanListElement::PriorityChannel\nDR1801UVCodeplug::ScanListElement::priorityChannel1() const {\n  return (PriorityChannel) getUInt8(Offset::priorityChannel1());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setPriorityChannel1(PriorityChannel mode) {\n  setUInt8(Offset::priorityChannel1(), (uint8_t) mode);\n}\nunsigned int\nDR1801UVCodeplug::ScanListElement::priorityChannel1Index() const {\n  return getUInt16_le(Offset::priorityChannel1Index());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setPriorityChannel1Index(unsigned int index) {\n  setUInt16_le(Offset::priorityChannel1Index(), index);\n}\n\nDR1801UVCodeplug::ScanListElement::PriorityChannel\nDR1801UVCodeplug::ScanListElement::priorityChannel2() const {\n  return (PriorityChannel) getUInt8(Offset::priorityChannel2());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setPriorityChannel2(PriorityChannel mode) {\n  setUInt8(Offset::priorityChannel2(), (uint8_t) mode);\n}\nunsigned int\nDR1801UVCodeplug::ScanListElement::priorityChannel2Index() const {\n  return getUInt16_le(Offset::priorityChannel2Index());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setPriorityChannel2Index(unsigned int index) {\n  setUInt16_le(Offset::priorityChannel2Index(), index);\n}\n\nDR1801UVCodeplug::ScanListElement::RevertChannel\nDR1801UVCodeplug::ScanListElement::revertChannel() const {\n  return (RevertChannel) getUInt8(Offset::revertChannel());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setRevertChannel(RevertChannel mode) {\n  setUInt8(Offset::revertChannel(), (uint8_t) mode);\n}\nunsigned int\nDR1801UVCodeplug::ScanListElement::revertChannelIndex() const {\n  return getUInt16_le(Offset::revertChannelIndex());\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setRevertChannelIndex(unsigned int index) {\n  setUInt16_le(Offset::revertChannelIndex(), index);\n}\n\nQString\nDR1801UVCodeplug::ScanListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nunsigned int\nDR1801UVCodeplug::ScanListElement::entryIndex(unsigned int n) {\n  return getUInt16_le(Offset::memberIndices() + 2*n);\n}\nvoid\nDR1801UVCodeplug::ScanListElement::setEntryIndex(unsigned int n, unsigned int index) {\n  setUInt16_le(Offset::memberIndices()+2*n, index);\n}\n\nScanList *\nDR1801UVCodeplug::ScanListElement::toScanListObj(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  return new ScanList(name());\n}\n\nbool\nDR1801UVCodeplug::ScanListElement::linkScanListObj(ScanList *obj, Context &ctx, const ErrorStack &err) {\n  // Link priority channels.\n  switch (priorityChannel1()) {\n  case PriorityChannel::Selected:\n    obj->setPrimaryChannel(SelectedChannel::get());\n    break;\n  case PriorityChannel::Fixed:\n    if (! ctx.has<Channel>(priorityChannel1Index())) {\n      errMsg(err) << \"Cannot link to priority channel 1: Channel with index \" << priorityChannel1Index() << \" not defined.\";\n      return false;\n    }\n    obj->setPrimaryChannel(ctx.get<Channel>(priorityChannel1Index()));\n    break;\n  case PriorityChannel::None: break;\n  }\n\n  switch (priorityChannel2()) {\n  case PriorityChannel::Selected:\n    obj->setSecondaryChannel(SelectedChannel::get());\n    break;\n  case PriorityChannel::Fixed:\n    if (! ctx.has<Channel>(priorityChannel2Index())) {\n      errMsg(err) << \"Cannot link to priority channel 2: Channel with index \" << priorityChannel2Index() << \" not defined.\";\n      return false;\n    }\n    obj->setSecondaryChannel(ctx.get<Channel>(priorityChannel2Index()));\n    break;\n  case PriorityChannel::None: break;\n  }\n\n  for (unsigned int i=0; i<entryCount(); i++) {\n    if (! ctx.has<Channel>(entryIndex(i))) {\n      errMsg(err) << \"Cannot link scan-list entry \" << i\n                  << \": Channel with index \" << entryIndex(i) << \" not defined.\";\n      return false;\n    }\n    obj->addChannel(ctx.get<Channel>(entryIndex(i)));\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::ScanListElement::encode(ScanList *obj, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setName(obj->name());\n\n  if (nullptr == obj->primaryChannel())\n    setPriorityChannel1(PriorityChannel::None);\n  else if (obj->primaryChannel() && (SelectedChannel::get() == obj->primaryChannel())) {\n    setPriorityChannel1(PriorityChannel::Selected);\n  } else {\n    setPriorityChannel1(PriorityChannel::Fixed);\n    setPriorityChannel1Index(ctx.index(obj->primaryChannel()));\n  }\n\n  if (nullptr == obj->secondaryChannel())\n    setPriorityChannel2(PriorityChannel::None);\n  else if (obj->secondaryChannel() && (SelectedChannel::get() == obj->secondaryChannel())) {\n    setPriorityChannel2(PriorityChannel::Selected);\n  } else {\n    setPriorityChannel2(PriorityChannel::Fixed);\n    setPriorityChannel2Index(ctx.index(obj->secondaryChannel()));\n  }\n\n  if (nullptr == obj->revertChannel())\n    setRevertChannel(RevertChannel::LastActive);\n  else if (obj->revertChannel() && (SelectedChannel::get() == obj->revertChannel())) {\n    setRevertChannel(RevertChannel::Selected);\n  } else {\n    setRevertChannel(RevertChannel::Fixed);\n    setRevertChannelIndex(ctx.index(obj->revertChannel()));\n  }\n\n  unsigned int n = std::min(Limit::memberCount(), (unsigned int)obj->count());\n  setEntryCount(n);\n  for (unsigned int i=0; i<n; i++) {\n    setEntryIndex(i, ctx.index(obj->channel(i)));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::MessageBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr)\n  : Element(ptr, MessageBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::MessageBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::MessageBankElement::messageCount() const {\n  return getUInt8(Offset::messageCount());\n}\nvoid\nDR1801UVCodeplug::MessageBankElement::setMessageCount(unsigned int count) {\n  setUInt8(Offset::messageCount(), count);\n}\n\nDR1801UVCodeplug::MessageElement\nDR1801UVCodeplug::MessageBankElement::message(unsigned int n) const {\n  return MessageElement(_data + Offset::messages() + n*MessageElement::size());\n}\n\nbool\nDR1801UVCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err);\n\n  ctx.config()->smsExtension()->smsTemplates()->clear();\n\n  for (unsigned int i=0,j=0; i<Limit::messageCount() && j<messageCount(); i++) {\n    MessageElement msg = message(i);\n    if (! msg.isValid())\n      continue;\n    j++;\n    SMSTemplate *sms = new SMSTemplate();\n    sms->setName(QString(\"Message %1\").arg(msg.index()));\n    sms->setMessage(msg.text());\n    ctx.config()->smsExtension()->smsTemplates()->add(sms);\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::MessageBankElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setMessageCount(0);\n\n  SMSExtension *smsExt = ctx.config()->smsExtension();\n  unsigned int count = std::min(Limit::messageCount(), (unsigned int)smsExt->smsTemplates()->count());\n  for (unsigned int i=0; i<count; i++) {\n    MessageElement msg = message(i);\n    msg.setText(smsExt->smsTemplates()->message(i)->message());\n    msg.setIndex(i+1);\n  }\n\n  setMessageCount(count);\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::MessageElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::MessageElement::MessageElement(uint8_t *ptr)\n  : Element(ptr, MessageElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::MessageElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::MessageElement::isValid() const {\n  return 0x00 != getUInt8(Offset::index());\n}\n\nunsigned int\nDR1801UVCodeplug::MessageElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nDR1801UVCodeplug::MessageElement::setIndex(unsigned int index) {\n  setUInt8(Offset::index(), index+1);\n}\n\nQString\nDR1801UVCodeplug::MessageElement::text() const {\n  return readASCII(Offset::text(), Limit::textLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::MessageElement::setText(const QString &text) {\n  setUInt8(Offset::textLength(),\n           std::min(Limit::textLength(), (unsigned int)text.length()));\n  writeASCII(Offset::text(), text, Limit::textLength(), 0x00);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::KeySettingsElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::KeySettingsElement::KeySettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::KeySettingsElement::KeySettingsElement(uint8_t *ptr)\n  : Element(ptr, KeySettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::KeySettingsElement::clear() {\n  memset(_data, 0, _size);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::sideKey1Short() const {\n  return (Function) getUInt8(Offset::sideKey1Short());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setSideKey1Short(Function func) {\n  setUInt8(Offset::sideKey1Short(), (uint8_t) func);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::sideKey1Long() const {\n  return (Function) getUInt8(Offset::sideKey1Long());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setSideKey1Long(Function func) {\n  setUInt8(Offset::sideKey1Long(), (uint8_t) func);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::sideKey2Short() const {\n  return (Function) getUInt8(Offset::sideKey2Short());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setSideKey2Short(Function func) {\n  setUInt8(Offset::sideKey2Short(), (uint8_t) func);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::sideKey2Long() const {\n  return (Function) getUInt8(Offset::sideKey2Long());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setSideKey2Long(Function func) {\n  setUInt8(Offset::sideKey2Long(), (uint8_t) func);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::topKeyShort() const {\n  return (Function) getUInt8(Offset::topKeyShort());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setTopKeyShort(Function func) {\n  setUInt8(Offset::topKeyShort(), (uint8_t) func);\n}\n\nDR1801UVCodeplug::KeySettingsElement::Function\nDR1801UVCodeplug::KeySettingsElement::topKeyLong() const {\n  return (Function) getUInt8(Offset::topKeyLong());\n}\nvoid\nDR1801UVCodeplug::KeySettingsElement::setTopKeyLong(Function func) {\n  setUInt8(Offset::topKeyLong(), (uint8_t) func);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::VFOBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::VFOBankElement::VFOBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::VFOBankElement::VFOBankElement(uint8_t *ptr)\n  : Element(ptr, VFOBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::VFOBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nDR1801UVCodeplug::ChannelElement\nDR1801UVCodeplug::VFOBankElement::vfoA() const {\n  return ChannelElement(_data + Offset::vfoA());\n}\n\nDR1801UVCodeplug::ChannelElement\nDR1801UVCodeplug::VFOBankElement::vfoB() const {\n  return ChannelElement(_data + Offset::vfoB());\n}\n\nQString\nDR1801UVCodeplug::VFOBankElement::nameA() const {\n  return readASCII(Offset::nameA(), Limit::nameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::VFOBankElement::setNameA(const QString &name) {\n  writeASCII(Offset::nameA(), name, Limit::nameLength(), 0x00);\n}\n\nQString\nDR1801UVCodeplug::VFOBankElement::nameB() const {\n  return readASCII(Offset::nameB(), Limit::nameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::VFOBankElement::setNameB(const QString &name) {\n  writeASCII(Offset::nameB(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::EncryptionKeyBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr)\n  : Element(ptr, EncryptionKeyBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::EncryptionKeyBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nDR1801UVCodeplug::EncryptionKeyElement\nDR1801UVCodeplug::EncryptionKeyBankElement::key(unsigned int index) const {\n  return EncryptionKeyElement(_data + index*EncryptionKeyElement::size());\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyBankElement::decode(Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0; i<Limit::keyCount(); i++) {\n    EncryptionKey *obj = key(i).toKeyObj(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode encryption key at index \" << i << \".\";\n      return false;\n    }\n    // Add key to index and config\n    ctx.add(obj, key(i).index());\n    ctx.config()->commercialExtension()->encryptionKeys()->add(obj);\n  }\n  return true;\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyBankElement::link(Context &ctx, const ErrorStack &err) const\n{\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  // Nothing to do\n  return true;\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int n = std::min(Limit::keyCount(), ctx.count<BasicEncryptionKey>());\n  for (unsigned int i=0; i<Limit::keyCount(); i++) {\n    EncryptionKeyElement key = this->key(i);\n    if (i>=n) {\n      key.clear();\n      continue;\n    }\n    if (! key.encode(ctx.get<BasicEncryptionKey>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode DMR encryption key '\" << ctx.get<BasicEncryptionKey>(i)->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n    key.setIndex(i);\n  }\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::EncryptionKeyElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr)\n  : Element(ptr, EncryptionKeyElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::EncryptionKeyElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyElement::isValid() const {\n  return 0x00 != getUInt8(Offset::index());\n}\n\nunsigned int\nDR1801UVCodeplug::EncryptionKeyElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nDR1801UVCodeplug::EncryptionKeyElement::setIndex(unsigned int index) {\n  setUInt8(Offset::index(), index+1);\n}\n\nunsigned int\nDR1801UVCodeplug::EncryptionKeyElement::keyLength() const {\n  return getUInt16_le(Offset::length());\n}\n\nQString\nDR1801UVCodeplug::EncryptionKeyElement::key() const {\n  return readASCII(Offset::key(), Limit::keyLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::EncryptionKeyElement::setKey(const QString &key) {\n  setUInt8(Offset::length(), 8);\n  writeASCII(Offset::key(), key, Limit::keyLength(), 0x00);\n}\n\nEncryptionKey *\nDR1801UVCodeplug::EncryptionKeyElement::toKeyObj(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx);\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode an invalid encryption key.\";\n    return nullptr;\n  }\n\n  BasicEncryptionKey *obj = new BasicEncryptionKey();\n  if (! obj->fromHex(key(), err)) {\n    errMsg(err) << \"Cannot decode key '\" << key() << \"'.\";\n    delete obj;\n    return nullptr;\n  }\n\n  return obj;\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyElement::linkKeyObj(EncryptionKey *obj, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(obj); Q_UNUSED(ctx); Q_UNUSED(err);\n  // There is nothing to do.\n  return true;\n}\n\nbool\nDR1801UVCodeplug::EncryptionKeyElement::encode(EncryptionKey *obj, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx);\n\n  if (!obj->is<BasicEncryptionKey>()) {\n    errMsg(err) << \"Cannot encode AES encryption key. Not supported by the device.\";\n    return false;\n  }\n  BasicEncryptionKey *key = obj->as<BasicEncryptionKey>();\n  setKey(key->key().toHex());\n\n  return true;\n}\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DTMFSettingsElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DTMFSettingsElement::DTMFSettingsElement(uint8_t *ptr)\n  : Element(ptr, DR1801UVCodeplug::DTMFSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::clear() {\n  memset(_data, 0, _size);\n}\n\nQString\nDR1801UVCodeplug::DTMFSettingsElement::radioID() const {\n  return readASCII(Offset::radioID(), Limit::radioIDLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setRadioID(const QString &id) {\n  setUInt8(Offset::radioIDLength(),\n           std::min(Limit::radioIDLength(), (unsigned int)id.length()));\n  writeASCII(Offset::radioID(), id, Limit::radioIDLength(), 0x00);\n}\n\nQString\nDR1801UVCodeplug::DTMFSettingsElement::killCode() const {\n  return readASCII(Offset::killCode(), Limit::killCodeLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setKillCode(const QString &code) {\n  setUInt8(Offset::killCodeLength(),\n           std::min(Limit::killCodeLength(), (unsigned int)code.length()));\n  writeASCII(Offset::killCode(), code, Limit::killCodeLength(), 0x00);\n}\n\nQString\nDR1801UVCodeplug::DTMFSettingsElement::wakeCode() const {\n  return readASCII(Offset::wakeCode(), Limit::wakeCodeLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setWakeCode(const QString &code) {\n  setUInt8(Offset::wakeCodeLength(),\n           std::min(Limit::wakeCodeLength(), (unsigned int)code.length()));\n  writeASCII(Offset::wakeCode(), code, Limit::wakeCodeLength(), 0x00);\n}\n\nDR1801UVCodeplug::DTMFSettingsElement::NonNumber\nDR1801UVCodeplug::DTMFSettingsElement::delimiter() const {\n  return (NonNumber) getUInt8(Offset::delimiter());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setDelimiter(NonNumber code) {\n  setUInt8(Offset::delimiter(), (uint8_t)code);\n}\n\nDR1801UVCodeplug::DTMFSettingsElement::NonNumber\nDR1801UVCodeplug::DTMFSettingsElement::groupCode() const {\n  return (NonNumber) getUInt8(Offset::groupCode());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setGroupCode(NonNumber code) {\n  setUInt8(Offset::groupCode(), (uint8_t)code);\n}\n\nDR1801UVCodeplug::DTMFSettingsElement::Response\nDR1801UVCodeplug::DTMFSettingsElement::response() const {\n  return (Response) getUInt8(Offset::response());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setResponse(Response resp) {\n  setUInt8(Offset::response(), (uint8_t)resp);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSettingsElement::autoResetTime() const {\n  return getUInt8(Offset::autoResetTime());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setAutoResetTime(unsigned int sec) {\n  setUInt8(Offset::autoResetTime(), sec);\n}\n\nbool\nDR1801UVCodeplug::DTMFSettingsElement::killWakeEnabled() const {\n  return 0x01 == getUInt8(Offset::killWake());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::enableKillWake(bool enable) {\n  setUInt8(Offset::killWake(), enable ? 0x01 : 0x00);\n}\n\nDR1801UVCodeplug::DTMFSettingsElement::Kill\nDR1801UVCodeplug::DTMFSettingsElement::killType() const {\n  return (Kill) getUInt8(Offset::killType());\n}\nvoid\nDR1801UVCodeplug::DTMFSettingsElement::setKillType(Kill type) {\n  setUInt8(Offset::killType(), (uint8_t)type);\n}\n\nDR1801UVCodeplug::DTMFSystemBankElement\nDR1801UVCodeplug::DTMFSettingsElement::dtmfSystems() const {\n  return DTMFSystemBankElement(_data+Offset::dtmfSystems());\n}\n\nDR1801UVCodeplug::DTMFIDBankElement\nDR1801UVCodeplug::DTMFSettingsElement::dtmfIDs() const {\n  return DTMFIDBankElement(_data + Offset::dtmfIDs());\n}\n\nDR1801UVCodeplug::PTTIDBankElement\nDR1801UVCodeplug::DTMFSettingsElement::pttIDs() const {\n  return PTTIDBankElement(_data + Offset::pttIDs());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DTMFSystemBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::DTMFSystemBankElement::DTMFSystemBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DTMFSystemBankElement::DTMFSystemBankElement(uint8_t *ptr)\n  : Element(ptr, DTMFSystemBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DTMFSystemBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSystemBankElement::systemCount() const {\n  return getUInt8(Offset::systemCount());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemBankElement::setSystemCount(unsigned int count) {\n  setUInt8(Offset::systemCount(), count);\n}\n\nDR1801UVCodeplug::DTMFSystemElement\nDR1801UVCodeplug::DTMFSystemBankElement::system(unsigned int n) const {\n  return DTMFSystemElement(_data + Offset::systems() + n*DTMFSystemElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DTMFSystemElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::DTMFSystemElement::DTMFSystemElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DTMFSystemElement::DTMFSystemElement(uint8_t *ptr)\n  : Element(ptr, DTMFSystemElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DTMFSystemElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::DTMFSystemElement::sideToneEnabled() const {\n  return 0x01 == getUInt8(Offset::sideTone());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemElement::enableSideTone(bool enable) {\n  setUInt8(Offset::sideTone(), enable ? 0x01 : 0x00);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSystemElement::preTime() const {\n  return getUInt16_le(Offset::preTime());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemElement::setPreTime(unsigned int ms) {\n  setUInt16_le(Offset::preTime(), ms);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSystemElement::codeDuration() const {\n  return getUInt16_le(Offset::codeDuration());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemElement::setCodeDuration(unsigned int ms) {\n  setUInt16_le(Offset::codeDuration(), ms);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSystemElement::codeItervall() const {\n  return getUInt16_le(Offset::codeIntervall());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemElement::setCodeItervall(unsigned int ms) {\n  setUInt16_le(Offset::codeIntervall(), ms);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFSystemElement::resetTime() const {\n  return getUInt16_le(Offset::resetTime());\n}\nvoid\nDR1801UVCodeplug::DTMFSystemElement::setResetTime(unsigned int ms) {\n  setUInt16_le(Offset::resetTime(), ms);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DTMFIDBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::DTMFIDBankElement::DTMFIDBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DTMFIDBankElement::DTMFIDBankElement(uint8_t *ptr)\n  : Element(ptr, DTMFIDBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DTMFIDBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFIDBankElement::idCount() const {\n  return getUInt8(Offset::idCount());\n}\nvoid\nDR1801UVCodeplug::DTMFIDBankElement::setIDCount(unsigned int n) {\n  setUInt8(Offset::idCount(), n);\n}\n\nDR1801UVCodeplug::DTMFIDElement\nDR1801UVCodeplug::DTMFIDBankElement::id(unsigned int n) const {\n  return DTMFIDElement(_data + Offset::ids() + n*DTMFIDElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DTMFIDElement\n * ******************************************************************************************** */\nQVector<QChar> DR1801UVCodeplug::DTMFIDElement::_bin_dtmf_tab = {\n  '0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'\n};\n\nDR1801UVCodeplug::DTMFIDElement::DTMFIDElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DTMFIDElement::DTMFIDElement(uint8_t *ptr)\n  : Element(ptr, DR1801UVCodeplug::DTMFIDElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DTMFIDElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::DTMFIDElement::numberLength() const {\n  return getUInt8(Offset::numberLength());\n}\nvoid\nDR1801UVCodeplug::DTMFIDElement::setNumberLength(unsigned int len) {\n  setUInt8(Offset::numberLength(), len);\n}\n\nQString\nDR1801UVCodeplug::DTMFIDElement::number() const {\n  QString number; number.reserve(16);\n  for (unsigned int i=0; i<numberLength(); i++) {\n    number.append(_bin_dtmf_tab[getUInt8(Offset::number()+i)&0xf]);\n  }\n  return number;\n}\n\nvoid\nDR1801UVCodeplug::DTMFIDElement::setNumber(const QString &number) {\n  QString lnumber = number.toUpper();\n  QRegularExpression re(\"[0-9A-D*#]+\");\n  if (! re.match(lnumber).isValid())\n    return;\n  setNumberLength(std::min(Limit::numberLength(), (unsigned int)lnumber.length()));\n  for (unsigned int i=0; i<numberLength(); i++) {\n    setUInt8(Offset::number() + i, _bin_dtmf_tab.indexOf(lnumber.at(i)));\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::PTTIDBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::PTTIDBankElement::PTTIDBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::PTTIDBankElement::PTTIDBankElement(uint8_t *ptr)\n  : Element(ptr, PTTIDBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::PTTIDBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::PTTIDBankElement::idCount() const {\n  return getUInt8(Offset::idCount());\n}\nvoid\nDR1801UVCodeplug::PTTIDBankElement::setPTTIDCount(unsigned int n) {\n  setUInt8(Offset::idCount(), n);\n}\n\nDR1801UVCodeplug::PTTIDElement\nDR1801UVCodeplug::PTTIDBankElement::pttID(unsigned int n) {\n  return PTTIDElement(_data + Offset::ids() + n*PTTIDElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::PTTIDElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::PTTIDElement::PTTIDElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::PTTIDElement::PTTIDElement(uint8_t *ptr)\n  : Element(ptr, PTTIDElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::PTTIDElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::PTTIDElement::hasDTMFSystem() const {\n  return 0 != getUInt8(Offset::dtmfSystemIndex());\n}\nunsigned int\nDR1801UVCodeplug::PTTIDElement::dtmfSystemIndex() const {\n  return getUInt8(Offset::dtmfSystemIndex())-1;\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::setDTMFSystemIndex(unsigned int index) {\n  setUInt8(Offset::dtmfSystemIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::clearDTMFSystem() {\n  setUInt8(Offset::dtmfSystemIndex(), 0);\n}\n\nDR1801UVCodeplug::PTTIDElement::Transmit\nDR1801UVCodeplug::PTTIDElement::transmitMode() const {\n  return (Transmit) getUInt8(Offset::transmitMode());\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::setTransmitMode(Transmit mode) {\n  setUInt8(Offset::transmitMode(), (uint8_t)mode);\n}\n\nDR1801UVCodeplug::PTTIDElement::IDMode\nDR1801UVCodeplug::PTTIDElement::idMode() const {\n  return (IDMode) getUInt8(Offset::idMode());\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::setIDMode(IDMode mode) {\n  setUInt8(Offset::idMode(), (uint8_t)mode);\n}\n\nbool\nDR1801UVCodeplug::PTTIDElement::hasBOTID() const {\n  return 0 != getUInt8(Offset::botIDIndex());\n}\nunsigned int\nDR1801UVCodeplug::PTTIDElement::botIDIndex() const {\n  return getUInt8(Offset::botIDIndex())-1;\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::setBOTIDIndex(unsigned int index) {\n  setUInt8(Offset::botIDIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::clearBOTID() {\n  setUInt8(Offset::botIDIndex(), 0);\n}\n\nbool\nDR1801UVCodeplug::PTTIDElement::hasEOTID() const {\n  return 0 != getUInt8(Offset::eotIDIndex());\n}\nunsigned int\nDR1801UVCodeplug::PTTIDElement::eotIDIndex() const {\n  return getUInt8(Offset::eotIDIndex())-1;\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::setEOTIDIndex(unsigned int index) {\n  setUInt8(Offset::eotIDIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::PTTIDElement::clearEOTID() {\n  setUInt8(Offset::eotIDIndex(), 0);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::AlarmSystemBankElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::AlarmSystemBankElement::AlarmSystemBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::AlarmSystemBankElement::AlarmSystemBankElement(uint8_t *ptr)\n  : Element(ptr, AlarmSystemBankElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::AlarmSystemBankElement::clear() {\n  memset(_data, 0, _size);\n}\n\nunsigned int\nDR1801UVCodeplug::AlarmSystemBankElement::alarmSystemCount() const {\n  return getUInt8(Offset::alarmSystemCount());\n}\nvoid\nDR1801UVCodeplug::AlarmSystemBankElement::setAlarmSystemCount(unsigned int n) {\n  setUInt8(Offset::alarmSystemCount(), n);\n}\n\nDR1801UVCodeplug::AlarmSystemElement\nDR1801UVCodeplug::AlarmSystemBankElement::alarmSystem(unsigned int n) const {\n  return AlarmSystemElement(_data + Offset::alarmSystems() + n*AlarmSystemElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::AlarmSystemElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::AlarmSystemElement::AlarmSystemElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::AlarmSystemElement::AlarmSystemElement(uint8_t *ptr)\n  : Element(ptr, AlarmSystemElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::AlarmSystemElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::AlarmSystemElement::isValid() const {\n  return 0 != getUInt8(Offset::index());\n}\n\nunsigned int\nDR1801UVCodeplug::AlarmSystemElement::index() const {\n  return getUInt8(Offset::index())-1;\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::setIndex(unsigned int index) {\n  setUInt8(Offset::index(), index+1);\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::clearIndex() {\n  setUInt8(Offset::index(), 0);\n}\n\nbool\nDR1801UVCodeplug::AlarmSystemElement::alarmEnabled() const {\n  return 0x01 == getUInt8(Offset::alarmEnabled());\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::enableAlarm(bool enable) {\n  setUInt8(Offset::alarmEnabled(), enable ? 0x01 : 0x00);\n}\n\nbool\nDR1801UVCodeplug::AlarmSystemElement::noAlarmChannel() const {\n  return 0 == getUInt16_le(Offset::alarmChannelIndex());\n}\nbool\nDR1801UVCodeplug::AlarmSystemElement::alarmChannelIsSelected() const {\n  return 0xffff == getUInt16_le(Offset::alarmChannelIndex());\n}\nunsigned int\nDR1801UVCodeplug::AlarmSystemElement::alarmChannelIndex() const {\n  return getUInt16_le(Offset::alarmChannelIndex())-1;\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::setAlarmChannelIndex(unsigned int index) {\n  setUInt16_le(Offset::alarmChannelIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::setAlarmChannelSelected() {\n  setUInt16_le(Offset::alarmChannelIndex(), 0xffff);\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::clearAlarmChannel() {\n  setUInt16_le(Offset::alarmChannelIndex(), 0);\n}\n\nQString\nDR1801UVCodeplug::AlarmSystemElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0x00);\n}\nvoid\nDR1801UVCodeplug::AlarmSystemElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::DMRSettingsElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr)\n  : Element(ptr, DMRSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::DMRSettingsElement::clear() {\n  memset(_data, 0, _size);\n}\n\n\nInterval\nDR1801UVCodeplug::DMRSettingsElement::callHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::holdTime()));\n}\n\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setCallHangTime(const Interval &dur) {\n  auto t = Limit::holdTime().limit(dur);\n  setUInt8(Offset::holdTime(), t.seconds());\n}\n\n\nunsigned int\nDR1801UVCodeplug::DMRSettingsElement::remoteListen() const {\n  return getUInt8(Offset::remoteListen());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setRemoteListen(unsigned int sec) {\n  sec = Limit::remoteListen().limit(sec);\n  setUInt8(Offset::remoteListen(), sec);\n}\n\nunsigned int\nDR1801UVCodeplug::DMRSettingsElement::activeWait() const {\n  return getUInt8(Offset::activeWait());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setActiveWait(unsigned int ms) {\n  ms = Limit::activeWait().limit(ms);\n  setUInt8(Offset::activeWait(), ms);\n}\n\nunsigned int\nDR1801UVCodeplug::DMRSettingsElement::activeResend() const {\n  return getUInt8(Offset::activeResend());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setActiveResend(unsigned int count) {\n  count = Limit::activeResend().limit(count);\n  setUInt8(Offset::activeResend(), count);\n}\n\nunsigned int\nDR1801UVCodeplug::DMRSettingsElement::preSend() const {\n  return getUInt8(Offset::preSend());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setPreSend(unsigned int count) {\n  count = Limit::preSend().limit(count);\n  setUInt8(Offset::preSend(), count);\n}\n\nunsigned int\nDR1801UVCodeplug::DMRSettingsElement::voiceHead() const {\n  return getUInt8(Offset::voiceHead());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setVoiceHead(unsigned int count) {\n  count = Limit::voiceHead().limit(count);\n  setUInt8(Offset::voiceHead(), count);\n}\n\nDR1801UVCodeplug::DMRSettingsElement::SMSFormat\nDR1801UVCodeplug::DMRSettingsElement::smsFormat() const {\n  return (SMSFormat) getUInt8(Offset::smsFormat());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::setSMSFormat(SMSFormat format) {\n  setUInt8(Offset::smsFormat(), (uint8_t) format);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::killEnc() const {\n  return 0x01 == getUInt8(Offset::killEnc());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableKillEnc(bool enable) {\n  setUInt8(Offset::killEnc(), enable ? 0x01 : 0x00);\n}\nbool\nDR1801UVCodeplug::DMRSettingsElement::killDec() const {\n  return 0x01 == getUInt8(Offset::killDec());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableKillDec(bool enable) {\n  setUInt8(Offset::killDec(), enable ? 0x01 : 0x00);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::activeEnc() const {\n  return 0x01 == getUInt8(Offset::activeEnc());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableActiveEnc(bool enable) {\n  setUInt8(Offset::activeEnc(), enable ? 0x01 : 0x00);\n}\nbool\nDR1801UVCodeplug::DMRSettingsElement::activeDec() const {\n  return 0x01 == getUInt8(Offset::activeDec());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableActiveDec(bool enable) {\n  setUInt8(Offset::activeDec(), enable ? 0x01 : 0x00);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::checkEnc() const {\n  return 0x01 == getUInt8(Offset::checkEnc());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableCheckEnc(bool enable) {\n  setUInt8(Offset::checkEnc(), enable ? 0x01 : 0x00);\n}\nbool\nDR1801UVCodeplug::DMRSettingsElement::checkDec() const {\n  return 0x01 == getUInt8(Offset::checkDec());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableCheckDec(bool enable) {\n  setUInt8(Offset::checkDec(), enable ? 0x01 : 0x00);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::callAlterEnc() const {\n  return getBit(Offset::callAlterEnc());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableCallAlterEnc(bool enable) {\n  setBit(Offset::callAlterEnc(), enable);\n}\nbool\nDR1801UVCodeplug::DMRSettingsElement::callAlterDec() const {\n  return getBit(Offset::callAlterDec());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableCallAlterDec(bool enable) {\n  setBit(Offset::callAlterDec(), enable);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::remoteMonitorEnc() const {\n  return getBit(Offset::remoteMonitorEnc());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableRemoteMonitorEnc(bool enable) {\n  setBit(Offset::remoteMonitorEnc(), enable);\n}\nbool\nDR1801UVCodeplug::DMRSettingsElement::remoteMonitorDec() const {\n  return getBit(Offset::remoteMonitorDec());\n}\nvoid\nDR1801UVCodeplug::DMRSettingsElement::enableRemoteMonitorDec(bool enable) {\n  setBit(Offset::remoteMonitorDec(), enable);\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err);\n\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(callHangTime());\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(callHangTime());\n\n  switch(smsFormat()) {\n  case SMSFormat::IPData: ctx.config()->smsExtension()->setFormat(SMSExtension::Format::Motorola); break;\n  case SMSFormat::CompressedIP: ctx.config()->smsExtension()->setFormat(SMSExtension::Format::Hytera); break;\n  case SMSFormat::DefinedData: ctx.config()->smsExtension()->setFormat(SMSExtension::Format::DMR); break;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::DMRSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n\n  switch(ctx.config()->smsExtension()->format()) {\n  case SMSExtension::Format::Motorola: setSMSFormat(SMSFormat::IPData); break;\n  case SMSExtension::Format::Hytera: setSMSFormat(SMSFormat::CompressedIP); break;\n  case SMSExtension::Format::DMR: setSMSFormat(SMSFormat::DefinedData); break;\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::OneTouchSettingsElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::OneTouchSettingsElement::OneTouchSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::OneTouchSettingsElement::OneTouchSettingsElement(uint8_t *ptr)\n  : Element(ptr, OneTouchSettingsElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::OneTouchSettingsElement::clear() {\n  memset(_data, 0, _size);\n}\nunsigned int\nDR1801UVCodeplug::OneTouchSettingsElement::settingsCount() const {\n  return Limit::settingsCount();\n}\n\nDR1801UVCodeplug::OneTouchSettingElement\nDR1801UVCodeplug::OneTouchSettingsElement::setting(unsigned int n) const {\n  return OneTouchSettingElement(_data + Offset::settings() + n*OneTouchSettingElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug::OneTouchSettingElement\n * ******************************************************************************************** */\nDR1801UVCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nDR1801UVCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr)\n  : Element(ptr, OneTouchSettingElement::size())\n{\n  // pass...\n}\n\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nDR1801UVCodeplug::OneTouchSettingElement::isValid() const {\n  return Element::isValid() && (Type::Disabled != type());\n}\n\nbool\nDR1801UVCodeplug::OneTouchSettingElement::hasContact() const {\n  return 0 != getUInt16_le(Offset::contactIndex());\n}\nunsigned int\nDR1801UVCodeplug::OneTouchSettingElement::contactIndex() const {\n  return getUInt16_le(Offset::contactIndex())-1;\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::setContactIndex(unsigned int index) {\n  setUInt16_le(Offset::contactIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::clearContact() {\n  setUInt16_le(Offset::contactIndex(), 0);\n}\n\nDR1801UVCodeplug::OneTouchSettingElement::Action\nDR1801UVCodeplug::OneTouchSettingElement::action() const {\n  return (Action) getUInt8(Offset::action());\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::setAction(Action action) {\n  setUInt8(Offset::action(), (uint8_t) action);\n}\n\nbool\nDR1801UVCodeplug::OneTouchSettingElement::hasMessage() const {\n  return 0 != getUInt8(Offset::messageIndex());\n}\nunsigned int\nDR1801UVCodeplug::OneTouchSettingElement::messageIndex() const {\n  return getUInt8(Offset::messageIndex())-1;\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::setMessageIndex(unsigned int index) {\n  setUInt8(Offset::messageIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::clearMessage() {\n  setUInt8(Offset::messageIndex(), 0);\n}\n\nDR1801UVCodeplug::OneTouchSettingElement::Type\nDR1801UVCodeplug::OneTouchSettingElement::type() const {\n  return (Type) getUInt8(Offset::type());\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::setType(Type type) {\n  setUInt8(Offset::type(), (uint8_t)type);\n}\n\nbool\nDR1801UVCodeplug::OneTouchSettingElement::hasDTMFID() const {\n  return 0 != getUInt8(Offset::dtmfIDIndex());\n}\nunsigned int\nDR1801UVCodeplug::OneTouchSettingElement::dtmfIDIndex() const {\n  return getUInt8(Offset::dtmfIDIndex())-1;\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::setDTMFIDIndex(unsigned int index) {\n  setUInt8(Offset::dtmfIDIndex(), index+1);\n}\nvoid\nDR1801UVCodeplug::OneTouchSettingElement::clearDTMFIDIndex() {\n  setUInt8(Offset::dtmfIDIndex(), 0);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of DR1801UVCodeplug\n * ******************************************************************************************** */\nDR1801UVCodeplug::DR1801UVCodeplug(QObject *parent)\n  : Codeplug(parent)\n{\n  addImage(\"BTECH DR-1801UV Codeplug\");\n  image(0).addElement(0, Offset::size());\n}\n\nConfig *\nDR1801UVCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *copy = Codeplug::preprocess(config, err);\n  if (nullptr == copy) {\n    errMsg(err) << \"Cannot pre-process DR1801A6 codeplug.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(copy, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete copy;\n    return nullptr;\n  }\n\n  // Split dual-zones into two.\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(copy, err)) {\n    errMsg(err) << \"Cannot pre-process DR1801A6 codeplug.\";\n    delete copy;\n    return nullptr;\n  }\n\n  return copy;\n}\n\nbool\nDR1801UVCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process DR1801A6 codeplug.\";\n    return false;\n  }\n\n  // Merge split zones into one.\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot post-process DR1801A6 codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // All indices as 0-based. That is, the first channel gets index 0 etc.\n\n  // There must be a default DMR radio ID.\n  if (nullptr == ctx.config()->settings()->defaultId()) {\n    errMsg(err) << \"No default DMR radio ID specified.\";\n    errMsg(err) << \"Cannot index codeplug for encoding for the BTECH DR-1801UV.\";\n    return false;\n  }\n\n  // Map radio IDs\n  for (int i=0; i<ctx.config()->radioIDs()->count(); i++) {\n    if (ctx.config()->radioIDs()->get(i)->is<DMRRadioID>())\n      ctx.add(ctx.config()->radioIDs()->get(i)->as<DMRRadioID>(), i);\n  }\n\n  // Map digital and DTMF contacts\n  for (int i=0, d=0; i<config->contacts()->count(); i++) {\n    if (config->contacts()->contact(i)->is<DMRContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DMRContact>(), d); d++;\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i);\n\n  // Map channels\n  for (int i=0; i<config->channelList()->count(); i++)\n    ctx.add(config->channelList()->channel(i), i);\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i);\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  Context ctx(config);\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug.\";\n    return false;\n  }\n\n  if (! encodeElements(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::decode(Config *config, const ErrorStack &err) {\n  Context ctx(config);\n\n  if (! decodeElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode elements.\";\n    return false;\n  }\n\n  if (! linkElements(ctx, err)) {\n    errMsg(err) << \"Cannot decode elements.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! ChannelBankElement(data(Offset::channelBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode channel elements.\";\n    return false;\n  }\n\n  if (! ContactBankElement(data(Offset::contactBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode contact elements.\";\n    return false;\n  }\n\n  if (! GroupListBankElement(data(Offset::groupListBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode group list elements.\";\n    return false;\n  }\n\n  if (! ZoneBankElement(data(Offset::zoneBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode zone elements.\";\n    return false;\n  }\n\n  if (! MessageBankElement(data(Offset::messageBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode preset messages.\";\n    return false;\n  }\n\n  if (! SettingsElement(data(Offset::settings())).decode(ctx.config(), err)) {\n    errMsg(err) << \"Cannot decode settings element.\";\n    return false;\n  }\n\n  if (! DMRSettingsElement(data(Offset::dmrSettings())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode DMR settings element.\";\n    return false;\n  }\n\n  if (! ScanListBankElement(data(Offset::scanListBank())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode scan list elements.\";\n    return false;\n  }\n\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeyBank())).decode(ctx, err)) {\n    logWarn() << \"Cannot decode encryption keys:\\n\" << err.format(\" \");\n    //errMsg(err) << \"Cannot decode encryption keys.\";\n    //return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::linkElements(Context &ctx, const ErrorStack &err) {\n  if (! ChannelBankElement(data(Offset::channelBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  if (! ContactBankElement(data(Offset::contactBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link contacts.\";\n    return false;\n  }\n\n  if (! GroupListBankElement(data(Offset::groupListBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n\n  if (! ZoneBankElement(data(Offset::zoneBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  if (! ScanListBankElement(data(Offset::scanListBank())).link(ctx, err)) {\n    errMsg(err) << \"Cannot link scan lists.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVCodeplug::encodeElements(Context &ctx, const ErrorStack &err) {\n  if (! SettingsElement(data(Offset::settings())).encode(ctx.config(), err)) {\n    errMsg(err) << \"Cannot encode settings element.\";\n    return false;\n  }\n\n  if (! DMRSettingsElement(data(Offset::dmrSettings())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode DMR settings element.\";\n    return false;\n  }\n\n  if (! ZoneBankElement(data(Offset::zoneBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  if (! MessageBankElement(data(Offset::messageBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode messages.\";\n    return false;\n  }\n\n  if (! ContactBankElement(data(Offset::contactBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  if (! ScanListBankElement(data(Offset::scanListBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode scan lists.\";\n    return false;\n  }\n\n  if (! ChannelBankElement(data(Offset::channelBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode channels.\";\n    return false;\n  }\n\n  if (! GroupListBankElement(data(Offset::groupListBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeyBank())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode encryption keys.\";\n    return false;\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/dr1801uv_codeplug.hh",
    "content": "#ifndef DR1801UVCODEPLUG_HH\n#define DR1801UVCODEPLUG_HH\n\n#include \"channel.hh\"\n#include \"codeplug.hh\"\n#include \"contact.hh\"\n#include \"bootsettings.hh\"\n\nclass Zone;\n\n\n/** Implements the binary codeplug representation of a BTECH DR-1801UV (a.k.a, BF-1801-A6).\n *\n * This device is kind of a re-release of the BTECH DM-1801. However, the hardware and firmware\n * is completely different. It is just the chassis that remained the same. This version uses the\n * Auctus A6 radio-on-a-chip as its main component.\n *\n * @section dr1801ver Matching firmware versions\n * This class implements the codeplug for the firmware version @c 1.10. The codeplug format usually\n * does not change much with firmware revisions, in particular not with older radios.\n *\n * @section dr1801cpl Codeplug structure within radio\n * The memory representation of the codeplug within the radio is simply a single large block of\n * data, read and written entirely. The total size of the codeplug is 01dd90h bytes.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><td>0x00000</td> <td>0x00300</td>  <td>0x00300</td>  <td>Some unknown settings, not\n *      configurable through the CPS. Likely some sort of calibration data. Must be conserved.</td></tr>\n *  <tr><td>0x00300</td> <td>0x003bc</td>  <td>0x000bc</td>  <td>Some information about the radio.\n *      Like serial number, firmware version, etc and timestamp. </td></tr>\n *  <tr><td>0x003b4</td> <td>0x00418</td>  <td>0x00064</td>  <td>General settings, see\n *      @c DR1801UVCodeplug::SettingsElement. </td></tr>\n *  <tr><td>0x00418</td> <td>0x04110</td>  <td>0x00000</td>  <td>Zone bank, see\n *      @c DR1801UVCodeplug::ZoneBankElement. </td></tr>\n *  <tr><td>0x04110</td> <td>0x04274</td>  <td>0x00164</td>  <td>Message bank, see\n *      @c DR1801UVCodeplug::MessageBankElement. </td></tr>\n *  <tr><td>0x04334</td> <td>0x0a338</td>  <td>0x06008</td>  <td>Contact bank, see\n *      @c DR1801UVCodeplug::ContactBankElement. </td></tr>\n *  <tr><td>0x0a338</td> <td>0x0a65c</td>  <td>0x00324</td>  <td>Scan-list bank, see\n *      @c DR1801UVCodeplug::ScanListBankElement. </td></tr>\n *  <tr><td>0x0a65c</td> <td>0x1c660</td>  <td>0x12004</td>  <td>Channel bank, see\n *      @c DR1801UVCodeplug::ChannelBankElement. </td></tr>\n *  <tr><td>0x1c6c4</td> <td>0x1c6dc</td>  <td>0x0018</td>  <td>Key settings, see\n *      @c DR1801UVCodeplug::KeySettingsElement. </td></tr>\n *  <tr><td>0x1c6dc</td> <td>0x1d7e0</td>  <td>0x01104</td>  <td>Group list bank, see\n *      @c DR1801UVCodeplug::GroupListBankElement. </td></tr>\n *  <tr><td>0x1d7e0</td> <td>0x1d858</td>  <td>0x00078</td>  <td>Encryption keys, see\n *      @c DR1801UVCodeplug::EncryptionKeyBankElement. </td></tr>\n *  <tr><td>0x1d858</td> <td>0x1daf4</td>  <td>0x0029c</td>  <td>DTMF signaling settings, see\n *      @c DR1801UVCodeplug::DTMFSettingsElement. </td></tr>\n *  <tr><td>0x1daf4</td> <td>0x1dbb8</td>  <td>0x00c4</td>   <td>Alarm settings, see\n *      @c DR1801UVCodeplug::AlarmSettingsBankElement. </tr>\n *  <tr><td>0x1dbb8</td> <td>0x1dbc8</td>  <td>0x0010</td>  <td>DMR settings, see\n *      @c DR1801UVCodeplug::DMRSettingsElement. </td></tr>\n *  <tr><td>0x1dbc8</td> <td>0x1dbf0</td>  <td>0x00028</td>  <td>One-touch settings, see\n *      @c DR1801UVCodeplug::OneTouchSettingsElement. </td></tr>\n *  <tr><td>0x1dbf0</td> <td>0x1dd00</td>  <td>0x00110</td>  <td>Some unknown settings.</td></tr>\n *  <tr><td>0x1dd00</td> <td>0x1dd90</td>  <td>0x00090</td>  <td>VFO channels, see\n *      @c DR1801UVCodeplug::VFOBankElement. </td></tr>\n * </table>\n *\n * @ingroup dr1801uv */\nclass DR1801UVCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements the binary encoding of the channels settings.\n   *\n   * Memory representation of the channel settings (0034h bytes):\n   * @verbinclude dr1801uv_channelelement.txt */\n  class ChannelElement: public Element\n  {\n  public:\n    /** Possible channel types. */\n    enum class Type {\n      FM = 1, DMR = 3\n    };\n    /** Possible power settings. */\n    enum class Power {\n      Low = 0, High = 1\n    };\n    /** Possible values for the admid criterion. */\n    enum class Admit {\n      Always = 0, ColorCode_or_Tone = 1, ChannelFree = 2\n    };\n    /** Possible time-slot values. */\n    enum class TimeSlot {\n      TS1 = 1, TS2 = 2\n    };\n    /** Possible FM signaling modes. */\n    enum class SignalingMode {\n      None = 0, DTMF = 1\n    };\n    /** Possible band width settings. */\n    enum class Bandwidth {\n      Narrow = 1, Wide = 2\n    };\n    /** Possible subtone types. */\n    enum class SubToneType {\n      None = 0, CTCSS = 1, DCS = 2\n    };\n    /** Possible DCS modes. */\n    enum class DCSMode {\n      Normal = 0, Inverted = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor from pointer. */\n    ChannelElement(uint8_t *ptr);\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00034; }\n\n    /** Returns the 0-based index of the channel. */\n    virtual unsigned int index() const;\n    /** Sets the index. */\n    virtual void setIndex(unsigned int idx);\n\n    /** Returns the channel type. */\n    virtual Type channelType() const;\n    /** Sets the channel type. */\n    virtual void setChannelType(Type type);\n\n    /** Returns the power settings of the channel. */\n    virtual Channel::Power power() const;\n    /** Sets the power for the channel. */\n    virtual void setPower(Channel::Power pwr);\n\n    /** Returns the RX frequency in MHz. */\n    virtual Frequency rxFrequency() const;\n    /** Sets the RX frequency in MHz. */\n    virtual void setRXFrequency(Frequency MHz);\n    /** Returns the TX frequency in MHz. */\n    virtual Frequency txFrequency() const;\n    /** Sets the TX frequency in MHz. */\n    virtual void setTXFrequency(Frequency MHz);\n\n    /** Returns @c true if a contact index is set. */\n    virtual bool hasTransmitContact() const;\n    /** Returns the contact index. */\n    virtual unsigned int transmitContactIndex() const;\n    /** Sets the transmit contact index. */\n    virtual void setTransmitContactIndex(unsigned int index);\n    /** Clears the contact index. */\n    virtual void clearTransmitContactIndex();\n\n    /** Returns the admit criterion. */\n    virtual Admit admitCriterion() const;\n    /** Sets the admit criterion. */\n    virtual void setAdmitCriterion(Admit admit);\n\n    /** Returns the color code (0-15). */\n    virtual unsigned int colorCode() const;\n    /** Sets the color code (0-15). */\n    virtual void setColorCode(unsigned int cc);\n    /** Returns the time slot. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns @c true if an ecryption key index is set. */\n    virtual bool hasEncryptionKey() const;\n    /** Returns the encryption key index. */\n    virtual unsigned int encryptionKeyIndex() const;\n    /** Sets the encryption key index. */\n    virtual void setEncryptionKeyIndex(unsigned int index);\n    /** Clears the encryption key index. */\n    virtual void clearEncryptionKeyIndex();\n\n    /** Returns @c true if dual-capacity direct mode is enabled. */\n    virtual bool dcdm() const;\n    /** Enables/disables dual-capacity direct mode. */\n    virtual void enableDCDM(bool enable);\n    /** Returns @c true if private-call confirmation is enabled. */\n    virtual bool confirmPrivateCall() const;\n    /** Enables/disables private-call confirmation. */\n    virtual void enablePrivateCallConfirmation(bool enable);\n\n    /** Returns the FM signaling mode. */\n    virtual SignalingMode signalingMode() const;\n    /** Sets the FM signaling mode. */\n    virtual void setSignalingMode(SignalingMode mode);\n\n    /** Returns @c true if the alarm system index is set. */\n    virtual bool hasAlarmSystem() const;\n    /** Returns the index of the alarm system. */\n    virtual unsigned int alarmSystemIndex() const;\n    /** Sets the alarm system index. */\n    virtual void setAlarmSystemIndex(unsigned int index);\n    /** Clears the alarm system index. */\n    virtual void clearAlarmSystemIndex();\n\n    /** Returns the band width for FM channels. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the band width for FM channels. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns @c true, if the auto-scan is enabled. */\n    virtual bool autoScanEnabled() const;\n    /** Enables/disables auto-scan. */\n    virtual void enableAutoScan(bool enable);\n    /** Returns @c true if a scan list is assigned. */\n    virtual bool hasScanList() const;\n    /** Returns the scan list index. */\n    virtual unsigned int scanListIndex() const;\n    /** Sets the scan list index. */\n    virtual void setScanListIndex(unsigned int index);\n    /** Clears the scan list index. */\n    virtual void clearScanListIndex();\n\n    /** Returns the RX CTCSS/DCS signaling. */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the RX CTCSS/DCS signaling. */\n    virtual void setRXTone(const SelectiveCall &code);\n    /** Returns the TX CTCSS/DCS signaling. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the TX CTCSS/DCS signaling. */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns @c true if talkaround is enabled. */\n    virtual bool talkaround() const;\n    /** Enables/disables talkaround. */\n    virtual void enableTalkaround(bool enable);\n\n    /** Returns @c true if a PTT ID is set. */\n    virtual bool hasPTTID() const;\n    /** Returns the PTT-ID index. */\n    virtual unsigned int pttIDIndex() const;\n    /** Sets the PTT-ID index. */\n    virtual void setPTTIDIndex(unsigned int idx);\n    /** Clears the PTT ID. */\n    virtual void clearPTTID();\n\n    /** Returns @c true if a group list is assigned. */\n    virtual bool hasGroupList() const;\n    /** Returns the group-list index. */\n    virtual unsigned int groupListIndex() const;\n    /** Sets the group-list index. */\n    virtual void setGroupListIndex(unsigned int index);\n    /** Clears the group list index. */\n    virtual void clearGroupListIndex();\n\n    /** Returns @c true if lone-worker is enabled. */\n    virtual bool loneWorker() const;\n    /** Enables/disables lone-worker. */\n    virtual void enableLoneWorker(bool enable);\n\n    /** Constructs a channel object. */\n    virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the channel object. */\n    virtual bool linkChannelObj(Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the given channel. */\n    virtual bool encode(Channel *channel, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some offsets within the codeplug. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int channelType() { return 0x0002; }\n      static constexpr unsigned int power() { return 0x0003; }\n      static constexpr unsigned int rxFrequency() { return 0x0004; }\n      static constexpr unsigned int txFrequency() { return 0x0008; }\n      static constexpr unsigned int transmitContactIndex() { return 0x000c; }\n      static constexpr unsigned int admitCriterion() { return 0x000e; }\n      static constexpr unsigned int colorCode() { return 0x0010; }\n      static constexpr unsigned int timeSlot() { return 0x0011; }\n      static constexpr unsigned int encryptionKeyIndex() { return 0x0014; }\n      static constexpr Bit dcdm() { return {0x0015, 1} ; }\n      static constexpr Bit confirmPivateCall() { return {0x0015, 0}; }\n      static constexpr unsigned int signalingMode() { return 0x0016; }\n      static constexpr unsigned int alarmSystemIndex() { return 0x0018; }\n      static constexpr unsigned int bandwidth() { return 0x0019; }\n      static constexpr unsigned int autoScan() { return 0x001a; }\n      static constexpr unsigned int scanListIndex() { return 0x001b; }\n      static constexpr unsigned int rxSubtoneCode() { return 0x001c; }\n      static constexpr unsigned int rxSubtoneType() { return 0x001e; }\n      static constexpr unsigned int rxDCSMode() { return 0x001f; }\n      static constexpr unsigned int txSubtoneCode() { return 0x0020; }\n      static constexpr unsigned int txSubtoneType() { return 0x0022; }\n      static constexpr unsigned int txDCSMode() { return 0x0023; }\n      static constexpr unsigned int talkaround() { return 0x0025; }\n      static constexpr unsigned int pttIDIndex() { return 0x0028; }\n      static constexpr unsigned int groupListIndex() { return 0x002a; }\n      static constexpr unsigned int loneWorker() { return 0x002f; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the channel bank.\n   *\n   * Holds up to 1024 @c DR1801UVCodeplug::ChannelElement.\n   *\n   * Memory representation of the channel bank (12004h bytes):\n   * @verbinclude dr1801uv_channelbankelement.txt */\n  class ChannelBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ChannelBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x12004; }\n\n    /** Returns the number of channels. */\n    virtual unsigned int channelCount() const;\n    /** Sets the number of channels. */\n    virtual void setChannelCount(unsigned int count);\n\n    /** Returns a reference to the channel element that the given index. */\n    virtual ChannelElement channel(unsigned int index) const;\n\n    /** Returns the name of the channel at the given index. */\n    virtual QString channelName(unsigned int index) const;\n    /** Sets the name of the channel at the given index. */\n    virtual void setChannelName(unsigned int index, const QString &name);\n\n    /** Decodes all defined channels. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links channels. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all channels. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Limits of some elements. */\n    struct Limit {\n      /** Returns the maximum number of channels. */\n      static constexpr unsigned int channelCount()       { return 1024; }\n      /** Returns the maximum length of a channel name. */\n      static constexpr unsigned int channelNameLength()  { return 0x00014; }\n    };\n\n  protected:\n    /** Offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int channelCount() { return 0x00000; }\n      static constexpr unsigned int channel()      { return 0x00004; }\n      static constexpr unsigned int channelName()  { return 0x0d004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of a contact.\n   *\n   * Memory representation of contact (0018h bytes):\n   * @verbinclude dr1801uv_contactelement.txt */\n  class ContactElement: public Element\n  {\n  public:\n    /** Possible call types. */\n    enum class CallType {\n      AllCall = 0x20, PrivateCall = 0x40, GroupCall = 0x80\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00018; }\n\n    /** Returns @c true if the contact has a successor. */\n    virtual bool hasSuccessor() const;\n    /** Returns the index of the next element. */\n    virtual uint16_t successorIndex() const;\n    /** Sets successor index. */\n    virtual void setSuccessorIndex(uint16_t index);\n    /** Clears the successor index. */\n    virtual void clearSuccessorIndex();\n\n    /** Returns the number. */\n    virtual uint32_t dmrID() const;\n    /** Sets the number. */\n    virtual void setDMRID(uint32_t id);\n\n    /** Returns the call type. */\n    virtual DMRContact::Type type() const;\n    /** Sets the call type. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Constructs a DMR contact object from this contact elmeent. */\n    virtual DMRContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the DMR contact object. */\n    virtual bool linkContactObj(DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the contact. */\n    virtual bool encode(DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum length of the contact name. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Defines offsets within the element. */\n    struct Offset : public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int successorIndex() { return 0x0000; }\n      static constexpr unsigned int nameLength() { return 0x0002; }\n      static constexpr unsigned int dmrID() { return 0x0004; }\n      static constexpr unsigned int callType() { return 0x0007; }\n      static constexpr unsigned int name() { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the contact bank.\n   *\n   * The bank holds the list of all contacts defined. See @c DR1801UVCodeplug::ContactElement for\n   * details.\n   *\n   * Memory representation of the contact bank (6008h bytes):\n   * @verbinclude dr1801uv_contactbankelement.txt */\n  class ContactBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x06004; }\n\n    /** Returns the number of contacts. */\n    virtual unsigned int contactCount() const;\n    /** Sets the number of contacts. */\n    virtual void setContactCount(unsigned int count);\n\n    /** Returns the index of the first contact. */\n    virtual unsigned int firstIndex() const;\n    /** Sets the index of the first element. */\n    virtual void setFirstIndex(unsigned int index);\n\n    /** Returns a reference to the n-th contact. */\n    virtual ContactElement contact(unsigned int index) const;\n\n    /** Decodes all contacts and stores them into the given context and config. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links all contacts. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all contacts. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some Limits.*/\n    struct Limit {\n      /** The maximum number of contacts. */\n      static constexpr unsigned int contactCount() { return 1024; }\n    };\n\n  protected:\n    /** Offsets within the element. */\n    struct Offset : public Element::Offset{\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contactCount() { return 0x0000; }\n      static constexpr unsigned int firstIndex() { return 0x0002; }\n      static constexpr unsigned int contacts() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of a group list.\n   *\n   * Memory representation of group list (44h bytes):\n   * @verbinclude dr1801uv_grouplistelement.txt */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    bool isValid() const;\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00044; }\n\n    /** Returns the index of the group list. */\n    virtual unsigned int index() const;\n    /** Sets the index of the group list. */\n    virtual void setIndex(unsigned int index);\n\n    /** Returns the number of elements in the list. */\n    virtual unsigned int count() const;\n    /** Sets the number of elements in the list. */\n    virtual void setCount(unsigned int n);\n\n    /** Returns @c true if the n-th member index is set. */\n    virtual bool hasMemberIndex(unsigned int n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned int memberIndex(unsigned int n) const;\n    /** Sets the n-th member index. */\n    virtual void setMemberIndex(unsigned int n, unsigned int index);\n    /** Clears the n-th member index. */\n    virtual void clearMemberIndex(unsigned int n);\n\n    /** Constructs a group list object from this elmeent. */\n    virtual RXGroupList *toGroupListObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the group list object. */\n    virtual bool linkGroupListObj(RXGroupList *list, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the group list object. */\n    virtual bool encode(RXGroupList *list, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some offset within the codeplug. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()   { return 0x0000; }\n      static constexpr unsigned int index()   { return 0x0002; }\n      static constexpr unsigned int members() { return 0x0004; }\n      /// @endcond\n    };\n    /** Some limits. */\n    struct Limit {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members() { return 10; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the group-list bank.\n   *\n   * Memory representation of the group-list bank (??h bytes):\n   * @verbinclude dr1801uv_grouplistbankelement.txt */\n  class GroupListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x01104; }\n\n    /** Returns the number of group lists defined. */\n    virtual unsigned int groupListCount() const;\n    /** Sets the number of group lists. */\n    virtual void setGroupListCount(unsigned int count);\n\n    /** Returns a reference to the group list at the given index. */\n    virtual GroupListElement groupList(unsigned int index) const;\n\n    /** Decodes all group lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links all group lists. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all group lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of group lists. */\n      static constexpr unsigned int groupListCount() { return 64; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int groupListCount() { return 0x0000; }\n      static constexpr unsigned int groupLists() { return 0x00004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of a zone.\n   *\n   * Memory representation of zone (68h bytes):\n   * @verbinclude dr1801uv_zoneelement.txt */\n  class ZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneElement(uint8_t *ptr);\n\n    bool isValid() const;\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00068; }\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Returns the number of entries. */\n    virtual unsigned int numEntries() const;\n    /** Returns the channel index of the n-th entry. */\n    virtual unsigned int entryIndex(unsigned int n);\n    /** Sets the n-th entry index. */\n    virtual void setEntryIndex(unsigned int n, unsigned int index);\n\n    /** Returns the index of the zone. */\n    virtual unsigned int index() const;\n    /** Sets the index of the zone. */\n    virtual void setIndex(unsigned int index);\n\n    /** Constructs a zone object from this element. */\n    virtual Zone *toZoneObj(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the zone object. */\n    virtual bool linkZoneObj(Zone *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the zone. */\n    virtual bool encode(Zone *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength() { return 32; }\n      /** The maximum number of channels in the zone. */\n      static constexpr unsigned int memberCount() { return 32; }\n    };\n\n  protected:\n    /** Some offset within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int nameLength() { return 0x0020; }\n      static constexpr unsigned int numEntries() { return 0x0022; }\n      static constexpr unsigned int index() { return 0x0024; }\n      static constexpr unsigned int members() { return 0x0028; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the zone bank.\n   *\n   * Memory representation of the zone bank (3cf8h bytes):\n   * @verbinclude dr1801uv_zonebankelement.txt */\n  class ZoneBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x03cf8; }\n\n    /** Returns the number of zones. */\n    virtual unsigned int zoneCount() const;\n    /** Sets the number of zones. */\n    virtual void setZoneCount(unsigned int count);\n\n    /** Returns the so-called up-zone index. */\n    virtual unsigned int upZoneIndex() const;\n    /** Sets the so-called up-zone index. */\n    virtual void setUpZoneIndex(unsigned int index);\n    /** Returns the so-called down-zone index. */\n    virtual unsigned int downZoneIndex() const;\n    /** Sets the so-called down-zone index. */\n    virtual void setDownZoneIndex(unsigned int index);\n\n    /** Returns a reference to the n-th zone. */\n    virtual ZoneElement zone(unsigned int index) const;\n\n    /** Decodes all zones. */\n    virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack()) const;\n    /** Links all zones. */\n    virtual bool link(Context &ctx, const ErrorStack &err = ErrorStack()) const;\n    /** Encodes all zones. */\n    virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of zones. */\n      static constexpr unsigned int zoneCount() { return 150; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int zoneCount() { return 0x0000; }\n      static constexpr unsigned int upZoneIndex() { return 0x0002; }\n      static constexpr unsigned int downZoneIndex() { return 0x0004; }\n      static constexpr unsigned int zones() { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the settings element.\n   *\n   * Memory representation of settings element (64h bytes):\n   * @verbinclude dr1801uv_settingselement.txt */\n  class SettingsElement: public Element\n  {\n  public:\n    /** Possible power-save modes. */\n    enum class PowerSaveMode {\n      Off = 0, Save50 = 1, Save25 = 2, Save12 = 3\n    };\n\n    /** Possible UI languages. */\n    enum class Language {\n      SimplifiedChinese = 0, English = 1\n    };\n\n    /** Possible squelch modes. */\n    enum class SquelchMode {\n      Normal = 0, Silent = 1\n    };\n\n    /** Possible ring tone variants. */\n    enum class RingTone {\n      Off = 0, RingTone1 = 1, RingTone2 = 2, RingTone3 = 3, RingTone4 = 4, RingTone5 = 5,\n      RingTone6 = 6, RingTone7 = 7, RingTone8 = 8, RingTone9 = 9, RingTone10 = 10, RingTone11 = 11,\n      RingTone12 = 12, RingTone13 = 13, RingTone14 = 14, RingTone15 = 15, RingTone16 = 16,\n      RingTone17 = 17, RingTone18 = 18, RingTone19 = 19, RingTone20 = 20\n    };\n\n    /** Possible backlight time settings. */\n    enum class BacklightTime {\n      Infinite = 0, Off = 1, On5s = 2, On10s = 3\n    };\n\n    /** Possible tuning modes. */\n    enum class TuningMode {\n      Channel = 0, VFO = 1\n    };\n\n    /** Possible display modes. */\n    enum class DisplayMode {\n      Number = 0, Name = 1, Frequency = 2\n    };\n\n    /** Possible dual-watch modes. */\n    enum class DualWatchMode {\n      Off = 0, DoubleDouble = 1, DoubleSingle = 2\n    };\n\n    /** Possible scan modes. */\n    enum class ScanMode {\n      Time = 0, Carrier = 1, Search = 2\n    };\n\n    /** Possible boot screen modes. */\n    enum class BootScreen {\n      Picture = 0, Text = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    SettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    SettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00064; }\n\n    /** Returns the radios DMR ID. */\n    virtual unsigned int dmrID() const;\n    /** Sets the radios DMR ID. */\n    virtual void setDMRID(unsigned int id);\n\n    /** Returns the the power-save mode. */\n    virtual PowerSaveMode powerSaveMode() const;\n    /** Sets the power-save mode.  */\n    virtual void setPowerSaveMode(PowerSaveMode mode);\n\n    /** Returns the VOX sensitivity [0,10].\n     * 0 means VOX off. */\n    virtual Level voxSensitivity() const;\n    /** Sets the VOX sensitivity [0,10].\n     * 0 means VOX off. */\n    virtual void setVOXSensitivity(Level sens);\n    /** Returns the VOX delay in ms. */\n    virtual Interval voxDelay() const;\n    /** Sets the VOX delay in ms. */\n    virtual void setVOXDelay(Interval ms);\n\n    /** Returns @c true if encryption is enabled. */\n    virtual bool encryptionEnabled() const;\n    /** Enables/disables encryption globally. */\n    virtual void enableEncryption(bool enable);\n\n    /** Returns @c true if the key-lock is enabled. */\n    virtual bool keyLockEnabled() const;\n    /** Enable/disable key-lock. */\n    virtual void enableKeyLock(bool enable);\n    /** Returns the key-lock delay in seconds. */\n    virtual unsigned int keyLockDelay() const;\n    /** Sets the key-lock delay in seconds. */\n    virtual void setKeyLockDelay(unsigned int sec);\n    /** Returns @c true if the side-key 1 gets locked too. */\n    virtual bool lockSideKey1() const;\n    /** Enables/disables locking the side-key 1. */\n    virtual void enableLockSideKey1(bool enable);\n    /** Returns @c true if the side-key 2 gets locked too. */\n    virtual bool lockSideKey2() const;\n    /** Enables/disables locking the side-key 2. */\n    virtual void enableLockSideKey2(bool enable);\n    /** Returns @c true if the PTT gets locked too. */\n    virtual bool lockPTT() const;\n    /** Enables/disables locking the PTT. */\n    virtual void enableLockPTT(bool enable);\n\n    /** Returns the UI language. */\n    virtual Language language() const;\n    /** Sets the UI language. */\n    virtual void setLanguage(Language lang);\n\n    /** Returns the squelch mode. */\n    virtual SquelchMode squelchMode() const;\n    /** Sets the squelch mode. */\n    virtual void setSquelchMode(SquelchMode mode);\n\n    /** Returns @c true, if the roger tones are enabled. */\n    virtual bool rogerTonesEnabled() const;\n    /** Enables/disables roger tones. */\n    virtual void enableRogerTones(bool enable);\n    /** Returns @c true if the DMR call out roger tone is enabled. */\n    virtual bool dmrCallOutToneEnabled() const;\n    /** Enables/disables the DMR call out roger tone. */\n    virtual void enableDMRCallOutTone(bool enable);\n    /** Returns @c true if the DMR voice end roger tone is enabled. */\n    virtual bool dmrVoiceEndToneEnabled() const;\n    /** Enables/disables the DMR voice end roger tone. */\n    virtual void enableDMRVoiceEndTone(bool enable);\n    /** Returns @c true if the DMR call end roger tone is enabled. */\n    virtual bool dmrCallEndToneEnabled() const;\n    /** Enables/disables the DMR call end roger tone. */\n    virtual void enableDMRCallEndTone(bool enable);\n    /** Returns @c true if the FM voice end roger tone is enabled. */\n    virtual bool fmVoiceEndToneEnabled() const;\n    /** Enables/disables the FM voice end roger tone. */\n    virtual void enableFMVoiceEndTone(bool enable);\n    /** Returns @c true if the FM call out roger tone is enabled. */\n    virtual bool fmCallOutToneEnabled() const;\n    /** Enables/disables the FM call out roger tone. */\n    virtual void enableFMCallOutTone(bool enable);\n    /** Returns @c true if the message tone is enabled. */\n    virtual bool messageToneEnabled() const;\n    /** Enables/disables message tone. */\n    virtual void enableMessageTone(bool enable);\n\n    /** Returns the ringtone. */\n    virtual RingTone ringTone() const;\n    /** Sets the ringtone. */\n    virtual void setRingTone(RingTone tone);\n\n    /** Returns the radio name. */\n    virtual QString radioName() const;\n    /** Sets the radio name. */\n    virtual void setRadioName(const QString &name);\n\n    /** Returns the reverse burst frequency in Hz. */\n    virtual float reverseBurstFrequency() const;\n    /** Sets the reverse burst frequency in Hz. */\n    virtual void setReverseBurstFrequency(float Hz);\n\n    /** Returns the backlight time settings. */\n    virtual BacklightTime backlightTime() const;\n    /** Sets the backlight time. */\n    virtual void setBacklightTime(BacklightTime time);\n\n    /** Returns @c true, if campanding is enabled. */\n    virtual bool campandingEnabled() const;\n    /** Enables/disables campanding. */\n    virtual void enableCampanding(bool enable);\n\n    /** Returns the tuning mode up-direction. */\n    virtual TuningMode tuningModeUp() const;\n    /** Sets the tuning mode up-direction. */\n    virtual void setTuningModeUp(TuningMode mode);\n    /** Returns the tuning mode down-direction. */\n    virtual TuningMode tuningModeDown() const;\n    /** Sets the tuning mode down-direction. */\n    virtual void setTuningModeDown(TuningMode mode);\n\n    /** Returns the display mode. */\n    virtual DisplayMode displayMode() const;\n    /** Sets the display mode. */\n    virtual void setDisplayMode(DisplayMode mode);\n\n    /** Returns the dual-watch mode. */\n    virtual DualWatchMode dualWatchMode() const;\n    /** Sets the dual-watch mode. */\n    virtual void setDualWatchMode(DualWatchMode mode);\n\n    /** Returns the scan mode. */\n    virtual ScanMode scanMode() const;\n    /** Sets the scan mode. */\n    virtual void setScanMode(ScanMode mode);\n\n    /** Returns the boot-screen mode. */\n    virtual BootSettings::BootDisplay bootScreen() const;\n    /** Sets the boot-screen mode. */\n    virtual void setBootScreen(BootSettings::BootDisplay mode);\n\n    /** Returns the boot-screen line 1. */\n    virtual QString bootLine1() const;\n    /** Sets the boot-screen line 1. */\n    virtual void setBootLine1(const QString &line);\n    /** Returns the boot-screen line 2. */\n    virtual QString bootLine2() const;\n    /** Sets the boot-screen line 2. */\n    virtual void setBootLine2(const QString &line);\n\n    /** Returns @c true if the LED is enabled. */\n    virtual bool ledEnabled() const;\n    /** Enables/disables the LED. */\n    virtual void enableLED(bool enabled);\n\n    /** Returns the lone-worker response time in seconds. */\n    virtual unsigned int loneWorkerResponseTime() const;\n    /** Sets the lone-worker response time in seconds. */\n    virtual void setLoneWorkerResponseTime(unsigned int sec);\n    /** Returns the lone-worker reminder time in seconds. */\n    virtual unsigned int loneWorkerReminderTime() const;\n    /** Sets the lone-worker resminder time in seconds. */\n    virtual void setLoneWorkerReminderTime(unsigned int sec);\n\n    /** Returns @c true if the boot password is enabled. */\n    virtual bool bootPasswordEnabled() const;\n    /** Returns the boot password. */\n    virtual QString bootPassword() const;\n    /** Sets and enables boot password. */\n    virtual void setBootPassword(const QString &passwd);\n    /** Clears and disables boot password. */\n    virtual void clearBootPassword();\n\n    /** Returns @c true if the programming password is enabled. */\n    virtual bool progPasswordEnabled() const;\n    /** Returns the programming password. */\n    virtual QString progPassword() const;\n    /** Sets and enables programming password. */\n    virtual void setProgPassword(const QString &passwd);\n    /** Clears and disables programming password. */\n    virtual void clearProgPassword();\n\n    /** Updates configuration. */\n    virtual bool decode(Config *config, const ErrorStack &err=ErrorStack());\n    /** Encode from config. */\n    virtual bool encode(Config *config, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int dmrID()                 { return 0x0000; }\n      static constexpr unsigned int powerSaveEnabled()      { return 0x0008; }\n      static constexpr unsigned int powerSaveMode()         { return 0x0009; }\n      static constexpr unsigned int voxSensitivity()        { return 0x000a; }\n      static constexpr unsigned int voxDelay()              { return 0x000c; }\n      static constexpr unsigned int encryptionEnabled()     { return 0x000d; }\n      static constexpr unsigned int keyLockDelay()          { return 0x000e; }\n      static constexpr Bit lockPTT()                  { return {0x000f, 0}; }\n      static constexpr Bit lockSideKey1()             { return {0x000f, 1}; }\n      static constexpr Bit lockSideKey2()             { return {0x000f, 2}; }\n      static constexpr unsigned int language()              { return 0x0010; }\n      static constexpr unsigned int squelchMode()           { return 0x0011; }\n      static constexpr unsigned int rogerTonesEnabled()     { return 0x0013; }\n      static constexpr unsigned int keyLockEnabled()        { return 0x0017; }\n      static constexpr unsigned int ringTone()              { return 0x0016; }\n      static constexpr unsigned int radioName()             { return 0x0018; }\n      static constexpr Bit dmrCallOutToneEnabled()    { return {0x0028, 1}; }\n      static constexpr Bit fmCallOutToneEnabled()     { return {0x0028, 2}; }\n      static constexpr Bit dmrVoiceEndToneEnabled()   { return {0x0028, 3}; }\n      static constexpr Bit fmVoiceEndToneEnabled()    { return {0x0028, 4}; }\n      static constexpr Bit dmrCallEndToneEnabled()    { return {0x0028, 5}; }\n      static constexpr Bit messageToneEnabled()       { return {0x0028, 6}; }\n      static constexpr unsigned int reverseBurstFrequency() { return 0x002c; }\n      static constexpr unsigned int backlightTime()         { return 0x002f; }\n      static constexpr unsigned int voxEnabled()            { return 0x0030; }\n      static constexpr unsigned int campandingEnabled()     { return 0x0032; }\n      static constexpr unsigned int tuningModeUp()          { return 0x0036; }\n      static constexpr unsigned int tunigModeDown()         { return 0x0037; }\n      static constexpr unsigned int displayMode()           { return 0x003c; }\n      static constexpr unsigned int dualWatchMode()         { return 0x003d; }\n      static constexpr unsigned int scanMode()              { return 0x003e; }\n      static constexpr unsigned int bootScreen()            { return 0x003f; }\n      static constexpr unsigned int bootLine1()             { return 0x0040; }\n      static constexpr unsigned int bootLine2()             { return 0x0048; }\n      static constexpr unsigned int ledEnabled()            { return 0x0050; }\n      static constexpr unsigned int loneWorkerResponseTime() { return 0x0051; }\n      static constexpr unsigned int loneWorkerReminderTime() { return 0x005c; }\n      static constexpr Bit progPasswordEnabled()       { return {0x0052, 0}; }\n      static constexpr Bit bootPasswordEnabled()       { return {0x0052, 1}; }\n      static constexpr unsigned int progPasswordLength()     { return 0x0053; }\n      static constexpr unsigned int progPassword()           { return 0x0054; }\n      static constexpr unsigned int boolPasswordLength()     { return 0x005d; }\n      static constexpr unsigned int bootPassword()           { return 0x005e; }\n      /// @endcond\n    };\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum radio name length. */\n      static constexpr unsigned int radioNameLength()     { return 16; }\n      /** Maximum boot-text lines length. */\n      static constexpr unsigned int bootLineLength()      { return 8; }\n      /** Maximum boot password length. */\n      static constexpr unsigned int bootPasswordLength()  { return 6; }\n      /** Maximum programming password length. */\n      static constexpr unsigned int progPasswordLength()  { return 6; }\n      /** VOX sensitivity levels. */\n      static constexpr Range<unsigned int> vox() { return {1, 3}; }\n    };\n  };\n\n\n  /** Implements the binary encoding of a scan list element.\n   *\n   * Memory representation of a scan list element (50h bytes):\n   * @verbinclude dr1801uv_scanlistelement.txt */\n  class ScanListElement: public Element\n  {\n  public:\n    /** Possible priority channel modes. */\n    enum class PriorityChannel {\n      None = 0, Fixed = 1, Selected = 2\n    };\n\n    /** Possible revert channel modes. */\n    enum class RevertChannel {\n      LastActive = 0, Fixed = 1, Selected = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ScanListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n\n    bool isValid() const;\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00050; }\n\n    /** Returns the index of the scan list. */\n    virtual unsigned int index() const;\n    /** Sets the index of the scan list. */\n    virtual void setIndex(unsigned int idx);\n\n    /** Returns the number of entries. */\n    virtual unsigned int entryCount() const;\n    /** Sets the number of entries. */\n    virtual void setEntryCount(unsigned int num);\n\n    /** Returns the priority channel 1 setting. */\n    virtual PriorityChannel priorityChannel1() const;\n    /** Sets the priority channel 1 setting. */\n    virtual void setPriorityChannel1(PriorityChannel mode);\n    /** Returns the priority channel 1 index. */\n    virtual unsigned int priorityChannel1Index() const;\n    /** Sets the priority channel 1 index. */\n    virtual void setPriorityChannel1Index(unsigned int index);\n\n    /** Returns the priority channel 2 setting. */\n    virtual PriorityChannel priorityChannel2() const;\n    /** Sets the priority channel 2 setting. */\n    virtual void setPriorityChannel2(PriorityChannel mode);\n    /** Returns the priority channel 2 index. */\n    virtual unsigned int priorityChannel2Index() const;\n    /** Sets the priority channel 2 index. */\n    virtual void setPriorityChannel2Index(unsigned int index);\n\n    /** Returns the revert channel setting. */\n    virtual RevertChannel revertChannel() const;\n    /** Sets the revert channel setting. */\n    virtual void setRevertChannel(RevertChannel mode);\n    /** Returns the revert channel index. */\n    virtual unsigned int revertChannelIndex() const;\n    /** Sets the revert channel index. */\n    virtual void setRevertChannelIndex(unsigned int index);\n\n    /** Returns the name of the scan list. */\n    virtual QString name() const;\n    /** Sets the name of the scan list. */\n    virtual void setName(const QString &name);\n\n    /** Returns the n-th entry index. */\n    virtual unsigned int entryIndex(unsigned int n);\n    /** Sets the n-th entry index. */\n    virtual void setEntryIndex(unsigned int n, unsigned int index);\n\n    /** Constructs a scan-list object from this element. */\n    virtual ScanList *toScanListObj(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the scan-list object. */\n    virtual bool linkScanListObj(ScanList *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the scan list. */\n    virtual bool encode(ScanList *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NO_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int name() { return 0x0010; }\n      static constexpr unsigned int priorityChannel1() { return 0x0002; }\n      static constexpr unsigned int priorityChannel1Index() { return 0x0004; }\n      static constexpr unsigned int priorityChannel2() { return 0x0003; }\n      static constexpr unsigned int priorityChannel2Index() { return 0x0006; }\n      static constexpr unsigned int revertChannel() { return 0x0008; }\n      static constexpr unsigned int revertChannelIndex() { return 0x000a; }\n      static constexpr unsigned int memberCount() { return 0x0001; }\n      static constexpr unsigned int memberIndices() { return 0x0030; }\n      /// @endcond\n    };\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum length of the name. */\n      static constexpr unsigned int nameLength() { return 32; }\n      /** Maximum number of channels in scan list. */\n      static constexpr unsigned int memberCount() { return 16; }\n    };\n  };\n\n  /** Implements the binary encoding of the scan-list bank.\n   *\n   * Holds up to 10 @c DR1801UVCodeplug::ScanListElement.\n   *\n   * Memory representation of the scan list bank (324h bytes):\n   * @verbinclude dr1801uv_scanlistbankelement.txt */\n  class ScanListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00324; }\n\n    /** Returns the number of scan lists. */\n    virtual unsigned int scanListCount() const;\n    /** Sets the number of scan lists. */\n    virtual void setScanListCount(unsigned int count);\n\n    /** Returns a reference to the n-th scan list. */\n    virtual ScanListElement scanList(unsigned int index) const;\n\n    /** Decodes all scan lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the scan lists. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all scan lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int scanListCount() { return 0x0000; }\n      static constexpr unsigned int scanLists() { return 0x0004; }\n      /// @endcond\n    };\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum number of scan lists. */\n      static constexpr unsigned int scanListCount() { return 10; }\n    };\n  };\n\n\n  /** Implements the binary representation of a single message.\n   *\n   * Memory representation of the message element (44h bytes):\n   * @verbinclude dr1801uv_messageelement.txt */\n  class MessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageElement(uint8_t *ptr);\n\n    void clear();\n    bool isValid() const;\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0044; }\n\n    /** Returns the index of the message. */\n    virtual unsigned int index() const;\n    /** Sets the index of the message. */\n    virtual void setIndex(unsigned int index);\n\n    /** Returns the message text. */\n    virtual QString text() const;\n    /** Sets the message text. */\n    virtual void setText(const QString &text);\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int textLength() { return 0x0001; }\n      static constexpr unsigned int text()  { return 0x0004; }\n      /// @endcond\n    };\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum message length. */\n      static constexpr unsigned int textLength() { return 64; }\n    };\n  };\n\n  /** Implements the binary encoding of the preset message bank element.\n   *\n   * The message bank contains all messages defined. See @c DR1801UVCodeplug::MessageElement for\n   * details.\n   *\n   * Memory representation of the message bank element (164h bytes):\n   * @verbinclude dr1801uv_messagebankelement.txt */\n  class MessageBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x00164; }\n\n    /** Returns the number of elements in the bank. */\n    virtual unsigned int messageCount() const;\n    /** Sets the number of messages. */\n    virtual void setMessageCount(unsigned int count);\n\n    /** Returns a reference to the n-th message. */\n    virtual MessageElement message(unsigned int n) const;\n\n    /** Decodes all scan lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all scan lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int messageCount() { return 8; }   ///< Maximum number of messages.\n    };\n\n  protected:\n    /** Offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int messageCount() { return 0x0000; }\n      static constexpr unsigned int messages() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the key settings.\n   *\n   * Memory representation of the message bank element (0018h bytes):\n   * @verbinclude dr1801uv_keysettingselement.txt */\n  class KeySettingsElement: public Element\n  {\n  public:\n    /** Possible key functions. */\n    enum class Function {\n      None = 0, ToggleAlertTones = 1, EmergencyOn = 2, EmergencyOff = 3, TogglePower = 4,\n      Monitor = 5, DeleteNuisance = 6, OneTouch1 = 7, OneTouch2 = 8, OneTouch3 = 9, OneTouch4 = 10,\n      OneTouch5 = 11, ToggleTalkaround = 13, ToggleScan = 14, ToggleEncryption = 15, ToggleVOX = 16,\n      ZoneSelect = 17, ToggleLoneWorker = 19, PhoneExit = 20\n    };\n\n  protected:\n    /** Hidden constructor. */\n    KeySettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    KeySettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00018; }\n\n    /** Returns the function for the side-key 1, short press. */\n    virtual Function sideKey1Short() const;\n    /** Sets the function for the side-key 1, short press. */\n    virtual void setSideKey1Short(Function func);\n    /** Returns the function for the side-key 1, long press. */\n    virtual Function sideKey1Long() const;\n    /** Sets the function for the side-key 1, long press. */\n    virtual void setSideKey1Long(Function func);\n    /** Returns the function for the side-key 2, short press. */\n    virtual Function sideKey2Short() const;\n    /** Sets the function for the side-key 2, short press. */\n    virtual void setSideKey2Short(Function func);\n    /** Returns the function for the side-key 2, long press. */\n    virtual Function sideKey2Long() const;\n    /** Sets the function for the side-key 2, long press. */\n    virtual void setSideKey2Long(Function func);\n    /** Returns the function for the top-key, short press. */\n    virtual Function topKeyShort() const;\n    /** Sets the function for the top-key, short press. */\n    virtual void setTopKeyShort(Function func);\n    /** Returns the function for the top-key, long press. */\n    virtual Function topKeyLong() const;\n    /** Sets the function for the side-key, long press. */\n    virtual void setTopKeyLong(Function func);\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int sideKey1Short() { return 0x0001; }\n      static constexpr unsigned int sideKey1Long() { return 0x0002; }\n      static constexpr unsigned int sideKey2Short() { return 0x0005; }\n      static constexpr unsigned int sideKey2Long() { return 0x0006; }\n      static constexpr unsigned int topKeyShort() { return 0x0009; }\n      static constexpr unsigned int topKeyLong() { return 0x000a; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the VFO settings.\n   *\n   * Memory representation of the VFO settings (0090h bytes):\n   * @verbinclude dr1801uv_vfobankelement.txt */\n  class VFOBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    VFOBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    VFOBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n\n    /** Returns a reference to the VFO A settings. */\n    virtual ChannelElement vfoA() const;\n    /** Returns a reference to the VFO A settings. */\n    virtual ChannelElement vfoB() const;\n\n    /** Returns the name of the first VFO. */\n    virtual QString nameA() const;\n    /** Sets the name for the first VFO. */\n    virtual void setNameA(const QString &name);\n    /** Returns the name of the second VFO. */\n    virtual QString nameB() const;\n    /** Sets the name for the second VFO. */\n    virtual void setNameB(const QString &name);\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** The maximum length of the VFO names (not set). */\n      static constexpr unsigned int nameLength() { return 20; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int vfoA() { return 0x0000; }\n      static constexpr unsigned int vfoB() { return 0x0034; }\n      static constexpr unsigned int nameA() { return 0x0068; }\n      static constexpr unsigned int nameB() { return 0x007c; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of an encryption key.\n   *\n   * Memory representation of the encryption key bank (000ch bytes):\n   * @verbinclude dr1801uv_encryptionkeyelement.txt */\n  class EncryptionKeyElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EncryptionKeyElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    EncryptionKeyElement(uint8_t *ptr);\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x000c; }\n\n    /** Returns the index of the key. */\n    virtual unsigned int index() const;\n    /** Sets the index of the key. */\n    virtual void setIndex(unsigned int index);\n\n    /** Returns the length of the key. */\n    virtual unsigned int keyLength() const;\n\n    /** Returns the key as a string. */\n    virtual QString key() const;\n    /** Sets the key. */\n    virtual void setKey(const QString &key);\n\n    /** Creates a key object for this element. */\n    virtual EncryptionKey *toKeyObj(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the key object. */\n    virtual bool linkKeyObj(EncryptionKey *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the key. */\n    virtual bool encode(EncryptionKey *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** The maximum length of the key. */\n      static constexpr unsigned int keyLength() { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index() { return 0x0000; }\n      static constexpr unsigned int length() { return 0x0001; }\n      static constexpr unsigned int key() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the encryption keys.\n   *\n   * Memory representation of the encryption key bank (0078h bytes):\n   * @verbinclude dr1801uv_encryptionkeybankelement.txt */\n  class EncryptionKeyBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EncryptionKeyBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    EncryptionKeyBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0078;}\n\n    /** Returns a reference to the encryption key. */\n    virtual EncryptionKeyElement key(unsigned int index) const;\n\n    /** Decodes the all encryption keys defined. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links all encryption keys. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all keys. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the key bank. */\n    struct Limit {\n      /** The number of keys. */\n      static constexpr unsigned int keyCount() { return 10; }\n    };\n  };\n\n\n  /** Implements the DTMF system.\n   *\n   * Memory representation of the DTMF system (000ch bytes):\n   * @verbinclude dr1801uv_dtmfsystemelement.txt */\n  class DTMFSystemElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFSystemElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFSystemElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0c; }\n\n    /** Returns @c true if the side-tone is enabled. */\n    virtual bool sideToneEnabled() const;\n    /** Enables/disable side-tone. */\n    virtual void enableSideTone(bool enable);\n\n    /** Returns the pre-time in milliseconds. */\n    virtual unsigned int preTime() const;\n    /** Sets the pre-time in milliseconds. */\n    virtual void setPreTime(unsigned int ms);\n\n    /** Returns the code duration in milliseconds. */\n    virtual unsigned int codeDuration() const;\n    /** Sets the code duration in milliseconds. */\n    virtual void setCodeDuration(unsigned int ms);\n\n    /** Returns the code interval in milliseconds. */\n    virtual unsigned int codeItervall() const;\n    /** Sets the code interval in milliseconds. */\n    virtual void setCodeItervall(unsigned int ms);\n\n    /** Returns the reset time in milliseconds. */\n    virtual unsigned int resetTime() const;\n    /** Sets the reset time in milliseconds. */\n    virtual void setResetTime(unsigned int ms);\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int sideTone() { return 0x0000; }\n      static constexpr unsigned int preTime() { return 0x0002; }\n      static constexpr unsigned int codeDuration() { return 0x0004; }\n      static constexpr unsigned int codeIntervall() { return 0x0006; }\n      static constexpr unsigned int resetTime() { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the DTMF systems bank. Holding all defined DTMF systems\n   *  (see @c DTMFSystemElement).\n   *\n   * Memory representation of the DTMF system bank (0034h bytes):\n   * @verbinclude dr1801uv_dtmfsystembankelement.txt */\n  class DTMFSystemBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFSystemBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFSystemBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x34; }\n\n    /** Returns the number of DTMF systems. */\n    virtual unsigned int systemCount() const;\n    /** Sets the number of DTMF systems. */\n    virtual void setSystemCount(unsigned int count);\n\n    /** Returns a reference to the n-th system. */\n    virtual DTMFSystemElement system(unsigned int n) const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of DTMF systems. */\n      static constexpr unsigned int systemCount() { return 4; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int systemCount() { return 0x0000; }\n      static constexpr unsigned int systems()     { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the DTMF ID.\n   *\n   * Memory representation of the DTMF ID (014h bytes):\n   * @verbinclude dr1801uv_dtmfidelement.txt */\n  class DTMFIDElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFIDElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFIDElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0014; }\n\n  public:\n    /** Returns the DTMF code/number. */\n    virtual QString number() const;\n    /** Sets the DTMF code/number. */\n    virtual void setNumber(const QString &number);\n\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of digits of the number. */\n      static constexpr unsigned int numberLength() { return 16; }\n    };\n\n  protected:\n    /** Returns the length of the number. */\n    virtual unsigned int numberLength() const;\n    /** Sets the number length. */\n    virtual void setNumberLength(unsigned int len);\n\n    /** Some internal offset within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int numberLength() { return 0x0000; }\n      static constexpr unsigned int number() { return 0x0004; }\n      /// @endcond\n    };\n\n    /** Translation table. */\n    static QVector<QChar> _bin_dtmf_tab;\n  };\n\n  /** Implements the DTMF ID bank. Holding all defined DTMF IDS\n   *  (@c see DTMFIDElement).\n   *\n   * Memory representation of the DTMF ID bank (0144h bytes):\n   * @verbinclude dr1801uv_dtmfidbankelement.txt */\n  class DTMFIDBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFIDBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFIDBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0144; }\n\n    /** Returns the number of IDs encoded. */\n    virtual unsigned int idCount() const;\n    /** Sets the ID count. */\n    virtual void setIDCount(unsigned int n);\n\n    /** Returns a reference to the n-th ID. */\n    virtual DTMFIDElement id(unsigned int n) const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of IDs. */\n      static constexpr unsigned int idCount() { return 16; }\n    };\n\n  protected:\n    /** Some offsets within the codeplug. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int idCount() { return 0x0000; }\n      static constexpr unsigned int ids() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the PTT ID.\n   *\n   * Memory representation of the PTT ID (014h bytes):\n   * @verbinclude dr1801uv_pttidelement.txt */\n  class PTTIDElement: public Element\n  {\n  public:\n    /** Possible modes of transmission. */\n    enum class Transmit {\n      None = 0, Start = 1, End = 2, Both = 3\n    };\n    /** Possible ID modes. */\n    enum class IDMode {\n      Forbidden = 0, Each = 1, Once = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    PTTIDElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    PTTIDElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x14; }\n\n    /** Returns @c true, if the DTMF system is set. */\n    virtual bool hasDTMFSystem() const;\n    /** Returns the DTMF system index. */\n    virtual unsigned int dtmfSystemIndex() const;\n    /** Sets the DTMF system index. */\n    virtual void setDTMFSystemIndex(unsigned int index);\n    /** Clears the DTMF system. */\n    virtual void clearDTMFSystem();\n\n    /** Returns the ID transmission mode. */\n    virtual Transmit transmitMode() const;\n    /** Sets the ID transmission mode. */\n    virtual void setTransmitMode(Transmit mode);\n\n    /** Returns the ID mode. */\n    virtual IDMode idMode() const;\n    /** Sets the ID mode. */\n    virtual void setIDMode(IDMode mode);\n\n    /** Returns @c true if the BOT DTMF ID is set. */\n    virtual bool hasBOTID() const;\n    /** Returns the BOT DTMF ID index. */\n    virtual unsigned int botIDIndex() const;\n    /** Sets the BOT DTMF ID index. */\n    virtual void setBOTIDIndex(unsigned int index);\n    /** Clears the BOT DTMF ID index. */\n    virtual void clearBOTID();\n\n    /** Returns @c true if the EOT DTMF ID is set. */\n    virtual bool hasEOTID() const;\n    /** Returns the EOT DTMF ID index. */\n    virtual unsigned int eotIDIndex() const;\n    /** Sets the EOT DTMF ID index. */\n    virtual void setEOTIDIndex(unsigned int index);\n    /** Clears the EOT DTMF ID index. */\n    virtual void clearEOTID();\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int dtmfSystemIndex() { return 0x0000; }\n      static constexpr unsigned int transmitMode()    { return 0x0001; }\n      static constexpr unsigned int idMode()          { return 0x0002; }\n      static constexpr unsigned int botIDIndex()      { return 0x0003; }\n      static constexpr unsigned int eotIDIndex()      { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the encoding of the DTMF PTT IDs. Holding all PTT IDs\n   *  (see @c PTTIDElement).\n   *\n   * Memory representation of the PTT ID bank (0104h bytes):\n   * @verbinclude dr1801uv_pttidbankelement.txt */\n  class PTTIDBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    PTTIDBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    PTTIDBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0104; }\n\n    /** Returns the number of PTT IDs defined. */\n    virtual unsigned int idCount() const;\n    /** Set the number of PTT IDs. */\n    virtual void setPTTIDCount(unsigned int n);\n\n    /** Returns a reference to the n-th PTT ID. */\n    virtual PTTIDElement pttID(unsigned int n);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of PTT IDs. */\n      static constexpr unsigned int idCount() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int idCount() { return 0x0000; }\n      static constexpr unsigned int ids()     { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the DTMF signaling settings.\n   *\n   * Memory representation of the settings (029ch bytes):\n   * @verbinclude dr1801uv_dtmfsettingselement.txt */\n  class DTMFSettingsElement: public Element\n  {\n  public:\n    /** Possible DTMF non-number codes. Usually used as separator and group codes. */\n    enum class NonNumber {\n      None = 0, A=0xa, B=0xb, C=0xc, D=0xd, Asterisk=0xe, Gate=0xf\n    };\n    /** Possible responses. */\n    enum class Response {\n      None=0, Reminder=1, Reply=2, Both=3\n    };\n    /** Possible kill actions. */\n    enum class Kill {\n      DisableTX = 0, DisableRXandTX = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    DTMFSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x029c; }\n\n    /** Returns the DTMF radio ID as a string. */\n    virtual QString radioID() const;\n    /** Sets the DTMF radio ID as a string. */\n    virtual void setRadioID(const QString &id);\n    /** Returns the DTMF kill code as a string. */\n    virtual QString killCode() const;\n    /** Sets the DTMF kill code as a string. */\n    virtual void setKillCode(const QString &code);\n    /** Returns the DTMF wake code as a string. */\n    virtual QString wakeCode() const;\n    /** Sets the DTMF wake code as a string. */\n    virtual void setWakeCode(const QString &code);\n\n    /** Returns the delimiter code. */\n    virtual NonNumber delimiter() const;\n    /** Sets the delimiter code. */\n    virtual void setDelimiter(NonNumber code);\n    /** Returns the group code. */\n    virtual NonNumber groupCode() const;\n    /** Sets the group code. */\n    virtual void setGroupCode(NonNumber code);\n\n    /** Returns the decode response. */\n    virtual Response response() const;\n    /** Sets the decode response. */\n    virtual void setResponse(Response resp);\n\n    /** Returns the auto-reset time in seconds [5,60s]. */\n    virtual unsigned int autoResetTime() const;\n    /** Set the auto-reset time in seconds. */\n    virtual void setAutoResetTime(unsigned int sec);\n\n    /** Returns @c true if the kill/wake decoding is endabled. */\n    virtual bool killWakeEnabled() const;\n    /** Enables/disables the kill/wake decoding. */\n    virtual void enableKillWake(bool enable);\n\n    /** Returns the kill type. */\n    virtual Kill killType() const;\n    /** Sets the kill type. */\n    virtual void setKillType(Kill type);\n\n    /** Returns a reference to the DTMF systems. */\n    virtual DTMFSystemBankElement dtmfSystems() const;\n    /** Returns a reference to the DTMF IDs. */\n    virtual DTMFIDBankElement dtmfIDs() const;\n    /** Returns a reference to the PTT ID bank. */\n    virtual PTTIDBankElement pttIDs() const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum length of the radio ID. */\n      static constexpr unsigned int radioIDLength() { return 5; }\n      /** The maximum length of the kill code. */\n      static constexpr unsigned int killCodeLength() { return 6; }\n      /** The maximum length of the wake code. */\n      static constexpr unsigned int wakeCodeLength() { return 6; }\n    };\n\n  protected:\n    /** Internal offsets within the settings element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int radioID() { return 0x0000; }\n      static constexpr unsigned int radioIDLength() { return 0x0005; }\n      static constexpr unsigned int killCode() { return 0x0008; }\n      static constexpr unsigned int killCodeLength() { return 0x000e; }\n      static constexpr unsigned int wakeCode() { return 0x0010; }\n      static constexpr unsigned int wakeCodeLength() { return 0x0016; }\n      static constexpr unsigned int delimiter() { return 0x0018; }\n      static constexpr unsigned int groupCode() { return 0x0019; }\n      static constexpr unsigned int response() { return 0x001a; }\n      static constexpr unsigned int autoResetTime() { return 0x001b; }\n      static constexpr unsigned int killWake() { return 0x001c; }\n      static constexpr unsigned int killType() { return 0x001d; }\n      static constexpr unsigned int dtmfSystems() { return 0x0020; }\n      static constexpr unsigned int dtmfIDs() { return 0x0054; }\n      static constexpr unsigned int pttIDs() { return 0x0198; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the alarm system.\n   *\n   * Memory representation of the alarm system (0018h bytes):\n   * @verbinclude dr1801uv_alarmsystembankelement.txt */\n  class AlarmSystemElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AlarmSystemElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AlarmSystemElement(uint8_t *ptr);\n\n    void clear();\n    bool isValid() const;\n\n    /** The size of the alarm system element. */\n    static constexpr unsigned int size() { return 0x0018; }\n\n    /** Returns the index of the system. */\n    virtual unsigned int index() const;\n    /** Sets the index of the element. */\n    virtual void setIndex(unsigned int index);\n    /** Clears the index. */\n    virtual void clearIndex();\n\n    /** Returns @c true if the alarm is enabled. */\n    virtual bool alarmEnabled() const;\n    /** Enables/disables the alarm. */\n    virtual void enableAlarm(bool enable);\n\n    /** Returns @c true if no alarm channel is specified. */\n    virtual bool noAlarmChannel() const;\n    /** Returns @c true if the alarm channel is the current channel. */\n    virtual bool alarmChannelIsSelected() const;\n    /** Returns the index of the alarm channel. */\n    virtual unsigned int alarmChannelIndex() const;\n    /** Sets the alarm channel index. */\n    virtual void setAlarmChannelIndex(unsigned int index);\n    /** Sets the alarm channel to the selected channel. */\n    virtual void setAlarmChannelSelected();\n    /** Clears the alarm channel. */\n    virtual void clearAlarmChannel();\n\n    /** Returns the name. */\n    virtual QString name() const;\n    /** Sets the system name. */\n    virtual void setName(const QString &name);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the codeplug. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int index()             { return 0x0000; }\n      static constexpr unsigned int alarmEnabled()      { return 0x0001; }\n      static constexpr unsigned int alarmChannelIndex() { return 0x0004; }\n      static constexpr unsigned int name()              { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the alarm system bank.\n   *\n   * Holding all alarm systems, see @c AlarmSystemElement.\n   *\n   * Memory representation of the alarm system bank (00c4h bytes):\n   * @verbinclude dr1801uv_alarmsystembankelement.txt */\n  class AlarmSystemBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    AlarmSystemBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AlarmSystemBankElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the alarm system bank. */\n    static constexpr unsigned int size() { return 0x00c4; }\n\n    /** Returns the number of alarm systems defined. */\n    virtual unsigned int alarmSystemCount() const;\n    /** Sets the number of alarm systems. */\n    virtual void setAlarmSystemCount(unsigned int n);\n\n    /** Returns a reference to the n-th alarm system. */\n    virtual AlarmSystemElement alarmSystem(unsigned int n) const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of alarm systems. */\n      static constexpr unsigned int alarmSystemCount() { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int alarmSystemCount() { return 0x0000; }\n      static constexpr unsigned int alarmSystems() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of the DMR settings.\n   *\n   * Memory representation of the DMR settings (0010h bytes):\n   * @verbinclude dr1801uv_dmrsettingselement.txt */\n  class DMRSettingsElement: public Element\n  {\n  public:\n    /** Possible SMS encodings. */\n    enum class SMSFormat {\n      CompressedIP = 0, DefinedData = 1, IPData = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    DMRSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DMRSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0010; }\n\n    /** Returns the hold-time in seconds. */\n    virtual Interval callHangTime() const;\n    /** Sets the hold-time in seconds. */\n    virtual void setCallHangTime(const Interval &dur);\n\n    /** Returns the remote-listen duration in seconds. */\n    virtual unsigned int remoteListen() const;\n    /** Sets the remote-listen duration in seconds. */\n    virtual void setRemoteListen(unsigned int sec);\n\n    /** Returns the active wait period in ms. */\n    virtual unsigned int activeWait() const;\n    /** Sets the active wait period in ms. */\n    virtual void setActiveWait(unsigned int ms);\n\n    /** Returns the active resend count. */\n    virtual unsigned int activeResend() const;\n    /** Sets the active resend count. */\n    virtual void setActiveResend(unsigned int count);\n\n    /** Returns the pre-send count. */\n    virtual unsigned int preSend() const;\n    /** Sets the pre-send count. */\n    virtual void setPreSend(unsigned int count);\n\n    /** Returns the voice head count. */\n    virtual unsigned int voiceHead() const;\n    /** Sets the voice head count. */\n    virtual void setVoiceHead(unsigned int count);\n\n    /** Returns the SMS format. */\n    virtual SMSFormat smsFormat() const;\n    /** Sets the SMS format. */\n    virtual void setSMSFormat(SMSFormat format);\n\n    /** Returns @c true if the kill encoding is enabled. */\n    virtual bool killEnc() const;\n    /** Enables kill encoding. */\n    virtual void enableKillEnc(bool enable);\n    /** Returns @c true if the kill decoding is enabled. */\n    virtual bool killDec() const;\n    /** Enables kill decoding. */\n    virtual void enableKillDec(bool enable);\n\n    /** Returns @c true if the active encoding is enabled. */\n    virtual bool activeEnc() const;\n    /** Enables active encoding. */\n    virtual void enableActiveEnc(bool enable);\n    /** Returns @c true if the active decoding is enabled. */\n    virtual bool activeDec() const;\n    /** Enables active decoding. */\n    virtual void enableActiveDec(bool enable);\n\n    /** Returns @c true if the check encoding is enabled. */\n    virtual bool checkEnc() const;\n    /** Enables check encoding. */\n    virtual void enableCheckEnc(bool enable);\n    /** Returns @c true if the check decoding is enabled. */\n    virtual bool checkDec() const;\n    /** Enables check decoding. */\n    virtual void enableCheckDec(bool enable);\n\n    /** Returns @c true if the call alter encoding is enabled. */\n    virtual bool callAlterEnc() const;\n    /** Enables call alter encoding. */\n    virtual void enableCallAlterEnc(bool enable);\n    /** Returns @c true if the call alter decoding is enabled. */\n    virtual bool callAlterDec() const;\n    /** Enables call alter decoding. */\n    virtual void enableCallAlterDec(bool enable);\n\n    /** Returns @c true if the remote monitor encoding is enabled. */\n    virtual bool remoteMonitorEnc() const;\n    /** Enables remote monitor encoding. */\n    virtual void enableRemoteMonitorEnc(bool enable);\n    /** Returns @c true if the remote monitor decoding is enabled. */\n    virtual bool remoteMonitorDec() const;\n    /** Enables remote monitor decoding. */\n    virtual void enableRemoteMonitorDec(bool enable);\n\n    /** Decodes the DMR settings. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes all keys. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n\n  public:\n    /** Some limits. */\n    struct Limit: public Element::Limit {\n      /** The range of hold time. */\n      static constexpr Range<Interval> holdTime() {\n        return {Interval::fromSeconds(1), Interval::fromSeconds(90)}; }\n      /** The range of remote listen duration. */\n      static constexpr Range<unsigned int> remoteListen() { return {10, 120}; }\n      /** The range of active wait period. */\n      static constexpr Range<unsigned int> activeWait() { return {120, 600}; }\n      /** The range of active resend count. */\n      static constexpr Range<unsigned int> activeResend() { return {1, 10}; }\n      /** The range of pre-send count. */\n      static constexpr Range<unsigned int> preSend() { return {4, 15}; }\n      /** The range of voice head count. */\n      static constexpr Range<unsigned int> voiceHead() { return {0, 20}; }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int holdTime() { return 0x0000; }\n      static constexpr unsigned int remoteListen() { return 0x0001; }\n      static constexpr unsigned int activeWait() { return 0x0002; }\n      static constexpr unsigned int activeResend() { return 0x0003; }\n      static constexpr unsigned int preSend() { return 0x0004; }\n      static constexpr unsigned int killEnc() { return 0x0005; }\n      static constexpr unsigned int activeEnc() { return 0x0006; }\n      static constexpr unsigned int checkEnc() { return 0x0007; }\n      static constexpr unsigned int killDec() { return 0x0008; }\n      static constexpr unsigned int activeDec() { return 0x0009; }\n      static constexpr unsigned int checkDec() { return 0x000a; }\n      static constexpr unsigned int smsFormat() { return 0x000b; }\n      static constexpr unsigned int voiceHead() { return 0x000c; }\n      static constexpr Bit callAlterEnc() { return {0x000d, 0}; }\n      static constexpr Bit callAlterDec() { return {0x000d, 1}; }\n      static constexpr Bit remoteMonitorEnc() { return {0x000d, 2}; }\n      static constexpr Bit remoteMonitorDec() { return {0x000d, 3}; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the binary encoding of a one-touch setting.\n   *\n   * Memory representation of the one-touch setting (0008h bytes):\n   * @verbinclude dr1801uv_onetouchsettingelement.txt */\n  class OneTouchSettingElement: public Element\n  {\n  public:\n    /** Possible actions to perform. */\n    enum class Action {\n      Call = 0, Message = 1\n    };\n    /** Possible one-touch types. */\n    enum class Type {\n      Disabled = 0, DMR=1, FM=2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    OneTouchSettingElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    OneTouchSettingElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0008; }\n\n    /** Returns @c true if the setting is enabled. */\n    bool isValid() const;\n\n    /** Returns @c true if a contact is set. */\n    virtual bool hasContact() const;\n    /** Returns the DMR contact index. */\n    virtual unsigned int contactIndex() const;\n    /** Sets the contact index. */\n    virtual void setContactIndex(unsigned int index);\n    /** Clears the contact. */\n    virtual void clearContact();\n\n    /** Returns the one-touch action. */\n    virtual Action action() const;\n    /** Sets the one-touch action. */\n    virtual void setAction(Action action);\n\n    /** Returns @c true, if a message is set. */\n    virtual bool hasMessage() const;\n    /** Returns the message index. */\n    virtual unsigned int messageIndex() const;\n    /** Sets the message index. */\n    virtual void setMessageIndex(unsigned int index);\n    /** Clears the message. */\n    virtual void clearMessage();\n\n    /** Returns the type of the one-touch setting. */\n    virtual Type type() const;\n    /** Sets the type of the one-touch setting. */\n    virtual void setType(Type type);\n\n    /** Returns @c true if a DTMF ID is set. */\n    virtual bool hasDTMFID() const;\n    /** Returns the DTMF ID index. */\n    virtual unsigned int dtmfIDIndex() const;\n    /** Sets the DTMF ID index. */\n    virtual void setDTMFIDIndex(unsigned int index);\n    /** Clears the DTMF ID index. */\n    virtual void clearDTMFIDIndex();\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contactIndex() { return 0x0000; }\n      static constexpr unsigned int action() { return 0x0002; }\n      static constexpr unsigned int messageIndex() { return 0x0003; }\n      static constexpr unsigned int type() { return 0x0004; }\n      static constexpr unsigned int dtmfIDIndex() { return 0x0005; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the binary encoding of the one-touch settings.\n   *\n   * Holding all one-touch settings, see @c OneTouchSettingElement.\n   *\n   * Memory representation of the one-touch settings (0028h bytes):\n   * @verbinclude dr1801uv_onetouchsettingselement.txt */\n  class OneTouchSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    OneTouchSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    OneTouchSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0028; }\n\n    /** Returns the number of one-touch settings. */\n    virtual unsigned int settingsCount() const;\n\n    /** Returns a reference to the n-th one-touch setting. */\n    virtual OneTouchSettingElement setting(unsigned int n) const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Returns the maximum number of one-touch settings. */\n      static constexpr unsigned int settingsCount() { return 5; }\n    };\n\n  protected:\n    /** Some internal offset. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int settings() { return 0x0000; }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  /** Default constructor. */\n  explicit DR1801UVCodeplug(QObject *parent = nullptr);\n\n  Config * preprocess(Config *config, const ErrorStack &err) const;\n  bool postprocess(Config *config, const ErrorStack &err) const;\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n  bool encode(Config *config, const Flags &flags, const ErrorStack &err=ErrorStack());\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Decode codeplug elements. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link decoded elements. */\n  virtual bool linkElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode all elements. */\n  virtual bool encodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Defines the offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int size()              { return 0x1dd90; }\n    static constexpr unsigned int settings()          { return 0x003b4; }\n    static constexpr unsigned int zoneBank()          { return 0x00418; }\n    static constexpr unsigned int messageBank()       { return 0x04110; }\n    static constexpr unsigned int contactBank()       { return 0x04334; }\n    static constexpr unsigned int scanListBank()      { return 0x0a338; }\n    static constexpr unsigned int channelBank()       { return 0x0a65c; }\n    static constexpr unsigned int keySettings()       { return 0x1c6c4; }\n    static constexpr unsigned int groupListBank()     { return 0x1c6dc; }\n    static constexpr unsigned int encryptionKeyBank() { return 0x1d7e0; }\n    static constexpr unsigned int dtmfSettings()      { return 0x1d858; }\n    static constexpr unsigned int alarmSettings()     { return 0x1daf4; }\n    static constexpr unsigned int dmrSettings()       { return 0x1dbb8; }\n    static constexpr unsigned int vfoBank()           { return 0x1dd00; }\n    /// @endcond\n  };\n};\n\n#endif // DR1801UVCODEPLUG_HH\n"
  },
  {
    "path": "lib/dr1801uv_filereader.cc",
    "content": "#include \"dr1801uv_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_ADDR 0x00000000\n#define SEGMENT0_SIZE 0x0001dd90\n\nbool\nDR1801UVFileReader::read(const QString &filename, DR1801UVCodeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename\n                << \"': File does not exisist.\";\n    return false;\n  }\n  if (0x1dd90 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': File size is not 1dd90h bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename\n                << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n  char *ptr = (char *)codeplug->data(SEGMENT0_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/dr1801uv_filereader.hh",
    "content": "#ifndef DR1801UVFILEREADER_HH\n#define DR1801UVFILEREADER_HH\n\n#include \"dr1801uv_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug\n * data as written to the device. This makes the decoding of the manufacturer codeplug files very\n * easy.\n *\n * @ingroup dr1801uv */\nclass DR1801UVFileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err On error, contains an error message.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, DR1801UVCodeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // GD77FILEREADER_HH\n"
  },
  {
    "path": "lib/dr1801uv_interface.cc",
    "content": "#include \"dr1801uv_interface.hh\"\n#include <QtEndian>\n#include \"logger.hh\"\n#include \"dr1801uv.hh\"\n\n// Identifies itself as \"Prolific USB-serial controller\" (chip in cable).\n#define USB_VID 0x067b\n#define USB_PID 0x23a3\n\n#define WRITE_CODEPLUG_OFFSET 0x00000304\n#define WRITE_CODEPLUG_SIZE   0x0001da8c\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface::PrepareReadRequest\n * ********************************************************************************************* */\nDR1801UVInterface::PrepareReadRequest::PrepareReadRequest(uint32_t baudrate)\n  : baudrate(qToBigEndian(baudrate))\n{\n  // pass...\n}\n\nvoid\nDR1801UVInterface::PrepareReadRequest::setBaudrate(uint32_t baudrate) {\n  this->baudrate = qToBigEndian(baudrate);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface::PrepareReadResponse\n * ********************************************************************************************* */\nbool\nDR1801UVInterface::PrepareReadResponse::isSuccessful() const {\n  return 0x01 == success;\n}\nuint32_t\nDR1801UVInterface::PrepareReadResponse::getSize() const {\n  return qFromBigEndian(size);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface::PrepareWriteRequest\n * ********************************************************************************************* */\nDR1801UVInterface::PrepareWriteRequest::PrepareWriteRequest(uint32_t s, uint32_t speed, uint16_t crc=0)\n  : _unknown0(qToBigEndian((uint16_t)0x0001)), size(qToBigEndian(s)),\n    checksum(crc), baudRate(qToBigEndian(speed))\n{\n  // pass...\n}\n\nvoid\nDR1801UVInterface::PrepareWriteRequest::updateCRC(const uint8_t *data, size_t length) {\n  uint16_t *ptr = (uint16_t*)data;\n  for (unsigned int i=0; i<length/2;i++) {\n    checksum ^= ptr[i];\n  }\n}\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface::PrepareWriteResponse\n * ********************************************************************************************* */\nbool\nDR1801UVInterface::PrepareWriteResponse::isSuccessful() const {\n  return 0x0100 == qFromBigEndian(responseCode);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface::CodeplugWriteResponse\n * ********************************************************************************************* */\nbool\nDR1801UVInterface::CodeplugWriteResponse::isSuccessful() const {\n  return 0x01 == success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DR1801UVInterface\n * ********************************************************************************************* */\nDR1801UVInterface::DR1801UVInterface(const USBDeviceDescriptor &descriptor,\n                                     const ErrorStack &err, QObject *parent)\n  : AuctusA6Interface(descriptor, err, parent), _identifier()\n{\n  if (! enterProgrammingMode(err)) {\n    errMsg(err) << \"Cannot connect to DR-1801UV.\";\n    return;\n  }\n\n  QString id;\n  if (! getDeviceInfo(id, err)) {\n    errMsg(err) << \"Cannot connect to DR-1801UV.\";\n    return;\n  }\n\n  QStringList lst = id.split(\",\");\n  if (lst.size()<2) {\n    errMsg(err) << \"Cannot extract device ID from device info '\" << id\n                << \"'. Expected comma-separated list of strings.\";\n    _state = ERROR;\n    return;\n  }\n\n  _identifier = lst.at(1);\n}\n\nRadioInfo\nDR1801UVInterface::identifier(const ErrorStack &err) {\n  if ((!isOpen()) || (IDLE != _state) || _identifier.isEmpty())\n    return RadioInfo();\n  if (\"BF1801\" == _identifier) {\n    return DR1801UV::defaultRadioInfo();\n  }\n\n  errMsg(err) << \"Unknown device '\" << _identifier << \"'.\";\n  return RadioInfo();\n}\n\nbool\nDR1801UVInterface::readCodeplug(\n    Codeplug &codeplug, std::function<void(unsigned int, unsigned int)> progress,\n    const ErrorStack &err)\n{\n  if (! isOpen()) {\n    errMsg(err) << \"Cannot read codeplug from device: Interface not open.\";\n    return false;\n  }\n\n  if (IDLE != _state) {\n    errMsg(err) << \"Cannot read codeplug from device: Interface not in idle state. \"\n                << \"State=\" << _state << \".\";\n    return false;\n  }\n\n  PrepareReadResponse resp;\n  if (! prepareReading(ReadSpeed, resp, err)) {\n    errMsg(err) << \"Cannot start reading the codeplug from \" << _identifier << \".\";\n    _state = ERROR;\n    return false;\n  }\n\n  unsigned int bytesToTransfer = resp.getSize(), total = bytesToTransfer;\n  if (! startReading(err)) {\n    errMsg(err) << \"Cannot start reading the codeplug form \" << _identifier << \".\";\n    _state = ERROR;\n    return false;\n  }\n\n  if (codeplug.image(0).element(0).memSize() != bytesToTransfer) {\n    errMsg(err) << \"Codeplug size mismatch! Expected \" << codeplug.image(0).element(0).memSize()\n                << \" radio sends \" << bytesToTransfer << \".\";\n    _state = ERROR;\n    return false;\n  }\n\n  if (progress)\n    progress(0, total);\n  logDebug() << \"Start reading \" << bytesToTransfer << \"b of codeplug memory.\";\n\n  unsigned int offset = 0;\n  while (bytesToTransfer) {\n    unsigned n = std::min(256U, bytesToTransfer);\n    if (! AuctusA6Interface::read(codeplug.image(0).data(offset), n, 2000, err)) {\n      errMsg(err) << \"Cannot read from device '\" << portName() << \"'.\";\n      _state = ERROR;\n      return false;\n    }\n    offset += n; bytesToTransfer -= n;\n    if (progress)\n      progress(offset, total);\n  }\n\n  // Set the baud-rate back to 9600\n  logDebug() << \"Reset baudrate to \" << DefaultSpeed << \".\";\n  if (! this->setBaudRate(DefaultSpeed)) {\n    errMsg(err) << \"Cannot set baud-rate of serial port '\" << portName() << \"'.\";\n    return false;\n  }\n  QThread::msleep(250);\n\n  _state = IDLE;\n\n  return true;\n}\n\nbool\nDR1801UVInterface::writeCodeplug(\n    const Codeplug &codeplug, std::function<void(unsigned int, unsigned int)> progress,\n    const ErrorStack &err)\n{\n  if (! isOpen()) {\n    errMsg(err) << \"Cannot write codeplug to device: Interface not open.\";\n    return false;\n  }\n\n  if (IDLE != _state) {\n    errMsg(err) << \"Cannot write codeplug to device: Interface not in idle state. \"\n                << \"State=\" << _state << \".\";\n    return false;\n  }\n\n  if (! enterProgrammingMode(err)) {\n    errMsg(err) << \"Cannot write codeplug.\";\n    return false;\n  }\n\n  // Compute CRC over codeplug\n  uint16_t crc = 0;\n  unsigned int offset = WRITE_CODEPLUG_OFFSET,\n      bytesToTransfer = WRITE_CODEPLUG_SIZE,\n      total = bytesToTransfer;\n  for (unsigned int i=0; i<bytesToTransfer/2; i++) {\n    crc ^= *((const uint16_t*)codeplug.image(0).data(offset +2*i));\n  }\n\n  if (! prepareWriting(bytesToTransfer, DR1801UVInterface::WriteSpeed, crc, err) ) {\n    errMsg(err) << \"Cannot initialize codeplug write.\";\n    return false;\n  }\n\n  if (progress)\n    progress(0, total);\n\n  logDebug() << \"Write codeplug...\";\n  while (bytesToTransfer) {\n    uint32_t n = std::min(200U, bytesToTransfer);\n    if (! QSerialPort::write((char*)codeplug.data(offset), n)) {\n      errMsg(err) << \"Cannot write codeplug to device.\";\n      return false;\n    }\n    // Wait for bytes written\n    while (bytesToWrite()) {\n      if (! waitForBytesWritten(2000)) {\n        errMsg(err) << errorString();\n        errMsg(err) << \"Cannot write codeplug to the device.\";\n        _state = ERROR;\n        return false;\n      }\n    }\n\n    if (bytesAvailable()) {\n      logDebug() << \"Oops\";\n      logDebug() << \"Unexpected data \" << readAll().toHex() << \".\";\n    }\n\n    offset += n; bytesToTransfer -= n;\n    if (progress)\n      progress(offset, total);\n  }\n\n  logDebug() << \"Receive write-ACK.\";\n  if (! receiveWriteACK(err)) {\n    errMsg(err) << \"Cannot finish write operation properly. Partial write?\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err);\n  return false;\n}\n\nbool\nDR1801UVInterface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(data); Q_UNUSED(nbytes); Q_UNUSED(err);\n  return false;\n}\n\nbool\nDR1801UVInterface::read_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return false;\n}\n\n\nbool\nDR1801UVInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err);\n  return false;\n}\n\nbool\nDR1801UVInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(data); Q_UNUSED(nbytes); Q_UNUSED(err);\n  return false;\n}\n\nbool\nDR1801UVInterface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return false;\n}\n\nUSBDeviceInfo\nDR1801UVInterface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, 0, 0, false);\n}\n\nQList<USBDeviceDescriptor>\nDR1801UVInterface::detect(bool saveOnly) {\n  // if non-save devices are allowed, search for all USB AMC-CDC devices.\n  if (! saveOnly)\n    return USBSerial::detect();\n  // otherwise, return none\n  return QList<USBDeviceDescriptor>();\n}\n\nbool\nDR1801UVInterface::getDeviceInfo(QString &info, const ErrorStack &err) {\n  uint8_t response[255], respLen = 255;\n  if (! send_receive(REQUEST_INFO, nullptr, 0, response, respLen, err)) {\n    errMsg(err) << \"Cannot request device information.\";\n    _state = ERROR;\n    return false;\n  }\n\n  // check status byte\n  if (1 > respLen) {\n    errMsg(err) << \"Invalid response length.\";\n    errMsg(err) << \"Cannot request device information.\";\n    _state = ERROR;\n    return false;\n  }\n\n  if (0x01 != response[0]) {\n    errMsg(err) << \"Device returned no success. Expected 01h, got \"\n                << QString::number(response[0],16) << \"h.\";\n    errMsg(err) << \"Cannot request device information.\";\n    _state = ERROR;\n    return false;\n  }\n\n  if (1 < respLen) {\n    logDebug() << QByteArray((const char *)response+1, respLen-1);\n    info = QString::fromLatin1((char *)(response+1), respLen-1);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Internal methods communicating with the device.\n * ********************************************************************************************* */\nbool\nDR1801UVInterface::enterProgrammingMode(const ErrorStack &err) {\n  uint8_t response[255], respLen=255;\n\n  if (! send_receive(ENTER_PROGRAMMING_MODE, nullptr, 0, response, respLen, err)) {\n    errMsg(err) << \"Cannot enter programming mode.\";\n    _state = ERROR;\n    return false;\n  }\n\n  if ((1 != respLen) || (0x01 != response[0])) {\n    errMsg(err) << \"Unexpected response from radio. Expected 01h, got \"\n                << QString::number(response[0], 16) << \"h.\";\n    errMsg(err) << \"Cannot enter programming mode.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVInterface::checkProgrammingPassword(const ErrorStack &err) {\n  // No argument -> no password\n  uint8_t request[] = {0x00}, reqLen=sizeof(request),\n      response[255], respLen=255;\n\n  if (! send_receive(CHECK_PROG_PASSWORD, request, reqLen, response, respLen, err)) {\n    errMsg(err) << \"Cannot enter programming mode.\";\n    _state = ERROR;\n    return false;\n  }\n\n  if ((1 != respLen) || (0x02 != response[0])) {\n    errMsg(err) << \"Unexpected response from radio. Expected 02h, got \"\n                << QString::number(response[0], 16) << \"h.\";\n    errMsg(err) << \"Cannot enter programming mode.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nDR1801UVInterface::prepareReading(uint32_t baudrate, PrepareReadResponse &response, const ErrorStack &err) {\n  PrepareReadRequest request(baudrate);\n  uint8_t respSize = sizeof(response);\n\n  if (! send_receive(PREPARE_CODEPLUG_READ, (uint8_t *)&request, sizeof(request), (uint8_t *)&response, respSize, err)) {\n    errMsg(err) << \"Cannot prepare reading of codeplug.\";\n    return false;\n  }\n\n  if ((sizeof(PrepareReadResponse) != respSize) || (! response.isSuccessful())) {\n    errMsg(err) << \"Prepare reading of codeplug failed.\";\n    return false;\n  }\n\n  logDebug() << \"Set baudrate to \" << baudrate << \".\";\n  if (! this->setBaudRate(baudrate)) {\n    errMsg(err) << \"Cannot set baud-rate of serial port '\" << portName() << \"'.\";\n    return false;\n  }\n  QThread::msleep(1000);\n\n  return true;\n}\n\nbool\nDR1801UVInterface::prepareWriting(uint32_t size, uint32_t baudrate, uint16_t crc, const ErrorStack &err) {\n  PrepareWriteRequest request(size, baudrate, crc);\n  PrepareReadResponse response;\n  uint8_t respSize = sizeof(request);\n\n  if (! send_receive(PREPARE_CODEPLUG_WRITE, (uint8_t *)&request, sizeof(request), (uint8_t *)&response, respSize, err)) {\n    errMsg(err) << \"Cannot prepare writing of codeplug.\";\n    return false;\n  }\n\n  if ((sizeof(PrepareWriteResponse) != respSize) || (! response.isSuccessful())) {\n    errMsg(err) << \"Prepare writing of codeplug failed.\";\n    return false;\n  }\n\n  logDebug() << \"Set baudrate to \" << baudrate << \".\";\n  if (! this->setBaudRate(baudrate)) {\n    errMsg(err) << \"Cannot set baud-rate of serial port '\" << portName() << \"'.\";\n    return false;\n  }\n  QThread::msleep(1000);\n\n  _state = WRITE_THROUGH;\n  return true;\n}\n\nbool\nDR1801UVInterface::receiveWriteACK(const ErrorStack &err) {\n  // Wait for device response\n  uint16_t rcode = CODEPLUG_WRITTEN;\n  CodeplugWriteResponse response;\n  uint8_t size = sizeof(CodeplugWriteResponse);\n  if (! receive(rcode, (uint8_t*) &response, size, err)) {\n    errMsg(err) << \"Cannot complete codeplug write.\";\n    return false;\n  }\n\n  if (((rcode & 0x7fff) != CODEPLUG_WRITTEN) || (! response.isSuccessful())) {\n    errMsg(err) << \"Cannot complete codeplug write.\";\n    return false;\n  }\n\n  // Set the baud-rate back to default speed\n  logDebug() << \"Reset baudrate to \" << DefaultSpeed;\n  if (! this->setBaudRate(DefaultSpeed)) {\n    errMsg(err) << \"Cannot set baud-rate of serial port '\" << portName() << \"'.\";\n    return false;\n  }\n  QThread::msleep(1000);\n  _state = IDLE;\n\n  return true;\n}\n\nbool\nDR1801UVInterface::startReading(const ErrorStack &err) {\n  if (! send(START_READ_DATA, 0, 0, err)) {\n    errMsg(err) << \"Cannot initialize codeplug read.\";\n    return false;\n  }\n  flush();\n\n _state = READ_THROUGH;\n return true;\n}\n"
  },
  {
    "path": "lib/dr1801uv_interface.hh",
    "content": "#ifndef DR1801UVINTERFACE_HH\n#define DR1801UVINTERFACE_HH\n\n#include \"auctus_a6_interface.hh\"\n#include <functional>\n\n\n// Forward declarations\nclass Codeplug;\n\n/** Implements the actual interface to the DR-1801UV, which builds upon the @c AuctusA6Interface.\n *\n * @ingroup dr1801uv */\nclass DR1801UVInterface : public AuctusA6Interface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs an interface to the BTECH DR-1801UV from the specifeid USB descriptor. */\n  DR1801UVInterface(const USBDeviceDescriptor &descriptor,\n                    const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  RadioInfo identifier(const ErrorStack &err);\n\n  /** Reads the codeplug from the device blocking. */\n  bool readCodeplug(Codeplug &codeplug, std::function<void (unsigned int, unsigned int)> progress,\n                    const ErrorStack &err=ErrorStack());\n\n  /** Writes the codeplug to the device blocking. */\n  bool writeCodeplug(const Codeplug &codeplug, std::function<void(unsigned int, unsigned int)> progress,\n                     const ErrorStack &err=ErrorStack());\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Some default speeds. */\n  enum DefaultTransferSpeed {\n    /** Initial speed, used to send commands. */\n    DefaultSpeed = QSerialPort::Baud9600,\n    /** Speed for reading the codeplug. */\n    ReadSpeed = QSerialPort::Baud115200,\n    /** Speed for writing the codeplug. */\n    WriteSpeed = QSerialPort::Baud9600\n  };\n\n  /** Implemented commands. */\n  enum Command {\n    REQUEST_INFO           = 0x0000, ///< Returns some information about the device.\n    ENTER_PROGRAMMING_MODE = 0x0104, ///< Puts the device into the programming mode.\n    CHECK_PROG_PASSWORD    = 0x002b, ///< Checks the programming password.\n    PREPARE_CODEPLUG_READ  = 0x0100, ///< Sets baud rate and prepares codeplug read.\n    START_READ_DATA        = 0x0101, ///< Actually starts reading the codeplug.\n    PREPARE_CODEPLUG_WRITE = 0x0102, ///< Prepares writing the codeplug.\n    CODEPLUG_WRITTEN       = 0x0103  ///< Send by the device once the codeplug was written.\n  };\n\n  /** Request to set transfer speed and load codeplug into RAM for transfer. */\n  struct Q_PACKED PrepareReadRequest {\n    /** The transfer speed. */\n    uint32_t baudrate;\n\n    /** Constructor. */\n    PrepareReadRequest(uint32_t baudrate);\n    /** Sets the baudrate. */\n    void setBaudrate(uint32_t baudrate);\n  };\n\n  /** Response to a @c PREPARE_CODEPLUG_READ command.\n   *  Contains some information about the codeplug to read. */\n  struct Q_PACKED PrepareReadResponse {\n    uint8_t  success;                ///< If successful, set to 0x01.\n    uint32_t size;                   ///< Contains the size of the codeplug in big-endian.\n    uint8_t  _unknown[10];           ///< Some additional information.\n\n    /** Returns @c true, if the operation was successful. */\n    bool isSuccessful() const;\n    /** Returns the codeplug size in bytes. */\n    uint32_t getSize() const;\n  };\n\n  /** Request to prepare a codeplug write.\n   *  Contains some information about the codeplug to write and transfer speed to use. */\n  struct Q_PACKED PrepareWriteRequest {\n    uint16_t _unknown0;              ///< Just set to 0x0001\n    uint32_t size;                   ///< Contains the size of the codeplug in big-endian.\n    uint16_t checksum;               ///< A checksum over the codeplug to be written.\n    uint32_t baudRate;               ///< The baud rate for the transfer.\n\n    /** Constructor. */\n    PrepareWriteRequest(uint32_t size, uint32_t speed, uint16_t crc);\n    /** Updates the crc with the data. */\n    void updateCRC(const uint8_t *data, size_t length);\n  };\n\n  /** Response to a prepare-write request. Just contains a status word. */\n  struct Q_PACKED PrepareWriteResponse {\n    uint16_t responseCode;           ///< Response code.\n\n    /** Returns @c true, if the operation was successful. */\n    bool isSuccessful() const;\n  };\n\n  /** Response to a codeplug write. Just contains a status word. */\n  struct Q_PACKED CodeplugWriteResponse {\n    uint8_t  success;                ///< Response code.\n    uint16_t unknown;                ///< Some unknown data.\n\n    /** Returns @c true, if the operation was successful. */\n    bool isSuccessful() const;\n  };\n\n  /** Puts the device into programming mode. */\n  bool enterProgrammingMode(const ErrorStack &err=ErrorStack());\n  /** Reads some information about the device. */\n  bool getDeviceInfo(QString &info, const ErrorStack &err=ErrorStack());\n  /** Checks the if a programming password is set. */\n  bool checkProgrammingPassword(const ErrorStack &err=ErrorStack());\n\n  /** Prepares reading the codeplug. */\n  bool prepareReading(uint32_t baudrate, PrepareReadResponse &response, const ErrorStack &err=ErrorStack());\n  /** Starts the read operation. Once the operation is complete, the device will close the\n   * connection. */\n  bool startReading(const ErrorStack &err=ErrorStack());\n\n  /** Repares the codeplug write. */\n  bool prepareWriting(uint32_t size, uint32_t baudrate, uint16_t crc, const ErrorStack &err=ErrorStack());\n  /** Receives the ACK for writing the codeplug. */\n  bool receiveWriteACK(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the device identifier, once read. */\n  QString _identifier;\n};\n\n#endif // DR1801UVINTERFACE_HH\n"
  },
  {
    "path": "lib/dr1801uv_limits.cc",
    "content": "#include \"dr1801uv_limits.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"scanlist.hh\"\n#include \"zone.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n#include \"dr1801uv_codeplug.hh\"\n\nDR1801UVLimits::DR1801UVLimits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = false;\n  _callSignDBImplemented  = false;\n  _numCallSignDBEntries   = 0;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(\n          -1, DR1801UVCodeplug::SettingsElement::Limit::bootLineLength(), RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(\n          -1, DR1801UVCodeplug::SettingsElement::Limit::bootLineLength(), RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(DR1801UVCodeplug::SettingsElement::Limit::bootPasswordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(\n             1, DR1801UVCodeplug::SettingsElement::Limit::radioNameLength(), RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, DR1801UVCodeplug::ContactBankElement::Limit::contactCount(),\n          new RadioLimitObject {\n            { \"name\", new RadioLimitString(\n              1, DR1801UVCodeplug::ContactElement::Limit::nameLength(), RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, DR1801UVCodeplug::GroupListBankElement::Limit::groupListCount(),\n        new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, -1, RadioLimitString::ASCII) }, // Name is not encoded.\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, DR1801UVCodeplug::ChannelBankElement::Limit::channelCount(),\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(\n               1, DR1801UVCodeplug::ChannelBankElement::Limit::channelNameLength(),\n               RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(\n               1, DR1801UVCodeplug::ChannelBankElement::Limit::channelNameLength(),\n               RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, true)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, DR1801UVCodeplug::ZoneBankElement::Limit::zoneCount(), new RadioLimitObject {\n          { \"name\", new RadioLimitString(\n            1, DR1801UVCodeplug::ZoneElement::Limit::nameLength(), RadioLimitString::Unicode) },\n          { \"A\", new RadioLimitRefList(\n            0, DR1801UVCodeplug::ZoneElement::Limit::memberCount(), Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 0, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, DR1801UVCodeplug::ScanListBankElement::Limit::scanListCount(),\n        new RadioLimitObject{\n          { \"name\", new RadioLimitString(\n            1, DR1801UVCodeplug::ScanListElement::Limit::nameLength(), RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(\n            0, DR1801UVCodeplug::ScanListBankElement::Limit::scanListCount(), Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems. */\n  add(\"positioning\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/dr1801uv_limits.hh",
    "content": "#ifndef DR1801UVLIMITS_HH\n#define DR1801UVLIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the configuration limits for the BTECH DR-1801UV.\n * @ingroup dr1801uv */\nclass DR1801UVLimits : public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DR1801UVLimits(QObject *parent=nullptr);\n};\n\n#endif // DR1801UV\n"
  },
  {
    "path": "lib/dummyfilereader.cc",
    "content": "#include \"dummyfilereader.hh\"\n\nbool\nDummyFileReader::read(const QString &filename, Codeplug *codeplug, const ErrorStack &err) {\n  Q_UNUSED(codeplug); Q_UNUSED(filename);\n  errMsg(err) << \"Cannot read manufacturer codeplug file: not implemented yet.\";\n  return false;\n}\n"
  },
  {
    "path": "lib/dummyfilereader.hh",
    "content": "#ifndef DUMMYFILEREADER_HH\n#define DUMMYFILEREADER_HH\n\n#include \"errorstack.hh\"\n\nclass Codeplug;\n\n/** Just a class implementing the filereader \"interface\" that fails. */\nclass DummyFileReader\n{\npublic:\n  /** Does not read the specified file. Just returns an error. */\n  static bool read(const QString &filename, Codeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // DUMMYFILEREADER_HH\n"
  },
  {
    "path": "lib/encryptionextension.cc",
    "content": "#include \"encryptionextension.hh\"\n#include \"logger.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of EncryptionKey\n * ********************************************************************************************* */\nEncryptionKey::EncryptionKey(QObject *parent)\n  : ConfigObject(parent)\n{\n  // pass...\n}\n\nvoid\nEncryptionKey::clear() {\n  _key.clear();\n}\n\nbool\nEncryptionKey::fromHex(const QString &hex, const ErrorStack &err) {\n  return setKey(QByteArray::fromHex(hex.toLocal8Bit()), err);\n}\n\nQString\nEncryptionKey::toHex() const {\n  return _key.toHex();\n}\n\nconst QByteArray &\nEncryptionKey::key() const {\n  return _key;\n}\n\nbool\nEncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) {\n  if (key.isEmpty()) {\n    errMsg(err) << \"Cannot set empty encryption key.\";\n    return false;\n  }\n\n  if (_key == key)\n    return true;\n\n  _key = key;\n  emit modified(this);\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of BasicEncryptionKey\n * ********************************************************************************************* */\nBasicEncryptionKey::BasicEncryptionKey(QObject *parent)\n  : EncryptionKey(parent)\n{\n  // pass...\n}\n\nConfigItem *\nBasicEncryptionKey::clone() const {\n  BasicEncryptionKey *key = new BasicEncryptionKey();\n  if (! key->copy(*this)) {\n    key->deleteLater();\n    return nullptr;\n  }\n  return key;\n}\n\nYAML::Node\nBasicEncryptionKey::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = EncryptionKey::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"dmr\"] = node;\n  return type;\n}\n\nbool\nBasicEncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse basic encryption key: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node key = node.begin()->second;\n  return EncryptionKey::parse(key, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ARC4EncryptionKey\n * ********************************************************************************************* */\nARC4EncryptionKey::ARC4EncryptionKey(QObject *parent)\n  : EncryptionKey(parent)\n{\n  // pass...\n}\n\nConfigItem *\nARC4EncryptionKey::clone() const {\n  ARC4EncryptionKey *key = new ARC4EncryptionKey();\n  if (! key->copy(*this)) {\n    key->deleteLater();\n    return nullptr;\n  }\n  return key;\n}\n\nbool\nARC4EncryptionKey::fromHex(const QString &hex, const ErrorStack &err) {\n  if (10 != hex.size()) {\n    errMsg(err) << \"Cannot set RC4 (enhanced) ecryption key to '\" << hex << \"': Not a 40bit key.\";\n    return false;\n  }\n  return EncryptionKey::fromHex(hex);\n}\n\nbool\nARC4EncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) {\n  if (5 != key.size()) {\n    errMsg(err) << \"Cannot set RC4 (enhanced) ecryption key: Not a 40bit key.\";\n    return false;\n  }\n\n  return EncryptionKey::setKey(key, err);\n}\n\nYAML::Node\nARC4EncryptionKey::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = EncryptionKey::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"rc4\"] = node;\n  return type;\n}\n\nbool\nARC4EncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse basic encryption key: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node key = node.begin()->second;\n  return EncryptionKey::parse(key, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of AESEncryptionKey\n * ********************************************************************************************* */\nAESEncryptionKey::AESEncryptionKey(QObject *parent)\n  : EncryptionKey(parent)\n{\n  // pass...\n}\n\nConfigItem *\nAESEncryptionKey::clone() const {\n  AESEncryptionKey *key = new AESEncryptionKey();\n  if (! key->copy(*this)) {\n    key->deleteLater();\n    return nullptr;\n  }\n  return key;\n}\n\nbool\nAESEncryptionKey::setKey(const QByteArray &key, const ErrorStack &err) {\n  if (16 > key.size()) {\n    errMsg(err) << \"Cannot set AES ecryption key to '\" << key.toHex() << \"': Key smaller than 128bit.\";\n    return false;\n  }\n  return EncryptionKey::setKey(key, err);\n}\n\nYAML::Node\nAESEncryptionKey::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = EncryptionKey::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  type[\"aes\"] = node;\n  return type;\n}\n\nbool\nAESEncryptionKey::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse enhanced encryption key: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node key = node.begin()->second;\n  return EncryptionKey::parse(key, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of EncryptionKeys\n * ********************************************************************************************* */\nEncryptionKeys::EncryptionKeys(QObject *parent)\n  : ConfigObjectList({BasicEncryptionKey::staticMetaObject, ARC4EncryptionKey::staticMetaObject, AESEncryptionKey::staticMetaObject}, parent)\n{\n  // pass...\n}\n\nint\nEncryptionKeys::add(ConfigObject *obj, int row, bool unique) {\n  if ((nullptr == obj) || (! obj->is<EncryptionKey>())) {\n    logError() << \"Cannot add nullptr or non-encryption key objects to key list.\";\n    return -1;\n  }\n  return ConfigObjectList::add(obj, row, unique);\n}\n\nEncryptionKey *\nEncryptionKeys::key(int index) const {\n  if (index >= count())\n    return nullptr;\n  return get(index)->as<EncryptionKey>();\n}\n\nConfigItem *\nEncryptionKeys::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n  if (! node)\n    return nullptr;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create encryption key: Expected object with one child.\";\n    return nullptr;\n  }\n\n  QString type = QString::fromStdString(node.begin()->first.as<std::string>());\n  if ((\"basic\" == type) || (\"dmr\" == type)) {\n    return new BasicEncryptionKey();\n  } else if (\"rc4\" == type) {\n    return new ARC4EncryptionKey();\n  } else if (\"aes\" == type) {\n    return new AESEncryptionKey();\n  }\n\n  errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n              << \": Cannot create encryption key: Unknown type '\" << type << \"'.\";\n\n  return nullptr;\n\n}\n"
  },
  {
    "path": "lib/encryptionextension.hh",
    "content": "#ifndef ENCRYPTIONEXTENSION_HH\n#define ENCRYPTIONEXTENSION_HH\n\n#include \"configobject.hh\"\n\n\n/** Base class of all encryption keys.\n * @ingroup conf */\nclass EncryptionKey: public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"key\")\n\n  /** The key representation as a hex string. */\n  Q_PROPERTY(QString key READ toHex WRITE fromHex)\n\nprotected:\n  /** Hidden constructor. */\n  explicit EncryptionKey(QObject *parent=nullptr);\n\npublic:\n  void clear();\n\n  /** Creates a key from the given hex-string. */\n  virtual bool fromHex(const QString &hex, const ErrorStack &err=ErrorStack());\n  /** Converts a key to a hex string. */\n  virtual QString toHex() const;\n\n  /** Returns the binary key. */\n  const QByteArray &key() const;\n  /** Sets the binary key. */\n  virtual bool setKey(const QByteArray &key, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the key data.\n   * The size depends on the key type. */\n  QByteArray _key;\n};\n\n\n/** Represents a DMR (basic) encryption key.\n *\n * This is a variable sized key used for the DMR basic encryption method.\n *\n * @ingroup conf */\nclass BasicEncryptionKey: public EncryptionKey\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"description\", \"A basic DMR encryption key.\")\n  Q_CLASSINFO(\"longDescription\",\n              \"This is a variable sized pre-shared key that can be used to encrypt/decrypt traffic \"\n              \"on DMR channels. Encryption is forbidden in HAM radio context!\")\n\npublic:\n  /** Empty constructor. */\n  Q_INVOKABLE explicit BasicEncryptionKey(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n/** Represents an (enhanced) ARC4 encryption key.\n *\n * This is a 40bit key used for the DMR enhanced encryption method.\n *\n * @ingroup conf */\nclass ARC4EncryptionKey: public EncryptionKey\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"description\", \"An enhanced DMR encryption key.\")\n  Q_CLASSINFO(\"longDescription\",\n              \"This is a 40bit pre-shared RC4 key, that can be used to encrypt/decrypt traffic on \"\n              \"DMR channels. Encryption is forbidden in HAM radio context!\")\n\npublic:\n  /** Empty constructor. */\n  Q_INVOKABLE explicit ARC4EncryptionKey(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  bool fromHex(const QString &hex, const ErrorStack &err=ErrorStack());\n\n  bool setKey(const QByteArray &key, const ErrorStack &err);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n/** Represents a variable size AES (enhanced) encryption key.\n *\n * This is a 128-256bit key used for the DMR enhanced encryption method.\n *\n * @ingroup conf */\nclass AESEncryptionKey: public EncryptionKey\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"An AES (advanced) DMR encryption key.\")\n  Q_CLASSINFO(\"longDescription\",\n              \"This is a variable sized (usually 128-256bit) pre-shared key that can be used to \"\n              \"encrypt/decrypt traffic on DMR channels. Encryption is forbidden in HAM radio \"\n              \"context!\")\n\npublic:\n  /** Empty constructor. */\n  Q_INVOKABLE explicit AESEncryptionKey(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  bool setKey(const QByteArray &key, const ErrorStack &err);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\nQ_DECLARE_OPAQUE_POINTER(AESEncryptionKey*)\n\n\n/** The list of encryption keys.\n *\n * This list holds all encryption keys defined within the codeplug.\n *\n * @warning Please note that this is a commercial feature and forbidden for HAM radio use.\n *\n * @ingroup conf */\nclass EncryptionKeys: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit EncryptionKeys(QObject *parent=nullptr);\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\n  /** Returns the key the given index. */\n  EncryptionKey *key(int index) const;\n\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n\n#endif // ENCRYPTIONEXTENSION_HH\n"
  },
  {
    "path": "lib/errorstack.cc",
    "content": "#include \"errorstack.hh\"\n#include <QFileInfo>\n#include <logger.hh>\n\n/* ********************************************************************************************* *\n * Implementation of ErrorStack::Message\n * ********************************************************************************************* */\nErrorStack::Message::Message()\n  : _file(), _line(0), _message()\n{\n  // pass...\n}\n\nErrorStack::Message::Message(const QString &file, unsigned line, const QString &message)\n  : _file(file), _line(line), _message(message)\n{\n  // pass...\n}\n\nconst QString &\nErrorStack::Message::file() const {\n  return _file;\n}\n\nunsigned\nErrorStack::Message::line() const {\n  return _line;\n}\n\nconst QString &\nErrorStack::Message::message() const {\n  return _message;\n}\n\nQString\nErrorStack::Message::format() const {\n  return QString(\"In %1:%2: %3\").arg(QFileInfo(file()).fileName()).arg(line()).arg(message());\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ErrorStack::MessageStream\n * ********************************************************************************************* */\nErrorStack::MessageStream::MessageStream(const ErrorStack &stack, const QString &file, unsigned line)\n  : QTextStream(&_message), _stack(stack), _file(file), _line(line), _message()\n{\n  // pass...\n}\n\nErrorStack::MessageStream::~MessageStream() {\n  _stack.push(Message(_file, _line, _message));\n  LogMessage(LogMessage::ERROR, _file, _line) << _message;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ErrorStack::Stack\n * ********************************************************************************************* */\nErrorStack::Stack::Stack() noexcept\n  : _refcount(1), _errorMessageStack()\n{\n  // pass...\n}\n\nvoid\nErrorStack::Stack::push(const Message &msg) {\n  _errorMessageStack.push_front(msg);\n}\n\nvoid\nErrorStack::Stack::push(const Stack &other) {\n  if (other.isEmpty())\n    return;\n  for (int i=(other.count()-1); i>=0; i--) {\n    push(other.message(i));\n  }\n}\n\nbool\nErrorStack::Stack::isEmpty() const {\n  return 0 == count();\n}\n\nunsigned\nErrorStack::Stack::count() const {\n  return _errorMessageStack.count();\n}\n\nconst ErrorStack::Message &\nErrorStack::Stack::message(unsigned i) const {\n  return _errorMessageStack.at(i);\n}\n\nQString\nErrorStack::Stack::format(const QString &indent) const {\n  QString res;\n  if (isEmpty())\n    return res;\n  res += indent + message(0).format();\n  for (unsigned i=1; i<count(); i++)\n    res += QString(\"\\n%1%2\").arg(indent).arg(message(i).format());\n  return res;\n}\n\nvoid\nErrorStack::Stack::clear() {\n  _errorMessageStack.clear();\n}\n\nErrorStack::Stack *\nErrorStack::Stack::ref() {\n  _refcount++;\n  return this;\n}\n\nvoid\nErrorStack::Stack::unref() {\n  if (0 < _refcount)\n    _refcount--;\n  if (0 == _refcount)\n    delete this;\n}\n\n/* ********************************************************************************************* *\n * Implementation of ErrorStack\n * ********************************************************************************************* */\nErrorStack::ErrorStack() noexcept\n  : _stack(new ErrorStack::Stack())\n{\n  // pass...\n}\n\nErrorStack::ErrorStack(const ErrorStack &other)\n  : _stack(other._stack->ref())\n{\n  // pass...\n}\n\nErrorStack::~ErrorStack() {\n  _stack->unref();\n  _stack = nullptr;\n}\n\nErrorStack &\nErrorStack::operator =(const ErrorStack &other) {\n  _stack->unref();\n  _stack = other._stack->ref();\n  return *this;\n}\n\nbool\nErrorStack::isEmpty() const {\n  return _stack->isEmpty();\n}\n\nunsigned\nErrorStack::count() const {\n  return _stack->count();\n}\n\nconst ErrorStack::Message &\nErrorStack::message(unsigned i) const {\n  return _stack->message(i);\n}\n\nvoid\nErrorStack::push(const Message &msg) const {\n  _stack->push(msg);\n}\n\nvoid\nErrorStack::take(const ErrorStack &other) const {\n  _stack->push(*other._stack);\n  other._stack->clear();\n}\n\nQString\nErrorStack::format(const QString &indent) const {\n  return _stack->format(indent);\n}\n"
  },
  {
    "path": "lib/errorstack.hh",
    "content": "#ifndef ERRORSTACK_HH\n#define ERRORSTACK_HH\n\n#include <QList>\n#include <QString>\n#include <QTextStream>\n\n/** Implements a stack of error messages to provide a pretty formatted error traceback.\n *\n * This class is intended to be used like:\n * @code\n * class MyClass: public ErrorStack\n * {\n *\n * // [...]\n *\n *   bool someMethod(const ErrorStack &err=ErrorStack()) {\n *     // [...]\n *     if (someError) {\n *       errMsg() << \"Some error happened!\";\n *       return false;\n *     }\n *     // [...]\n *   }\n *\n * // [...]\n * }\n * @endcode\n *\n * The error message can then be obtained using the public methods. E.g.,\n * @code\n * // [...]\n * MyClass instance;\n * ErrorStack err;\n *\n * if (! instance.someMethod(err)) {\n *   QString msg = err.formatErrorMessages();\n *   // []\n * }\n * @endcode\n * @ingroup log */\nclass ErrorStack\n{\npublic:\n  class Stack;\n\n  /** Represents a single error message. That is, a tuple of file, line and message. */\n  class Message\n  {\n  public:\n    /** Empty constructor. */\n    Message();\n    /** Constructor from file, line and message. */\n    Message(const QString &file, unsigned line, const QString &message);\n\n    /** Returns the file name. */\n    const QString &file() const;\n    /** Returns the line within the file. */\n    unsigned line() const;\n    /** Returns the error message. */\n    const QString &message() const;\n    /** Formats the error messaege. */\n    QString format() const;\n\n  protected:\n    /** Holds the file path. */\n    QString _file;\n    /** Holds the line. */\n    unsigned _line;\n    /** Holds the error message. */\n    QString _message;\n  };\n\n  /** A helper class to assemble error messages as streams. Use the @c errorMessage macro. */\n  class MessageStream: public QTextStream\n  {\n  public:\n    /** Constructor. */\n    MessageStream(const ErrorStack &stack, const QString &file, unsigned line);\n    /** Destructor, puts the message on the stack. */\n    virtual ~MessageStream();\n\n  protected:\n    /** Holds a weak reference to the error stack to put the message on. */\n    const ErrorStack &_stack;\n    /** The file path. */\n    QString _file;\n    /** The line number. */\n    unsigned _line;\n    /** The message buffer. */\n    QString _message;\n  };\n\n  /** The actual error message stack. */\n  class Stack\n  {\n  public:\n    /** Empty constructor. */\n    Stack() noexcept;\n\n  public:\n    /** Returns @c true if there are any error messages. */\n    bool isEmpty() const;\n    /** Returns the number of error messages. */\n    unsigned count() const;\n    /** Returns a specific error message. */\n    const Message &message(unsigned i) const;\n    /** Returns a formatted string of error messages. */\n    QString format(const QString &indent=\"  \") const;\n\n    /** Adds an error message to the stack. */\n    void push(const Message &msg);\n    /** Adds the error messages from another stack. */\n    void push(const Stack &other);\n\n    /** Clears the error stack. */\n    void clear();\n\n    /** Returns a new reference to the stack. */\n    Stack *ref();\n    /** Dereferences a stack, this decreases the ref count. When 0 is reached, the stack is\n     * destroyed. */\n    void unref();\n\n  private:\n    /** Reference counter. */\n    unsigned _refcount;\n    /** Holds the stack of error messages. */\n    QList<Message> _errorMessageStack;\n  };\n\npublic:\n  /** Default constructor. */\n  ErrorStack() noexcept;\n  /** Copy constructor. */\n  ErrorStack(const ErrorStack &other);\n  /** Destructor. */\n  ~ErrorStack();\n\n  /** Copy assignment. */\n  ErrorStack &operator= (const ErrorStack &other);\n\n  /** Returns @c true, if the stack is empty. */\n  bool isEmpty() const;\n  /** Returns the number of elements on the stack. */\n  unsigned count() const;\n  /** Returns the i-th message from the stack. */\n  const Message &message(unsigned i) const;\n\n  /** Pushes a message on the stack. */\n  void push(const Message &msg) const;\n  /** Takes all messages from the other stack. */\n  void take(const ErrorStack &other) const;\n  /** Returns a formatted string of error messages. */\n  QString format(const QString &indent=\"  \") const;\n\nprotected:\n  /** A reference to the actual message stack. */\n  Stack *_stack;\n};\n\n\n/** Utility macro to assemble a message stream. */\n#define errMsg(stack) (ErrorStack::MessageStream(stack, __FILE__, __LINE__))\n\n#endif // ERRORSTACK_HH\n"
  },
  {
    "path": "lib/frequency.cc",
    "content": "#include \"frequency.hh\"\n#include \"logger.hh\"\n#include <QRegularExpression>\n\n\ninline const QString HzString = QStringLiteral(\"Hz\");\ninline const QString KHzString = QStringLiteral(\"kHz\");\ninline const QString MHzString = QStringLiteral(\"MHz\");\ninline const QString GHzString = QStringLiteral(\"GHz\");\n\n/* ******************************************************************************************** *\n * Implementation of FrequencyBase\n * ******************************************************************************************** */\nFrequencyBase::FrequencyBase(const FrequencyBase &other)\n  : _frequency(other._frequency)\n{\n  // pass...\n}\n\nFrequencyBase &\nFrequencyBase::operator =(const FrequencyBase &other) {\n  _frequency = other._frequency;\n  return *this;\n}\n\nQString\nFrequencyBase::format(Unit unit) const {\n  switch (unit) {\n  case Unit::Auto:\n    if (10000LL > std::abs(_frequency)) {\n      return format(Unit::Hz);\n    } else if (10000000LL > std::abs(_frequency)) {\n      return format(Unit::kHz);\n    } else if (10000000000LL > std::abs(_frequency)) {\n      return format(Unit::MHz);\n    }\n    return format(Unit::GHz);\n\n  case Unit::Hz:\n    return QString(\"%1 Hz\").arg(inHz());\n\n  case Unit::kHz:\n    return QString(\"%1 kHz\").arg(inkHz(), 0, 'g', 6);\n\n  case Unit::MHz:\n    return QString(\"%1 MHz\").arg(inMHz(), 0, 'g', 9);\n\n  case Unit::GHz:\n    return QString(\"%1 GHz\").arg(inGHz(), 0, 'g', 12);\n\n  }\n  return \"\";\n}\n\nbool\nFrequencyBase::parse(const QString &value, Qt::CaseSensitivity caseSensitivity) {\n\n  QRegularExpression::PatternOption patternOption = caseSensitivity == Qt::CaseInsensitive\n    ? QRegularExpression::PatternOption::CaseInsensitiveOption : QRegularExpression::PatternOption::NoPatternOption;\n\n  QRegularExpression re(R\"(\\s*(\\+|-)?\\s*([0-9]+)(?:\\.([0-9]*)|)\\s*([kMG]|[kMG]?Hz|)\\s*)\", patternOption);\n  QRegularExpressionMatch match = re.match(value);\n\n  if (!match.hasMatch())\n    return false;\n\n  bool isNegative = (\"-\" == match.captured(1));\n  auto unit = unitFromString(match.captured(4));\n  QString decimals = match.captured(3);\n  QString leading = match.captured(2);\n  _frequency = leading.toUInt();\n\n  switch (unit)  {\n  case Unit::kHz: {\n    _frequency *= 1000ULL;\n    unsigned long long factor = 100ULL;\n    for (int i=0; i<std::min((qsizetype)3, decimals.size()); i++) {\n      _frequency += decimals[i].digitValue()*factor;\n      factor /= 10;\n    }\n    // Rounding to proper Hz\n    if ((decimals.size()>3) && (decimals[3].digitValue()>=5))\n      _frequency += 1;\n  }\n  break;\n  case Unit::MHz: {\n    _frequency *= 1000000ULL;\n    unsigned long long factor = 100000ULL;\n    for (int i=0; i<std::min((qsizetype)6, decimals.size()); i++) {\n      _frequency += decimals[i].digitValue()*factor;\n      factor /= 10;\n    }\n    // Rounding to proper Hz\n    if ((decimals.size()>6) && (decimals[6].digitValue()>=5))\n      _frequency += 1;\n  }\n  break;\n  case Unit::GHz: {\n    _frequency *= 1000000000ULL;\n    unsigned long long factor = 100000000;\n    for (int i=0; i<std::min((qsizetype)9, decimals.size()); i++) {\n      _frequency += decimals[i].digitValue()*factor;\n      factor /= 10;\n    }\n    // Rounding to proper Hz\n    if ((decimals.size()>9) && (decimals[9].digitValue()>=5))\n      _frequency+=1;\n  }\n  break;\n  case Unit::Hz:\n  default:\n    ; // Do Nothing\n  }\n\n  if (isNegative)\n    _frequency = -_frequency;\n\n  return true;\n}\n\nFrequencyBase::Unit FrequencyBase::unit() const {\n  qint64 absFreq = std::abs(_frequency);\n  if (absFreq >= 1000000000LL)\n    return Unit::GHz;\n  else if (absFreq >= 1000000LL)\n    return Unit::MHz;\n  else if (absFreq >= 1000LL)\n    return Unit::kHz;\n  else\n    return Unit::Hz;\n}\n\nFrequencyBase::Unit FrequencyBase::unitFromString(const QString& input) const {\n  Unit result;\n  if (input.length() == 0) {\n    return Unit::MHz;\n  }\n\n  auto multiplier = input[0].toLower();\n\n  if (multiplier == 'k') {\n    result = Unit::kHz;\n  } else if (multiplier == 'm') {\n    result = Unit::MHz;\n  } else if (multiplier == 'g') {\n    result = Unit::GHz;\n  } else {\n    result = Unit::Hz;\n  }\n  return result;\n}\n\nbool FrequencyBase::isMultipleOf(Unit u) const {\n  switch(u) {\n  case Unit::kHz: return (_frequency % 1000LL) == 0;\n  case Unit::MHz: return (_frequency % 1000000LL) == 0;\n  case Unit::GHz: return (_frequency % 1000000000LL) == 0;\n  case Unit::Hz:\n    //fallthrough\n  default: return true;\n  }\n  return false; // Unreachable\n}\n\nQString FrequencyBase::unitName(Unit unit) {\n  switch(unit) {\n  case Unit::kHz: return KHzString;\n  case Unit::MHz: return MHzString;\n  case Unit::GHz: return GHzString;\n  case Unit::Hz:\n    //fallthrough\n  default: return HzString;\n  }\n  return QString();\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of FrequencyOffset\n * ******************************************************************************************** */\nFrequencyOffset::FrequencyOffset()\n  : FrequencyBase(0)\n{\n  // pass...\n}\n\nFrequencyOffset::FrequencyOffset(const FrequencyOffset &other)\n  : FrequencyBase(other)\n{\n  // pass...\n}\n\nFrequencyOffset &\nFrequencyOffset::operator =(const FrequencyOffset &other) {\n  FrequencyBase::operator=(other);\n  return *this;\n}\n\nFrequencyOffset\nFrequencyOffset::fromString(const QString &freq) {\n  FrequencyOffset f;\n  if (! f.parse(freq)) {\n    logWarn() << \"Cannot parse offset '\" << freq << \"'.\";\n  }\n  return f;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of Frequency\n * ******************************************************************************************** */\nFrequency::Frequency(quint64 Hz)\n  : FrequencyBase(Hz)\n{\n  // pass...\n}\n\nFrequency::Frequency()\n  : FrequencyBase(0)\n{\n  // pass...\n}\n\nFrequency::Frequency(const Frequency &other)\n  : FrequencyBase(other)\n{\n  // pass...\n}\n\nFrequency &\nFrequency::operator =(const Frequency &other) {\n  FrequencyBase::operator=(other);\n  return *this;\n}\n\nFrequency\nFrequency::operator+(const FrequencyOffset &offset) const {\n  if ((0 > offset.inHz()) && (std::abs(offset.inHz()) > _frequency))\n    return Frequency();\n  return Frequency(_frequency + offset.inHz());\n}\n\nFrequencyOffset\nFrequency::operator-(const Frequency &other) const {\n  return FrequencyOffset(_frequency - other._frequency);\n}\n\nFrequency\nFrequency::fromString(const QString &freq) {\n  Frequency f;\n  if (! f.parse(freq)) {\n    logWarn() << \"Cannot parse frequency '\" << freq << \"'.\";\n  }\n  return f;\n}\n"
  },
  {
    "path": "lib/frequency.hh",
    "content": "#ifndef FREQUENCY_HH\n#define FREQUENCY_HH\n\n#include <yaml-cpp/yaml.h>\n#include <QString>\n#include <QMetaType>\n#include <QList>\n#include <QPair>\n\n\n/** Common base for all frequencies. That is frequencies (positive) and frequency offsets\n * (positive and negative). To this end, the type stores frequencies in Hz as a signed value. */\nstruct FrequencyBase\n{\npublic:\n  /** Frequency units. */\n  enum class Unit { Auto, Hz, kHz, MHz, GHz };\n\nprotected:\n  /** Hidden constructor from offset in Hz. */\n  FrequencyBase(qint64 hz) : _frequency(hz) {}\n\npublic:\n  /** Copy constructor. */\n  FrequencyBase(const FrequencyBase &other);\n\n  /** Assignment. */\n  FrequencyBase &operator = (const FrequencyBase &other);\n\n  /** Returns @c true of the frequency is negative. */\n  inline bool isNegative() const { return 0 > _frequency; }\n  /** Returns @c true of the frequency is positive. */\n  inline bool isPositive() const { return 0 < _frequency; }\n  /** Returns @c true of the frequency is zero. */\n  inline bool isZero() const { return 0 == _frequency; }\n\n  /** Format the frequency. */\n  QString format(Unit unit = Unit::Auto) const;\n  /** Parses a frequency. */\n  bool parse(const QString &value, Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive);\n\n  inline long long inHz() const { return _frequency; }             ///< Unit conversion.\n  inline double inkHz() const { return double(_frequency)/1e3; }   ///< Unit conversion.\n  inline double inMHz() const { return double(_frequency)/1e6; }   ///< Unit conversion.\n  inline double inGHz() const { return double(_frequency)/1e9; }   ///< Unit conversion.\n\n  /** Returns the most appropriate unit for the frequency value. */\n  Unit unit() const;\n\n  /** Returns unit as type base on string input */\n  Unit unitFromString(const QString& input) const;\n\n  /** Checks if frequency is a multiple of unit. */\n  bool isMultipleOf(Unit unit) const;\n\n  /** Helper for string conversion of unit. */\n  static QString unitName(Unit unit);\n\nprotected:\n  /** The actual frequency in Hz. */\n  qint64 _frequency;\n};\n\nstruct Frequency;\n\n/** Helper type to represent frequency differences aka offsets.\n * @ingroup utils */\nstruct FrequencyOffset: public FrequencyBase\n{\nfriend struct Frequency;\nprotected:\n  /** Hidden constructor. */\n  FrequencyOffset(qint64 Hz) : FrequencyBase(Hz) { }\n\npublic:\n  /** Default constructor. */\n  FrequencyOffset();\n  /** Copy constructor. */\n  FrequencyOffset(const FrequencyOffset &other);\n\n  /** Assignment. */\n  FrequencyOffset &operator = (const FrequencyOffset &other);\n\n  /** Invert an offset */\n  inline FrequencyOffset invert() const { return FrequencyOffset(this->_frequency * -1); }\n\n  /** Returns a positive frequency offset. */\n  inline FrequencyOffset abs() const {\n    return FrequencyOffset(std::abs(this->_frequency));\n  }\n\n  /** Parses an offset. */\n  static FrequencyOffset fromString(const QString &freq);\n\n  static inline FrequencyOffset fromHz(qint64 Hz)   { return FrequencyOffset(Hz); }             ///< Unit conversion.\n  static inline FrequencyOffset fromkHz(double kHz) { return FrequencyOffset(kHz*1e3); }      ///< Unit conversion.\n  static inline FrequencyOffset fromMHz(double MHz) { return FrequencyOffset(MHz*1e6); }      ///< Unit conversion.\n  static inline FrequencyOffset fromGHz(double GHz) { return FrequencyOffset(GHz*1e9); }      ///< Unit conversion.\n};\n\n\n\n/** Helper type to encode frequencies without any rounding error.\n * This is a specialization of @c FrequencyBase, enforcing a positive integer.\n * @ingroup utils */\nstruct Frequency: public FrequencyBase\n{\nprotected:\n  /** Hidden constructor from frequency in Hz. */\n  Frequency(quint64 hz);\n\npublic:\n  /** Default constructor. */\n  Frequency();\n  /** Copy constructor. */\n  Frequency(const Frequency &other);\n\n  /** Assignment. */\n  Frequency &operator =   (const Frequency &other);\n\n  inline bool operator<   (const Frequency &other) const {   ///< Comparison.\n    return _frequency < other._frequency;\n  }\n  inline bool operator <=(const Frequency &other) const {    ///< Comparison.\n    return _frequency <= other._frequency;\n  }\n  inline bool operator==  (const Frequency &other) const {   ///< Comparison.\n    return _frequency == other._frequency;\n  }\n  inline bool operator != (const Frequency &other) const {   ///< Comparison.\n    return _frequency != other._frequency;\n  }\n  inline bool operator>   (const Frequency &other) const {   ///< Comparison.\n    return _frequency > other._frequency;\n  }\n  inline bool operator >=(const Frequency &other) const {    ///< Comparison.\n    return _frequency >= other._frequency;\n  }\n\n  /** Frequency arithmetic. May result in an invalid frequency, if the negative offset is larger\n   * than the frequency. */\n  Frequency operator+(const FrequencyOffset &offset) const;\n  /** Frequency arithmetic. May result in an invalid frequency, if the negative offset is larger\n   * than the frequency. */\n  Frequency &operator+=(const FrequencyOffset &offset);\n  FrequencyOffset operator-(const Frequency &other) const;    ///< Frequency arithmetic.\n\n  /** Pareses a frequency. */\n  static Frequency fromString(const QString &freq);\n\n  inline quint64 inHz() const { return _frequency; }    ///< Unit conversion.\n\n  static inline Frequency fromHz(quint64 Hz)  { return Frequency(Hz); }      ///< Unit conversion.\n  static inline Frequency fromkHz(double kHz) { return Frequency(kHz*1e3); } ///< Unit conversion.\n  static inline Frequency fromMHz(double MHz) { return Frequency(MHz*1e6); } ///< Unit conversion.\n  static inline Frequency fromGHz(double GHz) { return Frequency(GHz*1e9); } ///< Unit conversion.\n\npublic:\n  /** Searches for the nearest frequency and returns an associated value. */\n  template <class T>\n  class MapNearest\n  {\n  protected:\n    typedef QList<QPair<Frequency, T>> container;\n    typedef typename container::const_iterator const_iterator;\n\n  public:\n    /** Constructs a nearest frequency mapping. */\n    MapNearest(const QList<QPair<Frequency, T>> &items)\n      : _items(items)\n    {\n      // Sort items ascending.\n      std::sort(_items.begin(), _items.end(), compItem);\n    }\n\n    /** Searches for the nearest item to the given frequency. */\n    const_iterator\n    find(const Frequency &f) const {\n      auto lower = std::lower_bound(_items.begin(), _items.end(), f, compItemValue);\n\n      if (_items.begin() == lower)\n        return lower;\n\n      auto upper = lower--;\n      if (_items.end() == upper)\n        return lower;\n\n      if ((f.inHz()-lower->first.inHz()) < (upper->first.inHz()-f.inHz()))\n        return lower;\n\n      return upper;\n    }\n\n    /** Returns the value associated with the nearest item for the given frequency. */\n    T value(const Frequency &f) const {\n      return find(f)->second;\n    }\n\n    /** Returns the frequency of the nearest item for the given frequency. */\n    Frequency key(const Frequency &f) const {\n      return find(f)->first;\n    }\n\n  protected:\n    /** Comparison of items by frequency. */\n    inline static bool compItem(const QPair<Frequency, T> &a, const QPair<Frequency, T> &b) {\n      return a.first < b.first;\n    }\n\n    /** Comparison of item and frequency. */\n    inline static bool compItemValue(const QPair<Frequency, T> &a, const Frequency &b) {\n      return a.first < b;\n    }\n\n  protected:\n    /** All items. */\n    container _items;\n  };\n};\n\n\nQ_DECLARE_METATYPE(Frequency)\n\n\nnamespace YAML\n{\n  /** Implements the conversion to and from YAML::Node. */\n  template<>\n  struct convert<Frequency>\n  {\n    /** Serializes the frequency. */\n    static Node encode(const Frequency& rhs) {\n      return Node(rhs.format().toStdString());\n    }\n\n    /** Parses the frequency. */\n    static bool decode(const Node& node, Frequency& rhs) {\n      if (! node.IsScalar())\n        return false;\n      return rhs.parse(QString::fromStdString(node.as<std::string>()), Qt::CaseSensitive);\n    }\n  };\n}\n\n#endif // FREQUENCY_HH\n"
  },
  {
    "path": "lib/gd73.cc",
    "content": "#include \"gd73.hh\"\n#include \"gd73_limits.hh\"\n\n#include \"logger.hh\"\n#include \"config.hh\"\n\n\n#define BSIZE           0x35\n\nRadioLimits * GD73::_limits = nullptr;\n\n\nGD73::GD73(GD73Interface *device, QObject *parent)\n  : Radio(parent), _name(\"Radioddity GD-73\"), _dev(device), _codeplugFlags(), _config(nullptr),\n    _codeplug()\n{\n  if (_dev)\n    _dev->setParent(this);\n}\n\nconst QString &\nGD73::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nGD73::limits() const {\n  if (nullptr == _limits)\n    _limits = new GD73Limits();\n  return *_limits;\n}\n\nconst Codeplug &\nGD73::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nGD73::codeplug() {\n  return _codeplug;\n}\n\nRadioInfo\nGD73::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::GD73, \"gd73\", \"GD-73\", \"Radioddity\", {GD73Interface::interfaceInfo()});\n}\n\nbool\nGD73::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  start();\n  return true;\n}\n\nbool\nGD73::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (! (_config = config))\n    return false;\n\n  _task = StatusUpload;\n  _codeplugFlags = flags;\n  if (flags.blocking()) {\n    this->run();\n    return (StatusIdle == _task);\n  }\n\n  _errorStack = err;\n  this->start();\n  return true;\n}\n\nbool\nGD73::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(selection);\n\n  errMsg(err) << \"Radio does not support a callsign DB.\";\n\n  return false;\n}\n\n\nbool\nGD73::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(flags);\n\n  errMsg(err) << \"Satellite config upload is not implemented yet.\";\n\n  return false;\n}\n\n\nvoid\nGD73::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    if (! download()) {\n      _dev->read_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit downloadFinished(this, &codeplug());\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! upload()) {\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n    _dev->write_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if(! uploadCallsigns()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit uploadComplete(this);\n  }\n}\n\nbool\nGD73::download() {\n  emit downloadStarted();\n\n  unsigned btot = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    btot += codeplug().image(0).element(n).data().size()/BSIZE;\n  }\n\n  if (! _dev->read_start(0,0,_errorStack))\n    return false;\n\n  unsigned bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    int b0 = codeplug().image(0).element(n).address()/BSIZE;\n    int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n    for (int i=0; i<nb; i++, bcount++) {\n      // read\n      if (! _dev->read(0, (b0+i)*BSIZE, codeplug().data((b0+i)*BSIZE), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot download codeplug.\";\n        return false;\n      }\n      emit downloadProgress(float(bcount*100)/btot);\n    }\n  }\n\n  _dev->read_finish(_errorStack);\n  return true;\n}\n\nbool\nGD73::upload() {\n  emit uploadStarted();\n\n  unsigned btot = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    btot += codeplug().image(0).element(n).data().size()/BSIZE;\n  }\n\n  unsigned bcount = 0;\n  if (_codeplugFlags.updateCodeplug()) {\n    if (! _dev->read_start(0,0,_errorStack))\n      return false;\n\n    // If codeplug gets updated, download codeplug from device first:\n    for (int n=0; n<codeplug().image(0).numElements(); n++) {\n      int b0 = codeplug().image(0).element(n).address()/BSIZE;\n      int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n      for (int i=0; i<nb; i++, bcount++) {\n        uint32_t addr = (b0+i)*BSIZE;\n        // read\n        if (! _dev->read(0, addr, codeplug().data(addr), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot upload codeplug.\";\n          return false;\n        }\n        emit uploadProgress(float(bcount*50)/btot);\n      }\n    }\n\n    _dev->read_finish(_errorStack);\n  }\n\n  // Encode config into codeplug\n  if (! codeplug().encode(_config, _codeplugFlags, _errorStack)) {\n    errMsg(_errorStack) << \"Codeplug upload failed.\";\n    return false;\n  }\n\n  if (! _dev->write_start(0, 0, _errorStack))\n    return false;\n\n  // then, upload modified codeplug\n  bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    int b0 = codeplug().image(0).element(n).address()/BSIZE;\n    int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n    for (int i=0; i<nb; i++, bcount++) {\n      // Select bank by addr\n      uint32_t addr = (b0+i)*BSIZE;\n      // write block\n      if (! _dev->write(0, addr, codeplug().data(addr), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot upload codeplug.\";\n        return false;\n      }\n      emit uploadProgress(50+float(bcount*50)/btot);\n    }\n  }\n\n  _dev->write_finish(_errorStack);\n\n  return true;\n}\n\nbool\nGD73::uploadCallsigns() {\n  return false;\n}\n\n"
  },
  {
    "path": "lib/gd73.hh",
    "content": "/** @defgroup gd73 Radioddity GD-73\n * Device specific classes for Radioddity GD-73A and GD-73E.\n *\n * \\image html gd73.webp \"GD-73\" width=200px\n * \\image latex gd73.webp \"GD-73\" width=200px\n *\n * @ingroup radioddity */\n\n#ifndef GD73_HH\n#define GD73_HH\n\n#include \"radio.hh\"\n#include \"gd73_interface.hh\"\n#include \"gd73_codeplug.hh\"\n\n\n\n/** Implements an USB interface to the Radioddity GD-73 UHF 2W DMR (Tier I&II) radio.\n *\n * @ingroup gd73 */\nclass GD73 : public Radio\n{\n  Q_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit GD73(GD73Interface *device=nullptr, QObject *parent=nullptr);\n\n  const QString &name() const;\n  const RadioLimits &limits() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, const Codeplug::Flags &flags = Codeplug::Flags(),\n                   const ErrorStack &err=ErrorStack());\n\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(),\n                             const ErrorStack &err=ErrorStack());\n\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err);\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n  void run();\n\nprivate:\n  virtual bool download();\n  virtual bool upload();\n  virtual bool uploadCallsigns();\n\nprotected:\n  /** The device identifier. */\n  QString _name;\n\n  /** The interface to the radio. */\n  GD73Interface *_dev;\n  /** Holds the flags to control assembly and upload of code-plugs. */\n  Codeplug::Flags _codeplugFlags;\n  /** The generic configuration. */\n  Config *_config;\n\n  /** The codeplug. */\n  GD73Codeplug _codeplug;\n\nprivate:\n  /** Holds the singleton instance of the radio limits for this radio. */\n  static RadioLimits *_limits;\n};\n\n#endif // GD77_HH\n"
  },
  {
    "path": "lib/gd73_codeplug.cc",
    "content": "#include \"gd73_codeplug.hh\"\n#include \"config.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"logger.hh\"\n\n\nQVector<SelectiveCall> _ctcss_codes = {\n  SelectiveCall(62.5),  SelectiveCall(67.0),  SelectiveCall(69.3),  SelectiveCall(71.9),\n  SelectiveCall(74.4),  SelectiveCall(77.0),  SelectiveCall(79.7),  SelectiveCall(82.5),\n  SelectiveCall(85.4),  SelectiveCall(88.5),  SelectiveCall(91.5),  SelectiveCall(94.8),\n  SelectiveCall(97.4), SelectiveCall(100.0), SelectiveCall(103.5), SelectiveCall(107.2),\n SelectiveCall(110.9), SelectiveCall(114.8), SelectiveCall(118.8), SelectiveCall(123.0),\n SelectiveCall(127.3), SelectiveCall(131.8), SelectiveCall(136.5), SelectiveCall(141.3),\n SelectiveCall(146.2), SelectiveCall(151.4), SelectiveCall(156.7), SelectiveCall(159.8),\n SelectiveCall(162.2), SelectiveCall(165.5), SelectiveCall(167.9), SelectiveCall(171.3),\n SelectiveCall(173.8), SelectiveCall(177.3), SelectiveCall(179.9), SelectiveCall(183.5),\n SelectiveCall(186.2), SelectiveCall(189.9), SelectiveCall(192.8), SelectiveCall(196.6),\n SelectiveCall(199.5), SelectiveCall(203.5), SelectiveCall(206.5), SelectiveCall(210.7),\n SelectiveCall(218.1), SelectiveCall(225.7), SelectiveCall(229.1), SelectiveCall(233.6),\n SelectiveCall(241.8), SelectiveCall(250.3), SelectiveCall(254.1)\n};\n\nQVector<unsigned int> _dcs_codes = {\n  23,  25,  26,  31,  32,  36,  43,  47,  51,  53,  54,  65,  71,  72,  73,  74,\n 114, 115, 116, 122, 125, 131, 132, 134, 143, 145, 152, 155, 156, 162, 165, 172,\n 174, 205, 212, 223, 225, 226, 243, 244, 245, 246, 251, 252, 255, 261, 263, 265,\n 266, 271, 274, 306, 311, 315, 325, 331, 332, 343, 346, 351, 356, 364, 365, 371,\n 411, 412, 413, 423, 431, 432, 445, 446, 452, 454, 455, 462, 464, 465, 466, 503,\n 506, 516, 523, 526, 532, 546, 565, 606, 612, 624, 627, 631, 632, 645, 654, 662,\n 703, 712, 723, 731, 732, 734, 743, 754\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::InformationElement\n * ********************************************************************************************* */\nGD73Codeplug::InformationElement::InformationElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::InformationElement::InformationElement(uint8_t *ptr)\n  : Element(ptr, InformationElement::size())\n{\n  // pass...\n}\n\nFrequencyRange\nGD73Codeplug::InformationElement::frequencyRange() const {\n  switch (getUInt8(Offset::frequencyRange())) {\n  case 0x00: return FrequencyRange {Frequency::fromMHz(406.1), Frequency::fromMHz(470.0)}; break;\n  case 0x01: return FrequencyRange {Frequency::fromMHz(446.0), Frequency::fromMHz(446.995)}; break;\n  case 0x02: return FrequencyRange {Frequency::fromMHz(400.0), Frequency::fromMHz(470.0)}; break;\n  }\n  return FrequencyRange();\n}\n\nvoid\nGD73Codeplug::InformationElement::setFrequencyRange(const FrequencyRange &range) {\n  if ((Frequency::fromMHz(446.0)<=range.lower) && (range.upper <=Frequency::fromMHz(446.995)))\n    setUInt8(Offset::frequencyRange(), 0x01);\n  else if ((range.lower>=Frequency::fromMHz(406.1)) && (range.upper <=Frequency::fromMHz(470.0)))\n    setUInt8(Offset::frequencyRange(), 0x00);\n  else\n    setUInt8(Offset::frequencyRange(), 0x02);\n}\n\n\nQDateTime\nGD73Codeplug::InformationElement::timestamp() const {\n  int year = ((int)getUInt8(Offset::dateCentury()))*100 +\n      getUInt8(Offset::dateYear());\n  return QDateTime(QDate(year, getUInt8(Offset::dateMonth()), getUInt8(Offset::dateDay())),\n                   QTime(getUInt8(Offset::dateHour()), getUInt8(Offset::dateMinute()),\n                         getUInt8(Offset::dateSecond())));\n}\n\nvoid\nGD73Codeplug::InformationElement::setTimestamp(const QDateTime &timestamp) {\n  setUInt8(Offset::dateCentury(), timestamp.date().year()/100);\n  setUInt8(Offset::dateYear(), timestamp.date().year()%100);\n  setUInt8(Offset::dateMonth(), timestamp.date().month());\n  setUInt8(Offset::dateDay(), timestamp.date().day());\n  setUInt8(Offset::dateHour(), timestamp.time().hour());\n  setUInt8(Offset::dateMinute(), timestamp.time().minute());\n  setUInt8(Offset::dateSecond(), timestamp.time().second());\n}\n\nQString\nGD73Codeplug::InformationElement::serial() const {\n  return readASCII(Offset::serial(), Limit::serial(), 0x00);\n}\n\nQString\nGD73Codeplug::InformationElement::modelName() const {\n  return readASCII(Offset::modelName(), Limit::modelName(), 0x00);\n}\n\nQString\nGD73Codeplug::InformationElement::deviceID() const {\n  return readASCII(Offset::deviceID(), Limit::deviceID(), 0x00);\n}\n\nQString\nGD73Codeplug::InformationElement::modelNumber() const {\n  return readASCII(Offset::modelNumber(), Limit::modelNumber(), 0x00);\n}\n\nQString\nGD73Codeplug::InformationElement::softwareVersion() const {\n  return readASCII(Offset::softwareVersion(), Limit::softwareVersion(), 0x00);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::SettingsElement::KeyFunction\n * ********************************************************************************************* */\nuint8_t\nGD73Codeplug::SettingsElement::KeyFunction::encode(RadioddityButtonSettingsExtension::Function func) {\n  switch (func) {\n  case RadioddityButtonSettingsExtension::Function::None: return None;\n  case RadioddityButtonSettingsExtension::Function::RadioEnable: return RadioEnable;\n  case RadioddityButtonSettingsExtension::Function::RadioCheck: return RadioCheck;\n  case RadioddityButtonSettingsExtension::Function::RadioDisable: return RadioDisable;\n  case RadioddityButtonSettingsExtension::Function::PowerLevel: return PowerLevel;\n  case RadioddityButtonSettingsExtension::Function::ToggleMonitor: return Monitor;\n  case RadioddityButtonSettingsExtension::Function::EmergencyOn: return EmergencyOn;\n  case RadioddityButtonSettingsExtension::Function::EmergencyOff: return EmergencyOff;\n  case RadioddityButtonSettingsExtension::Function::ZoneSelect: return ZoneSwitch;\n  case RadioddityButtonSettingsExtension::Function::ToggleScan: return ToggleScan;\n  case RadioddityButtonSettingsExtension::Function::ToggleVox: return ToggleVOX;\n  case RadioddityButtonSettingsExtension::Function::OneTouch1: return OneTouch1;\n  case RadioddityButtonSettingsExtension::Function::OneTouch2: return OneTouch2;\n  case RadioddityButtonSettingsExtension::Function::OneTouch3: return OneTouch3;\n  case RadioddityButtonSettingsExtension::Function::OneTouch4: return OneTouch4;\n  case RadioddityButtonSettingsExtension::Function::OneTouch5: return OneTouch5;\n  case RadioddityButtonSettingsExtension::Function::ToggleTalkaround: return ToggleTalkaround;\n  case RadioddityButtonSettingsExtension::Function::ToggleLoneWorker: return LoneWorker;\n  case RadioddityButtonSettingsExtension::Function::TBST: return TBST;\n  case RadioddityButtonSettingsExtension::Function::CallSwell: return CallSwell;\n  default: break;\n  }\n  return None;\n}\n\nRadioddityButtonSettingsExtension::Function\nGD73Codeplug::SettingsElement::KeyFunction::decode(uint8_t code) {\n  switch((Code) code) {\n  case None: return RadioddityButtonSettingsExtension::Function::None;\n  case RadioEnable: return RadioddityButtonSettingsExtension::Function::RadioEnable;\n  case RadioCheck: return RadioddityButtonSettingsExtension::Function::RadioCheck;\n  case RadioDisable: return RadioddityButtonSettingsExtension::Function::RadioDisable;\n  case PowerLevel: return RadioddityButtonSettingsExtension::Function::PowerLevel;\n  case Monitor: return RadioddityButtonSettingsExtension::Function::ToggleMonitor;\n  case EmergencyOn: return RadioddityButtonSettingsExtension::Function::EmergencyOn;\n  case EmergencyOff: return RadioddityButtonSettingsExtension::Function::EmergencyOff;\n  case ZoneSwitch: return RadioddityButtonSettingsExtension::Function::ZoneSelect;\n  case ToggleScan: return RadioddityButtonSettingsExtension::Function::ToggleScan;\n  case ToggleVOX: return RadioddityButtonSettingsExtension::Function::ToggleVox;\n  case OneTouch1: return RadioddityButtonSettingsExtension::Function::OneTouch1;\n  case OneTouch2: return RadioddityButtonSettingsExtension::Function::OneTouch2;\n  case OneTouch3: return RadioddityButtonSettingsExtension::Function::OneTouch3;\n  case OneTouch4: return RadioddityButtonSettingsExtension::Function::OneTouch4;\n  case OneTouch5: return RadioddityButtonSettingsExtension::Function::OneTouch5;\n  case ToggleTalkaround: return RadioddityButtonSettingsExtension::Function::ToggleTalkaround;\n  case LoneWorker: return RadioddityButtonSettingsExtension::Function::ToggleLoneWorker;\n  case TBST: return RadioddityButtonSettingsExtension::Function::TBST;\n  case CallSwell: return RadioddityButtonSettingsExtension::Function::CallSwell;\n  default: break;\n  }\n  return RadioddityButtonSettingsExtension::Function::None;\n}\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::SettingsElement\n * ********************************************************************************************* */\nGD73Codeplug::SettingsElement::SettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::SettingsElement::SettingsElement(uint8_t *ptr)\n  : Element(ptr, SettingsElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::SettingsElement::name() const {\n  return readUnicode(Offset::name(), Limit::name(), 0x0000);\n}\nvoid\nGD73Codeplug::SettingsElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::name(), 0x0000);\n}\n\nunsigned int\nGD73Codeplug::SettingsElement::dmrID() const {\n  return getUInt32_le(Offset::dmrId());\n}\nvoid\nGD73Codeplug::SettingsElement::setDMRID(unsigned int id) {\n  setUInt32_le(Offset::dmrId(), id);\n}\n\nGD73Codeplug::SettingsElement::Language\nGD73Codeplug::SettingsElement::language() const {\n  return (Language)getUInt8(Offset::language());\n}\nvoid\nGD73Codeplug::SettingsElement::setLanguage(Language lang) {\n  setUInt8(Offset::language(), (unsigned int)lang);\n}\n\nLevel\nGD73Codeplug::SettingsElement::vox() const {\n  return Level::fromValue(getUInt8(Offset::voxLevel()), Limit::vox());\n}\nvoid\nGD73Codeplug::SettingsElement::setVOX(Level level) {\n  setUInt8(Offset::voxLevel(), level.mapTo(Limit::vox()));\n}\n\nLevel\nGD73Codeplug::SettingsElement::squelch() const {\n  return Level::fromValue(getUInt8(Offset::squelchLevel()), Limit::squelch());\n}\nvoid\nGD73Codeplug::SettingsElement::setSquelch(Level level) {\n  setUInt8(Offset::squelchLevel(), level.mapTo(Limit::squelch()));\n}\n\nbool\nGD73Codeplug::SettingsElement::totIsSet() const {\n  return 0 != getUInt8(Offset::tot());\n}\nInterval\nGD73Codeplug::SettingsElement::tot() const  {\n  unsigned int n = getUInt8(Offset::tot());\n  if (0 == n)\n    return Interval::fromSeconds(0);\n  return Interval::fromSeconds(n*10+20);\n}\nvoid\nGD73Codeplug::SettingsElement::setTOT(const Interval &interval) {\n  Interval intv = Limit::tot().map(interval);\n  if (intv.seconds()<20)\n    setUInt8(Offset::tot(), 0);\n  setUInt8(Offset::tot(), (intv.seconds()-20)/10);\n}\nvoid\nGD73Codeplug::SettingsElement::clearTOT() {\n  setUInt8(Offset::tot(), 0);\n}\n\nbool\nGD73Codeplug::SettingsElement::txInterruptedEnabled() const {\n  return 0 != getUInt8(Offset::txInterrupt());\n}\nvoid\nGD73Codeplug::SettingsElement::enableTXInterrupt(bool enable) {\n  setUInt8(Offset::txInterrupt(), enable ? 0x01 : 0x00);\n}\n\nbool\nGD73Codeplug::SettingsElement::powerSaveEnabled() const {\n  return 0x00 != getUInt8(Offset::powerSave());\n}\nvoid\nGD73Codeplug::SettingsElement::enablePowerSave(bool enable) {\n  setUInt8(Offset::powerSave(), enable ? 0x01 : 0x00);\n}\nInterval\nGD73Codeplug::SettingsElement::powerSaveTimeout() const {\n  return Interval::fromSeconds(getUInt8(Offset::powerSaveTimeout()));\n}\nvoid\nGD73Codeplug::SettingsElement::setPowerSaveTimeout(const Interval &interval) {\n  Interval intv = Limit::powerSaveTimeout().map(interval);\n  setUInt8(Offset::powerSaveTimeout(), intv.seconds());\n}\n\nbool\nGD73Codeplug::SettingsElement::readLockEnabled() const {\n  return 0x00 != getUInt8(Offset::readLockEnable());\n}\nvoid\nGD73Codeplug::SettingsElement::enableReadLock(bool enable) {\n  setUInt8(Offset::readLockEnable(), enable ? 0x01 : 0x00);\n}\nQString\nGD73Codeplug::SettingsElement::readLockPin() const {\n  return readASCII(Offset::readLockPin(), Limit::pin(), 0x00);\n}\nvoid\nGD73Codeplug::SettingsElement::setReadLockPin(const QString &pin) {\n  writeASCII(Offset::readLockPin(), pin, Limit::pin(), 0x00);\n}\n\nbool\nGD73Codeplug::SettingsElement::writeLockEnabled() const {\n  return 0x00 != getUInt8(Offset::writeLockEnable());\n}\nvoid\nGD73Codeplug::SettingsElement::enableWriteLock(bool enable) {\n  setUInt8(Offset::writeLockEnable(), enable ? 0x01 : 0x00);\n}\nQString\nGD73Codeplug::SettingsElement::writeLockPin() const {\n  return readASCII(Offset::writeLockPin(), Limit::pin(), 0x00);\n}\nvoid\nGD73Codeplug::SettingsElement::setWriteLockPin(const QString &pin) {\n  writeASCII(Offset::writeLockPin(), pin, Limit::pin(), 0x00);\n}\n\nGD73Codeplug::SettingsElement::ChannelDisplayMode\nGD73Codeplug::SettingsElement::channelDisplayMode() const {\n  return (ChannelDisplayMode)getUInt8(Offset::channelDisplayMode());\n}\nvoid\nGD73Codeplug::SettingsElement::setChannelDisplayMode(ChannelDisplayMode mode) {\n  setUInt8(Offset::channelDisplayMode(), (unsigned int)mode);\n}\n\nLevel\nGD73Codeplug::SettingsElement::dmrMicGain() const {\n  return Level::fromValue(getUInt8(Offset::dmrMicGain())+1, Limit::micGain());\n}\nvoid\nGD73Codeplug::SettingsElement::setDMRMicGain(Level gain) {\n  setUInt8(Offset::dmrMicGain(), gain.mapTo(Limit::micGain())-1);\n}\n\nLevel\nGD73Codeplug::SettingsElement::fmMicGain() const {\n  return Level::fromValue(getUInt8(Offset::fmMicGain())+1, Limit::micGain());\n}\nvoid\nGD73Codeplug::SettingsElement::setFMMicGain(Level gain) {\n  setUInt8(Offset::fmMicGain(), gain.mapTo(Limit::micGain())-1);\n}\n\nInterval\nGD73Codeplug::SettingsElement::loneWorkerResponseTimeout() const {\n  return Interval::fromMinutes(getUInt16_le(Offset::loneWorkerResponseTimeout()));\n}\nvoid\nGD73Codeplug::SettingsElement::setLoneWorkerResponseTimeout(const Interval &interval) {\n  Interval intv = Limit::loneWorkerResponse().map(interval);\n  setUInt16_le(Offset::loneWorkerResponseTimeout(), intv.minutes());\n}\nInterval\nGD73Codeplug::SettingsElement::loneWorkerRemindPeriod() const {\n  return Interval::fromSeconds(getUInt16_le(Offset::loneWorkerReminderPeriod()));\n}\nvoid\nGD73Codeplug::SettingsElement::setLoneWorkerRemindPeriod(const Interval &interval) {\n  Interval intv = Limit::loneWorkerRemindPeriod().map(interval);\n  setUInt16_le(Offset::loneWorkerReminderPeriod(), intv.seconds());\n}\n\n\nBootSettings::BootDisplay\nGD73Codeplug::SettingsElement::bootDisplayMode() const {\n  switch ((BootDisplayMode)getUInt8(Offset::bootDisplayMode())) {\n    case BootDisplayMode::Off: return BootSettings::BootDisplay::Logo;\n    case BootDisplayMode::Text: return BootSettings::BootDisplay::Text;\n    case BootDisplayMode::Both:\n    case BootDisplayMode::Image: return BootSettings::BootDisplay::Image;\n  }\n  return BootSettings::BootDisplay::Logo;\n}\n\nvoid\nGD73Codeplug::SettingsElement::setBootDisplayMode(BootSettings::BootDisplay mode) {\n  switch (mode) {\n  case BootSettings::BootDisplay::Logo: setUInt8(Offset::bootDisplayMode(), (unsigned int)BootDisplayMode::Off); break;\n  case BootSettings::BootDisplay::Text: setUInt8(Offset::bootDisplayMode(), (unsigned int)BootDisplayMode::Text); break;\n  case BootSettings::BootDisplay::Image: setUInt8(Offset::bootDisplayMode(), (unsigned int)BootDisplayMode::Image); break;\n  }\n}\n\n\nQString\nGD73Codeplug::SettingsElement::bootTextLine1() const {\n  return readUnicode(Offset::bootTextLine1(), Limit::bootTextLine(), 0x0000);\n}\nvoid\nGD73Codeplug::SettingsElement::setBootTextLine1(const QString &line) {\n  writeUnicode(Offset::bootTextLine1(), line, Limit::bootTextLine(), 0x0000);\n}\nQString\nGD73Codeplug::SettingsElement::bootTextLine2() const {\n  return readUnicode(Offset::bootTextLine2(), Limit::bootTextLine(), 0x0000);\n}\nvoid\nGD73Codeplug::SettingsElement::setBootTextLine2(const QString &line) {\n  writeUnicode(Offset::bootTextLine2(), line, Limit::bootTextLine(), 0x0000);\n}\n\nbool\nGD73Codeplug::SettingsElement::keyToneEnabled() const {\n  return 0x00 != getUInt8(Offset::keyToneEnable());\n}\n\nvoid\nGD73Codeplug::SettingsElement::enableKeyTone(bool enable) {\n  setUInt8(Offset::keyToneEnable(), enable ? 0x01 : 0x00);\n}\n\nLevel\nGD73Codeplug::SettingsElement::keyToneVolume() const {\n  return Level::fromValue(getUInt8(Offset::keyToneVolume()), Limit::toneVolume());\n}\nvoid\nGD73Codeplug::SettingsElement::setKeyToneVolume(Level vol) {\n  setUInt8(Offset::keyToneVolume(), vol.mapTo(Limit::toneVolume()));\n}\n\nbool\nGD73Codeplug::SettingsElement::lowBatteryToneEnabled() const {\n  return 0x00 != getUInt8(Offset::lowBatToneEnable());\n}\nvoid\nGD73Codeplug::SettingsElement::enableLowBatteryTone(bool enable) {\n  setUInt8(Offset::lowBatToneEnable(), enable ? 0x01 : 0x00);\n}\nunsigned int\nGD73Codeplug::SettingsElement::lowBatteryToneVolume() const {\n  return getUInt8(Offset::lowBatToneVolume());\n}\nvoid\nGD73Codeplug::SettingsElement::setLowBatteryToneVolume(unsigned int vol) {\n  setUInt8(Offset::lowBatToneVolume(), Level::fromValue(vol).mapTo(Limit::toneVolume()));\n}\n\nInterval\nGD73Codeplug::SettingsElement::longPressDuration() const {\n  return Interval::fromMilliseconds(500*((unsigned int)getUInt8(Offset::longPressDuration())));\n}\nvoid\nGD73Codeplug::SettingsElement::setLongPressDuration(const Interval &interval) {\n  Interval intv = Limit::longPressDuration().map(interval);\n  setUInt8(Offset::longPressDuration(), intv.milliseconds()/500);\n}\nRadioddityButtonSettingsExtension::Function\nGD73Codeplug::SettingsElement::keyFunctionLongPressP1() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1LongPress()));\n}\nvoid\nGD73Codeplug::SettingsElement::setKeyFunctionLongPressP1(RadioddityButtonSettingsExtension::Function function) {\n  setUInt8(Offset::progFuncKey1LongPress(), KeyFunction::encode(function));\n}\nRadioddityButtonSettingsExtension::Function\nGD73Codeplug::SettingsElement::keyFunctionShortPressP1() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey1ShortPress()));\n}\nvoid\nGD73Codeplug::SettingsElement::setKeyFunctionShortPressP1(RadioddityButtonSettingsExtension::Function function) {\n  setUInt8(Offset::progFuncKey1ShortPress(), KeyFunction::encode(function));\n}\nRadioddityButtonSettingsExtension::Function\nGD73Codeplug::SettingsElement::keyFunctionLongPressP2() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2LongPress()));\n}\nvoid\nGD73Codeplug::SettingsElement::setKeyFunctionLongPressP2(RadioddityButtonSettingsExtension::Function function) {\n  setUInt8(Offset::progFuncKey2LongPress(), KeyFunction::encode(function));\n}\nRadioddityButtonSettingsExtension::Function\nGD73Codeplug::SettingsElement::keyFunctionShortPressP2() const {\n  return KeyFunction::decode(getUInt8(Offset::progFuncKey2ShortPress()));\n}\nvoid\nGD73Codeplug::SettingsElement::setKeyFunctionShortPressP2(RadioddityButtonSettingsExtension::Function function) {\n  setUInt8(Offset::progFuncKey2ShortPress(), KeyFunction::encode(function));\n}\nGD73Codeplug::OneTouchSettingElement\nGD73Codeplug::SettingsElement::oneTouch(unsigned int n) {\n  return OneTouchSettingElement(\n        _data+Offset::oneTouchSettings() + n*Offset::betweenOneTouchSettings());\n}\n\nbool\nGD73Codeplug::SettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Create radio ID\n  DMRRadioID *radioID = new DMRRadioID(name(), dmrID());\n  ctx.config()->radioIDs()->add(radioID);\n  ctx.config()->settings()->setDefaultId(radioID);\n  ctx.add(radioID, 0);\n\n  // Apply settings\n  ctx.config()->settings()->boot()->setMessage1(bootTextLine1());\n  ctx.config()->settings()->boot()->setMessage2(bootTextLine2());\n  // Audio settings\n  ctx.config()->settings()->audio()->setMicGain(dmrMicGain());\n  if (dmrMicGain() == fmMicGain())\n    ctx.config()->settings()->audio()->disableFMMicGain();\n  else\n    ctx.config()->settings()->audio()->setFMMicGain(fmMicGain());\n  ctx.config()->settings()->audio()->setSquelch(squelch());\n  ctx.config()->settings()->audio()->setVox(vox());\n\n  if (! totIsSet())\n    ctx.config()->settings()->disableTOT();\n  else\n    ctx.config()->settings()->setTOT(tot());\n\n  ctx.config()->settings()->boot()->setBootDisplay(bootDisplayMode());\n  ctx.config()->settings()->boot()->enableBootPassword(readLockEnabled());\n  ctx.config()->settings()->boot()->setBootPassword(readLockPin());\n\n  if (! keyToneEnabled())\n    ctx.config()->settings()->tone()->disableKeyTone();\n  else\n    ctx.config()->settings()->tone()->setKeyToneVolume(keyToneVolume());\n\n  // Get/add radioddity settings extension\n  RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension();\n  if (nullptr == ext)\n    ctx.config()->settings()->setRadioddityExtension(ext = new RadiodditySettingsExtension());\n\n  ext->setLoneWorkerResponseTime(loneWorkerResponseTimeout());\n  ext->setLoneWorkerReminderPeriod(loneWorkerRemindPeriod());\n\n  ext->enableTXInterrupt(txInterruptedEnabled());\n  switch(language()) {\n  case Language::Chinese: ext->setLanguage(RadiodditySettingsExtension::Language::Chinese); break;\n  case Language::English: ext->setLanguage(RadiodditySettingsExtension::Language::English); break;\n  }\n\n  ext->enablePowerSaveMode(powerSaveEnabled());\n  ext->setPowerSaveDelay(powerSaveTimeout());\n\n  ext->buttons()->setLongPressDuration(longPressDuration());\n  ext->buttons()->setFuncKey1Short(keyFunctionShortPressP1());\n  ext->buttons()->setFuncKey1Long(keyFunctionLongPressP1());\n  ext->buttons()->setFuncKey2Short(keyFunctionShortPressP2());\n  ext->buttons()->setFuncKey2Long(keyFunctionLongPressP2());\n\n  ext->tone()->enableLowBatteryWarn(lowBatteryToneEnabled());\n  ext->tone()->setLowBatteryWarnVolume(lowBatteryToneVolume());\n\n  if (writeLockEnabled())\n    ext->boot()->setProgPassword(writeLockPin());\n  else\n    ext->boot()->setProgPassword(\"\");\n\n  return true;\n}\n\nbool\nGD73Codeplug::SettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Get default radio ID\n  if (nullptr == ctx.config()->settings()->defaultId()) {\n    errMsg(err) << \"Cannot encode default radio DMR ID, none is set.\";\n    return false;\n  }\n\n  setDMRID(ctx.config()->settings()->defaultId()->number());\n  setName(ctx.config()->settings()->defaultId()->name());\n\n  // Apply settings\n  setBootTextLine1(ctx.config()->settings()->boot()->message1());\n  setBootTextLine2(ctx.config()->settings()->boot()->message2());\n\n  setDMRMicGain(ctx.config()->settings()->audio()->micGain());\n  setFMMicGain(ctx.config()->settings()->audio()->micGain());\n  if (ctx.config()->settings()->audio()->fmMicGainEnabled())\n    setFMMicGain(ctx.config()->settings()->audio()->fmMicGain());\n  setSquelch(ctx.config()->settings()->audio()->squelch());\n  setVOX(ctx.config()->settings()->audio()->vox());\n\n  enableKeyTone(ctx.config()->settings()->tone()->keyToneEnabled());\n  if (ctx.config()->settings()->tone()->keyToneEnabled())\n    setKeyToneVolume(ctx.config()->settings()->tone()->keyToneVolume());\n\n  if (ctx.config()->settings()->totDisabled())\n    clearTOT();\n  else\n    setTOT(ctx.config()->settings()->tot());\n\n  setLanguage(Language::English);\n  setUInt8(0x003c, 0x01);\n  setUInt8(0x003e, 0x01);\n\n  setBootDisplayMode(ctx.config()->settings()->boot()->bootDisplay());\n  enableReadLock(ctx.config()->settings()->boot()->bootPasswordEnabled());\n  setReadLockPin(ctx.config()->settings()->boot()->bootPassword());\n\n  // Get/add radioddity settings extension\n  RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension();\n  if (nullptr == ext)\n    return true;\n\n  setLoneWorkerResponseTimeout(ext->loneWorkerResponseTime());\n  setLoneWorkerRemindPeriod(ext->loneWorkerReminderPeriod());\n\n  enableTXInterrupt(ext->txInterrupt());\n  switch(ext->language()) {\n  case RadiodditySettingsExtension::Language::Chinese: setLanguage(Language::Chinese); break;\n  case RadiodditySettingsExtension::Language::English: setLanguage(Language::English); break;\n  }\n\n  enablePowerSave(ext->powerSaveMode());\n  setPowerSaveTimeout(ext->powerSaveDelay());\n\n  setLongPressDuration(ext->buttons()->longPressDuration());\n  setKeyFunctionShortPressP1(ext->buttons()->funcKey1Short());\n  setKeyFunctionLongPressP1(ext->buttons()->funcKey1Long());\n  setKeyFunctionShortPressP2(ext->buttons()->funcKey2Short());\n  setKeyFunctionLongPressP2(ext->buttons()->funcKey2Long());\n\n  enableLowBatteryTone(ext->tone()->lowBatteryWarn());\n  setLowBatteryToneVolume(ext->tone()->lowBatteryWarnVolume());\n\n  if (! ext->boot()->progPassword().isEmpty()) {\n    setWriteLockPin(ext->boot()->progPassword());\n    enableWriteLock(true);\n  } else {\n    enableWriteLock(false);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::OneTouchSettingElement\n * ********************************************************************************************* */\nGD73Codeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr)\n  : Element(ptr, OneTouchSettingElement::size())\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::DMRSettingsElement\n * ********************************************************************************************* */\nGD73Codeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::DMRSettingsElement::DMRSettingsElement(uint8_t *ptr)\n  : Element(ptr, DMRSettingsElement::size())\n{\n  // pass...\n}\n\nInterval\nGD73Codeplug::DMRSettingsElement::callHangTime() const {\n  return Interval::fromSeconds(getUInt8(Offset::callHangTime())+1);\n}\nvoid\nGD73Codeplug::DMRSettingsElement::setCallHangTime(const Interval &intv) {\n  setUInt8(Offset::callHangTime(), Limit::callHangTime().map(intv).seconds()-1);\n}\nInterval\nGD73Codeplug::DMRSettingsElement::activeWaitTime() const {\n  return Interval::fromMilliseconds(120 + ((unsigned int)getUInt8(Offset::activeWaitTime()))*5);\n}\nvoid\nGD73Codeplug::DMRSettingsElement::setActiveWaitTime(const Interval &intv) {\n  setUInt8(Offset::activeWaitTime(), (Limit::activeWaitTime().map(intv).milliseconds()-120)/5);\n}\n\nunsigned int\nGD73Codeplug::DMRSettingsElement::activeRetries() const {\n  return getUInt8(Offset::activeRetries());\n}\nvoid\nGD73Codeplug::DMRSettingsElement::setActiveRetries(unsigned int count) {\n  setUInt8(Offset::activeRetries(), Limit::activeRetires().map(count));\n}\n\nunsigned int\nGD73Codeplug::DMRSettingsElement::txPreambles() const {\n  return getUInt8(Offset::txPreambles());\n}\nvoid\nGD73Codeplug::DMRSettingsElement::setTXPreambles(unsigned int count) {\n  setUInt8(Offset::txPreambles(), Limit::txPreambles().map(count));\n}\n\nbool\nGD73Codeplug::DMRSettingsElement::decodeDisableRadioEnabled() const {\n  return 0x00 != getUInt8(Offset::decodeDisableRadio());\n}\nvoid\nGD73Codeplug::DMRSettingsElement::enableDecodeDisableRadio(bool enable) {\n  setUInt8(Offset::decodeDisableRadio(), enable ? 0x01 : 0x00);\n}\nbool\nGD73Codeplug::DMRSettingsElement::decodeRadioCheckEnabled() const {\n  return 0x00 != getUInt8(Offset::decodeCheckRadio());\n}\nvoid\nGD73Codeplug::DMRSettingsElement::enableDecodeRadioCheck(bool enable) {\n  setUInt8(Offset::decodeCheckRadio(), enable ? 0x01 : 0x00);\n}\nbool\nGD73Codeplug::DMRSettingsElement::decodeEnableRadioEnabled() const {\n  return 0x00 != getUInt8(Offset::decodeEnableRadio());\n}\nvoid\nGD73Codeplug::DMRSettingsElement::enableDecodeEnableRadio(bool enable) {\n  setUInt8(Offset::decodeEnableRadio(), enable ? 0x01 : 0x00);\n}\n\nbool\nGD73Codeplug::DMRSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(callHangTime());\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(callHangTime());\n\n  return true;\n}\n\nbool\nGD73Codeplug::DMRSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setCallHangTime(std::max(ctx.config()->settings()->dmr()->privateCallHangTime(),\n                           ctx.config()->settings()->dmr()->groupCallHangTime()));\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::EncryptionKeyElement\n * ********************************************************************************************* */\nGD73Codeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::EncryptionKeyElement::EncryptionKeyElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nGD73Codeplug::EncryptionKeyElement::clear() {\n  setKeySize(0);\n}\n\nbool\nGD73Codeplug::EncryptionKeyElement::isValid() const {\n  return Element::isValid() && (0 != keySize());\n}\n\nunsigned int\nGD73Codeplug::EncryptionKeyElement::keySize() const {\n  return getUInt8(Offset::size())*4;\n}\nvoid\nGD73Codeplug::EncryptionKeyElement::setKeySize(unsigned int size) {\n  setUInt8(Offset::size(), size/4);\n}\n\nBasicEncryptionKey *\nGD73Codeplug::EncryptionKeyElement::createEncryptionKey(const ErrorStack &err) const {\n  if (! isValid())\n    return nullptr;\n\n  BasicEncryptionKey *key = new BasicEncryptionKey();\n  if (! key->setKey(QByteArray((const char*)_data+Offset::key(), keySize()/8))) {\n    errMsg(err) << \"Cannot decode encryption key of size \" << keySize() << \".\";\n    delete key;\n    return nullptr;\n  }\n\n  return key;\n}\n\nbool\nGD73Codeplug::EncryptionKeyElement::encodeEncryptionKey(BasicEncryptionKey *key, const ErrorStack &err) {\n  unsigned int size = key->key().size()*8;\n  if (size > 32) {\n    errMsg(err) << \"Key size of \" << size << \" exceeds 32bit.\";\n    return false;\n  }\n\n  setKeySize(size);\n  memcpy(_data+Offset::key(), key->key().constData(), key->key().size());\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::EncryptionKeyBankElement\n * ********************************************************************************************* */\nGD73Codeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::EncryptionKeyBankElement::EncryptionKeyBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::EncryptionKeyBankElement::createEncryptionKeys(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::keys(); i++) {\n    EncryptionKeyElement keyElement(_data + Offset::keys() + i*Offset::betweenKeys());\n    if (! keyElement.isValid())\n      continue;\n    EncryptionKey *key = keyElement.createEncryptionKey(err);\n    if (nullptr == key) {\n      errMsg(err) << \"Cannot decode \" <<i+1<<\"-th encryption key. Skip.\";\n      continue;\n    }\n    key->setName(QString(\"Basic Key %1\").arg(i+1));\n    ctx.add(key, i);\n\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::EncryptionKeyBankElement::encodeEncryptionKeys(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::keys(); i++) {\n    EncryptionKeyElement keyElement(_data + Offset::keys() + i*Offset::betweenKeys());\n    keyElement.clear();\n    if (i >= ctx.count<BasicEncryptionKey>())\n      continue;\n    if (! keyElement.encodeEncryptionKey(ctx.get<BasicEncryptionKey>(i))) {\n      errMsg(err) << \"Cannot encode \" << i+1 << \"-th encryption key.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::MessageElement\n * ********************************************************************************************* */\nGD73Codeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::MessageElement::MessageElement(uint8_t *ptr)\n  : Element(ptr, GD73Codeplug::MessageElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::MessageElement::text() const {\n  unsigned int length = std::min(Limit::messageLength(), (unsigned int)getUInt8(Offset::size()));\n  return readUnicode(Offset::text(), length, 0x0000);\n}\n\nvoid\nGD73Codeplug::MessageElement::setText(const QString &message) {\n  unsigned int length = std::min(Limit::messageLength(), (unsigned int)message.length());\n  writeUnicode(Offset::text(), message, length, 0x0000);\n  setUInt8(Offset::size(), length);\n}\n\nbool\nGD73Codeplug::MessageElement::encode(SMSTemplate *message, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setText(message->message());\n  return true;\n}\n\nSMSTemplate *\nGD73Codeplug::MessageElement::decode(const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  SMSTemplate *sms = new SMSTemplate();\n  sms->setName(\"Message\");\n  sms->setMessage(text());\n  return sms;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::MessageBankElement\n * ********************************************************************************************* */\nGD73Codeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::MessageBankElement::MessageBankElement(uint8_t *ptr)\n  : Element(ptr, MessageBankElement::size())\n{\n  // pass...\n}\n\nunsigned int\nGD73Codeplug::MessageBankElement::memberCount() const {\n  return getUInt8(Offset::memberCount());\n}\nvoid\nGD73Codeplug::MessageBankElement::setMemberCount(unsigned int count) {\n  setUInt8(Offset::memberCount(), std::min(Limit::memberCount(), count));\n}\n\nGD73Codeplug::MessageElement\nGD73Codeplug::MessageBankElement::message(unsigned int i) {\n  i = std::min(i, memberCount()-1);\n  return MessageElement(_data + Offset::members() + i*Offset::betweenMembers());\n}\n\nbool\nGD73Codeplug::MessageBankElement::decode(SMSExtension *ext, const ErrorStack &err) {\n  ext->smsTemplates()->clear();\n  for (unsigned int i=0; i<memberCount(); i++) {\n    MessageElement element = message(i);\n    SMSTemplate *sms = element.decode(err);\n    if (nullptr == sms) {\n      errMsg(err) << \"Cannot decode \" << i+1 << \"-th preset message.\";\n      return false;\n    }\n    sms->setName(QString(\"Message %1\").arg(i+1));\n    ext->smsTemplates()->add(sms);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::MessageBankElement::encode(const SMSExtension *ext, const ErrorStack &err) {\n  setMemberCount(0);\n\n  unsigned int count = std::min((unsigned int) ext->smsTemplates()->count(), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    if (! message(i).encode(ext->smsTemplates()->get(i)->as<SMSTemplate>(), err)) {\n      errMsg(err) << \"Cannot encode \" << i+1 << \"-th preset message.\";\n      return false;\n    }\n  }\n  setMemberCount(count);\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ContactBankElement\n * ********************************************************************************************* */\nGD73Codeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ContactBankElement::ContactBankElement(uint8_t *ptr)\n  : Element(ptr, ContactBankElement::size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::ContactBankElement::createContacts(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)getUInt16_le(Offset::contactCount()), Limit::contactCount());\n  for (unsigned int i=0; i<count; i++) {\n    ContactElement contact(_data + Offset::contacts() + i*Offset::betweenContacts());\n    DMRContact *contactObj = contact.toContact(ctx, err);\n    if (nullptr == contactObj) {\n      errMsg(err) << \"Cannot decode \" << i << \"-th contact.\";\n      return false;\n    }\n    ctx.config()->contacts()->add(contactObj);\n    ctx.add(contactObj, i);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ContactBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<DigitalContact>(), Limit::contactCount());\n  setUInt16_le(Offset::contactCount(), count);\n\n  for (unsigned int i=0; i<count; i++) {\n    ContactElement contact(_data + Offset::contacts() + i*Offset::betweenContacts());\n    if (! ctx.has<DMRContact>(i)) {\n      errMsg(err) << \"Contact at index \" << i << \" not indexed.\";\n      errMsg(err) << \"Cannot encode contact bank.\";\n      return false;\n    }\n    DMRContact *contactObj = ctx.get<DMRContact>(i);\n    if (! contact.encode(contactObj, ctx, err)) {\n      errMsg(err) << \"Cannot encode contact at index \" << i << \".\";\n      errMsg(err) << \"Cannot encode contact bank.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ContactElement\n * ********************************************************************************************* */\nGD73Codeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, ContactElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::ContactElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nGD73Codeplug::ContactElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nDMRContact::Type\nGD73Codeplug::ContactElement::type() const {\n  switch (getUInt8(Offset::type())) {\n  case 0: return DMRContact::GroupCall;\n  case 1: return DMRContact::PrivateCall;\n  case 2: return DMRContact::AllCall;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nGD73Codeplug::ContactElement::setType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break;\n  case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break;\n  case DMRContact::AllCall: setUInt8(Offset::type(), 2); break;\n  }\n}\n\nunsigned int\nGD73Codeplug::ContactElement::id() const {\n  return getUInt32_le(Offset::id());\n}\nvoid\nGD73Codeplug::ContactElement::setID(unsigned int id) {\n  setUInt32_le(Offset::id(), id);\n}\n\nDMRContact *\nGD73Codeplug::ContactElement::toContact(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new DMRContact(type(), name(), id());\n}\n\nbool\nGD73Codeplug::ContactElement::encode(const DMRContact *contact, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  setType(contact->type());\n  setName(contact->name());\n  setID(contact->number());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::GroupListBankElement\n * ********************************************************************************************* */\nGD73Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : Element(ptr, GroupListBankElement::size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::GroupListBankElement::createGroupLists(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    GroupListElement gl(_data+Offset::members() + i*Offset::betweenMembers());\n    RXGroupList *glObj = gl.toGroupList(ctx, err);\n    if (nullptr == glObj) {\n      errMsg(err) << \"Cannot decode \" << i << \"-th group list.\";\n      return false;\n    }\n    ctx.config()->rxGroupLists()->add(glObj);\n    ctx.add(glObj, i);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::GroupListBankElement::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<RXGroupList>(), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    GroupListElement gl(_data+Offset::members() + i*Offset::betweenMembers());\n    RXGroupList *glObj = ctx.get<RXGroupList>(i);\n    if (! gl.linkGroupList(glObj, ctx, err)) {\n      errMsg(err) << \"Cannot link \" << i << \"-th group list.\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<RXGroupList>(), Limit::memberCount());\n  setUInt8(Offset::memberCount(), count);\n  for (unsigned int i=0; i<count; i++) {\n    GroupListElement gl(_data+Offset::members() + i*Offset::betweenMembers());\n    if (! ctx.has<RXGroupList>(i)) {\n      errMsg(err) << \"Group list at index \" << i << \" is not indexed.\";\n      return false;\n    }\n    if (!gl.encode(ctx.get<RXGroupList>(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode group list at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::GroupListElement\n * ********************************************************************************************* */\nGD73Codeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element(ptr, GroupListElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::GroupListElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nGD73Codeplug::GroupListElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nunsigned int\nGD73Codeplug::GroupListElement::members() const {\n  return getUInt8(Offset::memberCount());\n}\nbool\nGD73Codeplug::GroupListElement::hasMember(unsigned int i) const {\n  return 0 != getUInt16_le(Offset::members() + i*Offset::betweenMembers());\n}\nunsigned int\nGD73Codeplug::GroupListElement::memberIndex(unsigned int i) const {\n  return getUInt16_le(Offset::members() + i*Offset::betweenMembers())-1;\n}\n\nRXGroupList *\nGD73Codeplug::GroupListElement::toGroupList(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new RXGroupList(name());\n}\n\nbool\nGD73Codeplug::GroupListElement::linkGroupList(RXGroupList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  unsigned int count = std::min(members(), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    if (! hasMember(i))\n      continue;\n    unsigned int idx = memberIndex(i);\n    if (ctx.has<DMRContact>(idx))\n      lst->addContact(ctx.get<DMRContact>(idx));\n    else\n      logWarn() << \"Cannot link group list '\" << lst->name()\n                << \"': Cannot resolve contact at index \" << idx << \".\";\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::GroupListElement::encode(RXGroupList *lst, Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)lst->count(), Limit::memberCount());\n  setName(lst->name());\n  setUInt8(Offset::memberCount(), count);\n\n  for (unsigned int i=0; i<count; i++) {\n    if (0 > ctx.index(lst->contact(i))) {\n      errMsg(err) << \"Cannot resolve index of contact '\" << lst->contact(i)->name() << \"'.\";\n      return false;\n    }\n    setUInt16_le(Offset::members() + i*Offset::betweenMembers(), ctx.index(lst->contact(i))+1);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ChannelBankElement\n * ********************************************************************************************* */\nGD73Codeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr)\n  : Element(ptr, ChannelBankElement::size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::ChannelBankElement::createChannels(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::channelCount()), Limit::channelCount());\n  for (unsigned int i=0; i<count; i++) {\n    ChannelElement ch(_data + Offset::channels() + i*Offset::betweenChannels());\n    Channel *chObj = ch.toChannel(ctx, err);\n    if (nullptr == chObj) {\n      errMsg(err) << \"Cannot decode channel at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->channelList()->add(chObj);\n    ctx.add(chObj, i);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ChannelBankElement::linkChannels(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<Channel>(), Limit::channelCount());\n  for (unsigned int i=0; i<count; i++) {\n    ChannelElement ch(_data + Offset::channels() + i*Offset::betweenChannels());\n    Channel *chObj = ctx.get<Channel>(i);\n    if (! ch.linkChannel(chObj, ctx, err)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ChannelBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<Channel>(), Limit::channelCount());\n  setUInt16_le(Offset::channelCount(), count);\n  for (unsigned int i=0; i<count; i++) {\n    ChannelElement ch(_data + Offset::channels() + i*Offset::betweenChannels());\n    Channel *chObj = ctx.get<Channel>(i);\n    if (! ch.encode(chObj, ctx, err)) {\n      errMsg(err) << \"Cannot encode channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ChannelElement\n * ********************************************************************************************* */\nGD73Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, ChannelElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::ChannelElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nGD73Codeplug::ChannelElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nFMChannel::Bandwidth\nGD73Codeplug::ChannelElement::bandwidth() const {\n  switch (getUInt8(Offset::bandwidth())) {\n  case 0: return FMChannel::Bandwidth::Narrow;\n  case 1: return FMChannel::Bandwidth::Wide;\n  }\n  return FMChannel::Bandwidth::Narrow;\n}\nvoid\nGD73Codeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bandwidth) {\n  switch (bandwidth) {\n  case FMChannel::Bandwidth::Narrow: setUInt8(Offset::bandwidth(), 0); break;\n  case FMChannel::Bandwidth::Wide: setUInt8(Offset::bandwidth(), 1); break;\n  }\n}\n\nbool\nGD73Codeplug::ChannelElement::hasScanListIndex() const {\n  return 0 != getUInt8(Offset::scanList());\n}\nunsigned int\nGD73Codeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(Offset::scanList())-1;\n}\nvoid\nGD73Codeplug::ChannelElement::setScanListIndex(unsigned int idx) {\n  setUInt8(Offset::scanList(), idx+1);\n}\nvoid\nGD73Codeplug::ChannelElement::clearScanListIndex() {\n  setUInt8(Offset::scanList(), 0);\n}\n\nGD73Codeplug::ChannelElement::Type\nGD73Codeplug::ChannelElement::type() const {\n  return (Type) getUInt8(Offset::channelType());\n}\nvoid\nGD73Codeplug::ChannelElement::setType(Type type) {\n  setUInt8(Offset::channelType(), (unsigned int)type);\n}\n\nbool\nGD73Codeplug::ChannelElement::talkaroundEnabled() const {\n  return 0x00 != getUInt8(Offset::talkaround());\n}\nvoid\nGD73Codeplug::ChannelElement::enableTalkaround(bool enable) {\n  setUInt8(Offset::talkaround(), enable ? 0x01 : 0x00);\n}\n\nbool\nGD73Codeplug::ChannelElement::rxOnly() const {\n  return 0x00 != getUInt8(Offset::rxOnly());\n}\nvoid\nGD73Codeplug::ChannelElement::enableRXOnly(bool enable) {\n  setUInt8(Offset::rxOnly(), enable ? 0x01 : 0x00);\n}\n\nbool\nGD73Codeplug::ChannelElement::scanAutoStartEnabled() const {\n  return 0x00 != getUInt8(Offset::scanAutoStart());\n}\nvoid\nGD73Codeplug::ChannelElement::enableScanAutoStart(bool enable) {\n  setUInt8(Offset::scanAutoStart(), enable ? 0x01 : 0x00);\n}\n\nFrequency\nGD73Codeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(getUInt32_le(Offset::rxFrequency()));\n}\nvoid\nGD73Codeplug::ChannelElement::setRXFrequency(const Frequency &f) {\n  setUInt32_le(Offset::rxFrequency(), f.inHz());\n}\n\nFrequency\nGD73Codeplug::ChannelElement::txFrequency() const {\n  return Frequency::fromHz(getUInt32_le(Offset::txFrequency()));\n}\nvoid\nGD73Codeplug::ChannelElement::setTXFrequency(const Frequency &f) {\n  setUInt32_le(Offset::txFrequency(), f.inHz());\n}\n\nbool\nGD73Codeplug::ChannelElement::hasDTMFPTTSettingsIndex() const {\n  return 0x00 != getUInt8(Offset::dtmfPTTSettingsIndex());\n}\nunsigned int\nGD73Codeplug::ChannelElement::dtmfPTTSettingsIndex() const {\n  return getUInt8(Offset::dtmfPTTSettingsIndex())-1;\n}\nvoid\nGD73Codeplug::ChannelElement::setDTMFPTTSettingsIndex(unsigned int idx) {\n  setUInt8(Offset::dtmfPTTSettingsIndex(), idx+1);\n}\nvoid\nGD73Codeplug::ChannelElement::clearDTMFPTTSettingsIndex() {\n  setUInt8(Offset::dtmfPTTSettingsIndex(), 0);\n}\n\nChannel::Power\nGD73Codeplug::ChannelElement::power() const {\n  switch (getUInt8(Offset::power())) {\n  case 0: return Channel::Power::Low;\n  case 1: return Channel::Power::High;\n  }\n  return Channel::Power::Low;\n}\nvoid\nGD73Codeplug::ChannelElement::setPower(Channel::Power power) {\n  switch (power) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n  case Channel::Power::Mid:\n    setUInt8(Offset::power(), 0);\n    break;\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setUInt8(Offset::power(), 1);\n    break;\n  }\n}\n\nGD73Codeplug::ChannelElement::Admit\nGD73Codeplug::ChannelElement::admit() const {\n  return (Admit) getUInt8(Offset::admid());\n}\nvoid\nGD73Codeplug::ChannelElement::setAdmit(Admit admit) {\n  setUInt8(Offset::admid(), (unsigned int) admit);\n}\n\nSelectiveCall\nGD73Codeplug::ChannelElement::rxTone() const {\n  int mode = getUInt8(Offset::rxToneMode());\n  int ctcss_code = getUInt8(Offset::rxCTCSS());\n  int dcs_code = getUInt8(Offset::rxDCS());\n  if (0 == mode)\n    return SelectiveCall();\n  if (1 == mode) {\n    if (ctcss_code >= _ctcss_codes.size())\n      return SelectiveCall();\n    return _ctcss_codes[ctcss_code];\n  }\n  if (dcs_code >= _dcs_codes.size())\n    return SelectiveCall();\n  return SelectiveCall(_dcs_codes[dcs_code], 3 == mode);\n}\nvoid\nGD73Codeplug::ChannelElement::setRXTone(const SelectiveCall &code) {\n  int mode = 0, ctcss_code = 0, dcs_code = 0;\n  if (code.isCTCSS()) {\n    mode = 1;\n    ctcss_code = _ctcss_codes.indexOf(code);\n  } else if (code.isDCS()) {\n    if (code.isInverted())\n      mode = 3;\n    else\n      mode = 2;\n    dcs_code = _dcs_codes.indexOf(code.octalCode());\n  }\n  setUInt8(Offset::rxToneMode(), mode);\n  setUInt8(Offset::rxCTCSS(), ctcss_code);\n  setUInt8(Offset::rxDCS(), dcs_code);\n}\n\nSelectiveCall\nGD73Codeplug::ChannelElement::txTone() const {\n  int mode = getUInt8(Offset::txToneMode());\n  int ctcss_code = getUInt8(Offset::txCTCSS());\n  int dcs_code = getUInt8(Offset::txDCS());\n  if (0 == mode)\n    return SelectiveCall();\n  if (1 == mode) {\n    if (ctcss_code >= _ctcss_codes.size())\n      return SelectiveCall();\n    return _ctcss_codes[ctcss_code];\n  }\n  if (dcs_code >= _dcs_codes.size())\n    return SelectiveCall();\n  return SelectiveCall(_dcs_codes[dcs_code], 3 == mode);\n}\nvoid\nGD73Codeplug::ChannelElement::setTXTone(const SelectiveCall &code) {\n  int mode = 0, ctcss_code = 0, dcs_code = 0;\n  if (code.isCTCSS()) {\n    mode = 1;\n    ctcss_code = _ctcss_codes.indexOf(code);\n  } else if (code.isDCS()) {\n    if (code.isInverted())\n      mode = 3;\n    else\n      mode = 2;\n    dcs_code = _dcs_codes.indexOf(code.octalCode());\n  }\n  setUInt8(Offset::txToneMode(), mode);\n  setUInt8(Offset::txCTCSS(), ctcss_code);\n  setUInt8(Offset::txDCS(), dcs_code);\n}\n\nDMRChannel::TimeSlot\nGD73Codeplug::ChannelElement::timeSlot() const {\n  switch (getUInt8(Offset::timeslot())) {\n  case 0:\n  case 2:\n    return DMRChannel::TimeSlot::TS1;\n  case 1:\n  case 3:\n    return DMRChannel::TimeSlot::TS2;\n  }\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nGD73Codeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  switch (ts) {\n  case DMRChannel::TimeSlot::TS1: setUInt8(Offset::timeslot(), 0); break;\n  case DMRChannel::TimeSlot::TS2: setUInt8(Offset::timeslot(), 1); break;\n  }\n}\n\nunsigned int\nGD73Codeplug::ChannelElement::colorCode() const {\n  return getUInt8(Offset::colorcode());\n}\nvoid\nGD73Codeplug::ChannelElement::setColorCode(unsigned int cc) {\n  setUInt8(Offset::colorcode(), std::min(15u, cc));\n}\n\nbool\nGD73Codeplug::ChannelElement::groupListMatchesContact() const {\n  return 0 == getUInt8(Offset::groupListIndex());\n}\nbool\nGD73Codeplug::ChannelElement::groupListAllMatch() const {\n  return 1 == getUInt8(Offset::groupListIndex());\n}\nunsigned int\nGD73Codeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupListIndex())-2;\n}\nvoid\nGD73Codeplug::ChannelElement::setGroupListIndex(unsigned int idx) {\n  setUInt8(Offset::groupListIndex(), idx+2);\n}\nvoid\nGD73Codeplug::ChannelElement::setGroupListAllMatch() {\n  setUInt8(Offset::groupListIndex(), 1);\n}\nvoid\nGD73Codeplug::ChannelElement::setGroupListMatchesContact() {\n  setUInt8(Offset::groupListIndex(), 0);\n}\n\nbool\nGD73Codeplug::ChannelElement::hasTXContact() const {\n  return 0 != getUInt16_le(Offset::contactIndex());\n}\nunsigned int\nGD73Codeplug::ChannelElement::txContactIndex() const {\n  return getUInt16_le(Offset::contactIndex())-1;\n}\nvoid\nGD73Codeplug::ChannelElement::setTXContactIndex(unsigned int idx) {\n  setUInt16_le(Offset::contactIndex(), idx+1);\n}\nvoid\nGD73Codeplug::ChannelElement::clearTXContactIndex() {\n  setUInt16_le(Offset::contactIndex(), 0);\n}\n\nbool\nGD73Codeplug::ChannelElement::hasEmergencySystemIndex() const {\n  return 0 != getUInt8(Offset::emergencySystemIndex());\n}\nunsigned int\nGD73Codeplug::ChannelElement::emergencySystemIndex() const {\n  return getUInt8(Offset::emergencySystemIndex())-1;\n}\nvoid\nGD73Codeplug::ChannelElement::setEmergencySystemIndex(unsigned int idx) {\n  setUInt8(Offset::emergencySystemIndex(), idx+1);\n}\nvoid\nGD73Codeplug::ChannelElement::clearEmergencySystemIndex() {\n  setUInt8(Offset::emergencySystemIndex(), 0);\n}\n\nbool\nGD73Codeplug::ChannelElement::hasEncryptionKeyIndex() const {\n  return 0 != getUInt8(Offset::encryptionKeyIndex());\n}\nunsigned int\nGD73Codeplug::ChannelElement::encryptionKeyIndex() const {\n  return getUInt8(Offset::encryptionKeyIndex())-1;\n}\nvoid\nGD73Codeplug::ChannelElement::setEncryptionKeyIndex(unsigned int idx) {\n  setUInt8(Offset::encryptionKeyIndex(), idx+1);\n}\nvoid\nGD73Codeplug::ChannelElement::clearEncryptionKeyIndex() {\n  setUInt8(Offset::encryptionKeyIndex(), 0);\n}\n\nChannel *\nGD73Codeplug::ChannelElement::toChannel(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n\n  Channel *ch = nullptr;\n\n  if (Type::FM == type()) {\n    FMChannel *fm = new FMChannel(); ch = fm;\n    switch (admit()) {\n    case Admit::Always: fm->setAdmit(FMChannel::Admit::Always); break;\n    case Admit::CC_CTCSS: fm->setAdmit(FMChannel::Admit::Tone); break;\n    case Admit::Free: fm->setAdmit(FMChannel::Admit::Free); break;\n    }\n    fm->setBandwidth(bandwidth());\n    fm->setSquelchDefault();\n    fm->setRXTone(rxTone());\n    fm->setTXTone(txTone());\n    fm->extended()->enableTalkaround(talkaroundEnabled());\n  } else if (Type::DMR == type()) {\n    DMRChannel *dmr = new DMRChannel(); ch = dmr;\n    switch (admit()) {\n    case Admit::Always: dmr->setAdmit(DMRChannel::Admit::Always); break;\n    case Admit::CC_CTCSS: dmr->setAdmit(DMRChannel::Admit::ColorCode); break;\n    case Admit::Free: dmr->setAdmit(DMRChannel::Admit::Free); break;\n    }\n    dmr->setColorCode(colorCode());\n    dmr->setTimeSlot(timeSlot());\n    dmr->setRadioId(DefaultRadioID::get());\n    dmr->extended()->enableTalkaround(talkaroundEnabled());\n  }\n\n  ch->setName(name());\n  ch->setRXFrequency(rxFrequency());\n  ch->setTXFrequency(txFrequency());\n  ch->setRXOnly(rxOnly());\n  ch->setPower(power());\n\n  return ch;\n}\n\n\nbool\nGD73Codeplug::ChannelElement::linkChannel(Channel *ch, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if (Type::DMR == type()) {\n    DMRChannel *dmr = ch->as<DMRChannel>();\n    if (hasTXContact()) {\n      if (ctx.has<DMRContact>(txContactIndex()))\n        dmr->setContact(ctx.get<DMRContact>(txContactIndex()));\n      else\n        logWarn() << \"Cannot link channel '\" << name() << \"', cannot resolve contact index \"\n                  << txContactIndex() << \".\";\n    }\n    if ((! groupListAllMatch()) && (! groupListMatchesContact())) {\n      if (ctx.has<RXGroupList>(groupListIndex()))\n        dmr->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n      else\n        logWarn() << \"Cannot link channel '\" << name() << \"', cannot resolve group list index \"\n                  << groupListIndex() << \".\";\n    }\n    if (hasEncryptionKeyIndex()) {\n      // Check if already defined\n      if (! ctx.has<BasicEncryptionKey>(encryptionKeyIndex())) {\n        errMsg(err) << \"Cannot link channel '\" << name() << \"', cannot resolve encryption key index \"\n                    << encryptionKeyIndex() << \".\";\n        return false;\n      }\n      // set...\n      if (nullptr == dmr->commercialExtension())\n        dmr->setCommercialExtension(new CommercialChannelExtension());\n      dmr->commercialExtension()->setEncryptionKey(\n            ctx.get<BasicEncryptionKey>(encryptionKeyIndex()));\n    }\n  }\n\n  if (hasScanListIndex()) {\n    if (ctx.has<ScanList>(scanListIndex()))\n      ch->setScanList(ctx.get<ScanList>(scanListIndex()));\n    else\n      logWarn() << \"Cannot link channel '\" << name() << \"', cannot resolve scanlist index \"\n                << scanListIndex() << \".\";\n  }\n\n  return true;\n}\n\n\nbool\nGD73Codeplug::ChannelElement::encode(Channel *ch, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Encode common stuff\n  setName(ch->name());\n  setRXFrequency(ch->rxFrequency());\n  setTXFrequency(ch->txFrequency());\n  enableRXOnly(ch->rxOnly());\n  if (! ch->scanListRef()->isNull())\n    setScanListIndex(ctx.index(ch->scanList()));\n  setPower(ch->power());\n\n  // Dispatch by type\n  if (ch->is<DMRChannel>()) {\n    DMRChannel *dmr = ch->as<DMRChannel>();\n    setType(ChannelElement::Type::DMR);\n    if (! dmr->contactRef()->isNull())\n      setTXContactIndex(ctx.index(dmr->contact()));\n    if (dmr->groupListRef()->isNull())\n      setGroupListAllMatch();\n    else\n      setGroupListIndex(ctx.index(dmr->groupList()));\n    switch (dmr->admit()) {\n    case DMRChannel::Admit::Always: setAdmit(Admit::Always); break;\n    case DMRChannel::Admit::ColorCode: setAdmit(Admit::CC_CTCSS); break;\n    case DMRChannel::Admit::Free: setAdmit(Admit::Free); break;\n    }\n    setColorCode(dmr->colorCode());\n    setTimeSlot(dmr->timeSlot());\n    if (CommercialChannelExtension *ext = dmr->commercialExtension())\n      if ((! ext->encryptionKeyRef()->isNull()) && (0 <= ctx.index(ext->encryptionKey())) )\n        setEncryptionKeyIndex(ctx.index(ext->encryptionKey()));\n    enableTalkaround(dmr->extended()->talkaround());\n  } else if (ch->is<FMChannel>()) {\n    FMChannel *fm = ch->as<FMChannel>();\n    setType(ChannelElement::Type::FM);\n    switch (fm->admit()) {\n    case FMChannel::Admit::Always: setAdmit(Admit::Always); break;\n    case FMChannel::Admit::Tone: setAdmit(Admit::CC_CTCSS); break;\n    case FMChannel::Admit::Free: setAdmit(Admit::Free); break;\n    }\n    setBandwidth(fm->bandwidth());\n    setRXTone(fm->rxTone());\n    setTXTone(fm->txTone());\n    enableTalkaround(fm->extended()->talkaround());\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ZoneBankElement\n * ********************************************************************************************* */\nGD73Codeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr)\n  : Element(ptr, ZoneBankElement::size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::ZoneBankElement::createZones(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::zoneCount()), Limit::zoneCount());\n  for (unsigned int i=0; i<count; i++) {\n    ZoneElement zone(_data + Offset::zones() + i*Offset::betweenZones());\n    Zone *zoneObj = zone.toZone(ctx, err);\n    if (nullptr == zoneObj) {\n      errMsg(err) << \"Cannot decode zone at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->zones()->add(zoneObj);\n    ctx.add(zoneObj, i);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ZoneBankElement::linkZones(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<Zone>(), Limit::zoneCount());\n  for (unsigned int i=0; i<count; i++) {\n    ZoneElement zone(_data+Offset::zones() + i*Offset::betweenZones());\n    Zone *zoneObj = ctx.get<Zone>(i);\n    if (! zone.linkZone(zoneObj, ctx, err)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<Zone>(), Limit::zoneCount());\n  setUInt8(Offset::zoneCount(), count);\n  for (unsigned int i=0; i<count; i++) {\n    ZoneElement zone(_data+Offset::zones() + i*Offset::betweenZones());\n    Zone *zoneObj = ctx.get<Zone>(i);\n    if (! zone.encode(zoneObj, ctx, err)) {\n      errMsg(err) << \"Cannot encode zone at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ZoneElement\n * ********************************************************************************************* */\nGD73Codeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element(ptr, ZoneElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::ZoneElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nGD73Codeplug::ZoneElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nZone *\nGD73Codeplug::ZoneElement::toZone(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new Zone(name());\n}\n\nbool\nGD73Codeplug::ZoneElement::linkZone(Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::channeCount()), Limit::channelCount());\n  for (unsigned int i=0; i<count; i++) {\n    unsigned int index = getUInt16_le(Offset::channelIndices() + i*Offset::betweenChannelIndices());\n    if (0 == index)\n      continue;\n    if (ctx.has<Channel>(index-1))\n      zone->A()->add(ctx.get<Channel>(index-1));\n    else\n      logWarn() << \"Cannot link zone '\" << zone->name() << \"': Channel at index \" << (index-1)\n                << \" not known.\";\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ZoneElement::encode(Zone *zone, Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)zone->A()->count(), Limit::channelCount());\n  setUInt8(Offset::channeCount(), count);\n  setName(zone->name());\n\n  for (unsigned int i=0; i<count; i++) {\n    if (0 > ctx.index(zone->A()->get(i)->as<Channel>())) {\n      errMsg(err) << \"Cannot find index for channel \" << zone->A()->get(i)->name()\n                  << \" in zone \" << zone->name() << \".\";\n      return false;\n    }\n    setUInt16_le(Offset::channelIndices() + i*Offset::betweenChannelIndices(),\n                 ctx.index(zone->A()->get(i)->as<Channel>())+1);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ScanListBankElement\n * ********************************************************************************************* */\nGD73Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr)\n  : Element(ptr, ScanListBankElement::size())\n{\n  // pass...\n}\n\nbool\nGD73Codeplug::ScanListBankElement::createScanLists(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    ScanListElement lst(_data + Offset::members() + i*Offset::betweenMembers());\n    ScanList *lstObj = lst.toScanList(ctx, err);\n    if (nullptr == lstObj) {\n      errMsg(err) << \"Cannot decode scan list at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->scanlists()->add(lstObj);\n    ctx.add(lstObj, i);\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ScanListBankElement::linkScanLists(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<ScanList>(), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    ScanListElement lst(_data + Offset::members() + i*Offset::betweenMembers());\n    ScanList *lstObj = ctx.get<ScanList>(i);\n    if (! lst.linkScanList(lstObj, ctx, err)) {\n      errMsg(err) << \"Cannot link scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::ScanListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  unsigned int count = std::min(ctx.count<ScanList>(), Limit::memberCount());\n  setUInt8(Offset::memberCount(), count);\n  for (unsigned int i=0; i<count; i++) {\n    ScanListElement lst(_data + Offset::members() + i*Offset::betweenMembers());\n    ScanList *lstObj = ctx.get<ScanList>(i);\n    if (! lst.encode(lstObj, ctx, err)) {\n      errMsg(err) << \"Cannot encode scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug::ScanListElement\n * ********************************************************************************************* */\nGD73Codeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nGD73Codeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element(ptr, ScanListElement::size())\n{\n  // pass...\n}\n\nQString\nGD73Codeplug::ScanListElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nGD73Codeplug::ScanListElement::setName(const QString &name) {\n  writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nGD73Codeplug::ScanListElement::ChannelMode\nGD73Codeplug::ScanListElement::primaryChannelMode() const {\n  return (ChannelMode)getUInt8(Offset::priChannel1Mode());\n}\nvoid\nGD73Codeplug::ScanListElement::setPrimaryChannelMode(ChannelMode mode) {\n  setUInt8(Offset::priChannel1Mode(), (unsigned int)mode);\n}\nbool\nGD73Codeplug::ScanListElement::hasPrimaryZoneIndex() const {\n  return 0 != getUInt8(Offset::priChannel1Zone());\n}\nunsigned int\nGD73Codeplug::ScanListElement::primaryZoneIndex() const {\n  return getUInt8(Offset::priChannel1Zone())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setPrimaryZoneIndex(unsigned int idx) {\n  setUInt8(Offset::priChannel1Zone(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearPrimaryZoneIndex() {\n  setUInt8(Offset::priChannel1Zone(), 0);\n}\nbool\nGD73Codeplug::ScanListElement::hasPrimaryChannelIndex() const {\n  return 0 != getUInt8(Offset::priChannel1Channel());\n}\nunsigned int\nGD73Codeplug::ScanListElement::primaryChannelIndex() const {\n  return getUInt8(Offset::priChannel1Channel())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setPrimaryChannelIndex(unsigned int idx) {\n  setUInt8(Offset::priChannel1Channel(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearPrimaryChannelIndex() {\n  setUInt8(Offset::priChannel1Channel(), 0);\n}\n\nGD73Codeplug::ScanListElement::ChannelMode\nGD73Codeplug::ScanListElement::secondaryChannelMode() const {\n  return (ChannelMode)getUInt8(Offset::priChannel2Mode());\n}\nvoid\nGD73Codeplug::ScanListElement::setSecondaryChannelMode(ChannelMode mode) {\n  setUInt8(Offset::priChannel2Mode(), (unsigned int)mode);\n}\nbool\nGD73Codeplug::ScanListElement::hasSecondaryZoneIndex() const {\n  return 0 != getUInt8(Offset::priChannel2Zone());\n}\nunsigned int\nGD73Codeplug::ScanListElement::secondaryZoneIndex() const {\n  return getUInt8(Offset::priChannel2Zone())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setSecondaryZoneIndex(unsigned int idx) {\n  setUInt8(Offset::priChannel2Zone(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearSecondaryZoneIndex() {\n  setUInt8(Offset::priChannel2Zone(), 0);\n}\nbool\nGD73Codeplug::ScanListElement::hasSecondaryChannelIndex() const {\n  return 0 != getUInt8(Offset::priChannel2Channel());\n}\nunsigned int\nGD73Codeplug::ScanListElement::secondaryChannelIndex() const {\n  return getUInt8(Offset::priChannel2Channel())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setSecondaryChannelIndex(unsigned int idx) {\n  setUInt8(Offset::priChannel2Channel(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearSecondaryChannelIndex() {\n  setUInt8(Offset::priChannel2Channel(), 0);\n}\n\nGD73Codeplug::ScanListElement::ChannelMode\nGD73Codeplug::ScanListElement::revertChannelMode() const {\n  return (ChannelMode)getUInt8(Offset::txChannelMode());\n}\nvoid\nGD73Codeplug::ScanListElement::setRevertChannelMode(ChannelMode mode) {\n  setUInt8(Offset::txChannelMode(), (unsigned int)mode);\n}\nbool\nGD73Codeplug::ScanListElement::hasRevertZoneIndex() const {\n  return 0 != getUInt8(Offset::txChannelZone());\n}\nunsigned int\nGD73Codeplug::ScanListElement::revertZoneIndex() const {\n  return getUInt8(Offset::txChannelZone())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setRevertZoneIndex(unsigned int idx) {\n  setUInt8(Offset::txChannelZone(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearRevertZoneIndex() {\n  setUInt8(Offset::txChannelZone(), 0);\n}\nbool\nGD73Codeplug::ScanListElement::hasRevertChannelIndex() const {\n  return 0 != getUInt8(Offset::txChannelChannel());\n}\nunsigned\nGD73Codeplug::ScanListElement::revertChannelIndex() const {\n  return getUInt8(Offset::txChannelChannel())-1;\n}\nvoid\nGD73Codeplug::ScanListElement::setRevertChannelIndex(unsigned int idx) {\n  setUInt8(Offset::txChannelChannel(), idx+1);\n}\nvoid\nGD73Codeplug::ScanListElement::clearRevertChannelIndex() {\n  setUInt8(Offset::txChannelChannel(), 0);\n}\n\nInterval\nGD73Codeplug::ScanListElement::rxHoldTime() const {\n  return Interval::fromMilliseconds(500*getUInt8(Offset::holdTime()));\n}\nvoid\nGD73Codeplug::ScanListElement::setRXHoldTime(const Interval &interval) {\n  setUInt8(Offset::holdTime(), Limit::holdTime().map(interval).milliseconds()/500);\n}\n\nInterval\nGD73Codeplug::ScanListElement::txHoldTime() const {\n  return Interval::fromMilliseconds(500*getUInt8(Offset::txHoldTime()));\n}\nvoid\nGD73Codeplug::ScanListElement::setTXHoldTime(const Interval &interval) {\n  setUInt8(Offset::txHoldTime(), Limit::holdTime().map(interval).milliseconds()/500);\n}\n\nScanList *\nGD73Codeplug::ScanListElement::toScanList(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new ScanList(name());\n}\n\nbool\nGD73Codeplug::ScanListElement::linkScanList(ScanList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if ((ChannelMode::Fixed == primaryChannelMode()) && hasPrimaryChannelIndex()) {\n    if (ctx.has<Channel>(primaryChannelIndex()))\n      lst->setPrimaryChannel(ctx.get<Channel>(primaryChannelIndex()));\n    else\n      logWarn() << \"Cannot link scan list '\" << lst->name()\n                << \"': Cannot resolve primary channel index \" << primaryChannelIndex() << \".\";\n  } else if (ChannelMode::Selected == primaryChannelMode()) {\n    lst->setPrimaryChannel(SelectedChannel::get());\n  }\n\n  if ((ChannelMode::Fixed == secondaryChannelMode()) && hasSecondaryChannelIndex()) {\n    if (ctx.has<Channel>(secondaryChannelIndex()))\n      lst->setSecondaryChannel(ctx.get<Channel>(secondaryChannelIndex()));\n    else\n      logWarn() << \"Cannot link scan list '\" << lst->name()\n                << \"': Cannot resolve secondary channel index \" << secondaryChannelIndex() << \".\";\n  } else if (ChannelMode::Selected == secondaryChannelMode()) {\n    lst->setSecondaryChannel(SelectedChannel::get());\n  }\n\n  if ((ChannelMode::Fixed == revertChannelMode()) && hasRevertChannelIndex()) {\n    if (ctx.has<Channel>(revertChannelIndex()))\n      lst->setRevertChannel(ctx.get<Channel>(revertChannelIndex()));\n    else\n      logWarn() << \"Cannot link scan list '\" << lst->name()\n                << \"': Cannot resolve revert channel index \" << revertChannelIndex() << \".\";\n  } else if (ChannelMode::Selected == revertChannelMode()) {\n    lst->setRevertChannel(SelectedChannel::get());\n  }\n\n  unsigned int count = std::min((unsigned int)getUInt8(Offset::memberCount()), Limit::memberCount());\n  for (unsigned int i=0; i<count; i++) {\n    unsigned int index = getUInt16_le(Offset::members() + i*Offset::betweenMembers());\n    if (0 == index)\n      continue;\n    if (ctx.has<Channel>(index-1))\n      lst->addChannel(ctx.get<Channel>(index-1));\n    else\n      logWarn() << \"Cannot link scan list '\" << lst->name()\n                << \"': Cannot resolve member index\" << index-1 << \".\";\n  }\n\n  return true;\n}\n\nbool\nGD73Codeplug::ScanListElement::encode(ScanList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setName(lst->name());\n\n  if (! lst->primaryChannelRef()->isNull()) {\n    if (SelectedChannel::get() == lst->primaryChannel()) {\n      setPrimaryChannelMode(ChannelMode::Selected);\n    } else {\n      setPrimaryChannelMode(ChannelMode::Fixed);\n      setPrimaryChannelIndex(ctx.index(lst->primaryChannel()));\n    }\n  }\n\n  if (! lst->secondaryChannelRef()->isNull()) {\n    if (SelectedChannel::get() == lst->secondaryChannel()) {\n      setSecondaryChannelMode(ChannelMode::Selected);\n    } else {\n      setSecondaryChannelMode(ChannelMode::Fixed);\n      setSecondaryChannelIndex(ctx.index(lst->secondaryChannel()));\n    }\n  }\n\n  if (! lst->revertChannelRef()->isNull()) {\n    if (SelectedChannel::get() == lst->revertChannel()) {\n      setRevertChannelMode(ChannelMode::Selected);\n    } else {\n      setRevertChannelMode(ChannelMode::Fixed);\n      setRevertChannelIndex(ctx.index(lst->revertChannel()));\n    }\n  }\n\n  unsigned int count = std::min((unsigned int)lst->count(), Limit::memberCount());\n  setUInt8(Offset::memberCount(), count);\n\n  for (unsigned int i=0; i<count; i++) {\n    setUInt16_le(Offset::members() + i*Offset::betweenMembers(), ctx.index(lst->channel(i))+1);\n  }\n\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of GD73Codeplug\n * ********************************************************************************************* */\nGD73Codeplug::GD73Codeplug(QObject *parent)\n  : Codeplug{parent}\n{\n  addImage(\"Radioddity GD-73A/E codeplug\");\n  image(0).addElement(0x000000, 0x22014);\n}\n\nConfig *\nGD73Codeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *copy = Codeplug::preprocess(config, err);\n  if (nullptr == copy) {\n    errMsg(err) << \"Cannot pre-process codeplug for GD73A/E.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(copy, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete copy;\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(copy, err)) {\n    errMsg(err) << \"Cannot pre-process codeplug for GD73A/E.\";\n    return nullptr;\n  }\n\n  return copy;\n}\n\nbool\nGD73Codeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process codeplug for GD73A/E.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot post-process codeplug for GD73A/E.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nGD73Codeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  // There must be a default DMR radio ID.\n  if (nullptr == ctx.config()->settings()->defaultId()) {\n    errMsg(err) << \"No default DMR radio ID specified.\";\n    errMsg(err) << \"Cannot index codeplug for encoding for the Radioddity GD73.\";\n    return false;\n  }\n\n  // Map radio IDs\n  for (int i=0; i<ctx.config()->radioIDs()->count(); i++) {\n    if (ctx.config()->radioIDs()->get(i)->is<DMRContact>())\n      ctx.add(ctx.config()->radioIDs()->get(i)->as<DMRContact>(), i);\n  }\n  // Map digital and DTMF contacts\n  for (int i=0, d=0, a=0; i<config->contacts()->count(); i++) {\n    if (ctx.config()->contacts()->contact(i)->is<DMRContact>()) {\n      ctx.add(ctx.config()->contacts()->contact(i)->as<DMRContact>(), d); d++;\n    } else if (ctx.config()->contacts()->contact(i)->is<DTMFContact>()) {\n      ctx.add(ctx.config()->contacts()->contact(i)->as<DTMFContact>(), a); a++;\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(ctx.config()->rxGroupLists()->list(i), i);\n\n  // Map channels\n  for (int i=0; i<config->channelList()->count(); i++) {\n    if (ctx.config()->channelList()->get(i)->is<DMRChannel>() ||\n        ctx.config()->channelList()->get(i)->is<FMChannel>())\n      ctx.add(ctx.config()->channelList()->channel(i), i);\n  }\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i);\n\n  // Handle encryption keys)\n  if (nullptr != config->commercialExtension()) {\n    for (int i=0, j=0; i<config->commercialExtension()->encryptionKeys()->count(); i++) {\n      EncryptionKey *key = config->commercialExtension()->encryptionKeys()->key(i);\n      // Can only encode basic encryption keys\n      if (! key->is<BasicEncryptionKey>())\n        continue;\n      ctx.add(key->as<BasicEncryptionKey>(), j++);\n    }\n  }\n\n  return true;\n\n}\n\nbool\nGD73Codeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  Context ctx(config);\n\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeTimestamp(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeMessages(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeSettings(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeContacts(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeEncryptionKeys(ctx, err)) {\n    errMsg(err) << \"Cannot encode encryption keys for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeChannels(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeZones(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  if (! encodeScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot encode codeplug for Radioddity GD73.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nGD73Codeplug::decode(Config *config, const ErrorStack &err) {\n  Context ctx(config);\n\n  if (! decodeTimestamp(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createMessages(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! decodeSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createContacts(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createDTMFContacts(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createEncryptionKeys(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createChannels(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createZones(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! createScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! linkGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  if (! linkScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot decode codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nGD73Codeplug::decodeTimestamp(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  InformationElement info(data(Offset::timestamp()));\n  if (! info.isValid()) {\n    errMsg(err) << \"Cannot parse info element @\" << Qt::hex << Offset::timestamp() << \".\";\n    return false;\n  }\n\n  // Nothing to do here.\n  return true;\n}\n\nbool\nGD73Codeplug::encodeTimestamp(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n\n  InformationElement info(data(Offset::timestamp()));\n  info.setTimestamp(QDateTime::currentDateTimeUtc());\n  info.setFrequencyRange(FrequencyRange {Frequency::fromMHz(400.0), Frequency::fromMHz(470.0)});\n  return true;\n}\n\n\nbool\nGD73Codeplug::createMessages(Context &ctx, const ErrorStack &err) {\n  if (! MessageBankElement(data(Offset::messages())).decode(ctx.config()->smsExtension())) {\n    errMsg(err) << \"Cannot decode preset text messages.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nGD73Codeplug::encodeMessages(Context &ctx, const ErrorStack &err) {\n  if (! MessageBankElement(data(Offset::messages())).encode(ctx.config()->smsExtension())) {\n    errMsg(err) << \"Cannot encode preset text messages.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nGD73Codeplug::decodeSettings(Context &ctx, const ErrorStack &err) {\n  SettingsElement settings(data(Offset::settings()));\n  if (! settings.updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode settings element.\";\n    return false;\n  }\n  DMRSettingsElement dmrSettings(data(Offset::dmrSettings()));\n  if (! dmrSettings.updateConfig(ctx, err)) {\n    errMsg(err) << \"Cannot decode DMR settings element.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeSettings(Context &ctx, const ErrorStack &err) {\n  SettingsElement settings(data(Offset::settings()));\n  if (! settings.encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode settings element.\";\n    return false;\n  }\n  DMRSettingsElement dmrSettings(data(Offset::dmrSettings()));\n  if (! dmrSettings.encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode DMR settings element.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  ContactBankElement bank(data(Offset::contacts()));\n  if (! bank.createContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create contacts.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeContacts(Context &ctx, const ErrorStack &err) {\n  ContactBankElement bank(data(Offset::contacts()));\n  if (! bank.encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n  return true;\n}\n\n\nbool\nGD73Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\nbool\nGD73Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  if (! GroupListBankElement(data(Offset::groupLists())).createGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot decode group lists.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  if (! GroupListBankElement(data(Offset::groupLists())).linkGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeGroupLists(Context &ctx, const ErrorStack &err) {\n  if (! GroupListBankElement(data(Offset::groupLists())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::createEncryptionKeys(Context &ctx, const ErrorStack &err) {\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).createEncryptionKeys(ctx, err)) {\n    errMsg(err) << \"Cannot create encryption keys.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeEncryptionKeys(Context &ctx, const ErrorStack &err) {\n  if (! EncryptionKeyBankElement(data(Offset::encryptionKeys())).encodeEncryptionKeys(ctx, err)) {\n    errMsg(err) << \"Cannot encode encryption keys.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  if (! ChannelBankElement(data(Offset::channels())).createChannels(ctx, err)) {\n    errMsg(err) << \"Cannot create channels.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  if (! ChannelBankElement(data(Offset::channels())).linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeChannels(Context &ctx, const ErrorStack &err) {\n  if (! ChannelBankElement(data(Offset::channels())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode channels.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  if (! ZoneBankElement(data(Offset::zones())).createZones(ctx, err)) {\n    errMsg(err) << \"Cannot create zones.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  if (! ZoneBankElement(data(Offset::zones())).linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeZones(Context &ctx, const ErrorStack &err) {\n  if (! ZoneBankElement(data(Offset::zones())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  if (! ScanListBankElement(data(Offset::scanLists())).createScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot create scan-lists.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  if (! ScanListBankElement(data(Offset::scanLists())).linkScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot link scan-lists.\";\n    return false;\n  }\n  return true;\n}\n\nbool\nGD73Codeplug::encodeScanLists(Context &ctx, const ErrorStack &err) {\n  if (! ScanListBankElement(data(Offset::scanLists())).encode(ctx, err)) {\n    errMsg(err) << \"Cannot encode scan-lists.\";\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/gd73_codeplug.hh",
    "content": "#ifndef GD73CODEPLUG_HH\n#define GD73CODEPLUG_HH\n\n#include \"codeplug.hh\"\n#include \"interval.hh\"\n#include \"ranges.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"channel.hh\"\n#include \"zone.hh\"\n#include \"bootsettings.hh\"\n#include \"radioddity_extensions.hh\"\n\n\nclass SMSTemplate;\nclass SMSExtension;\n\n/** Represents, encodes and decodes the device specific codeplug for a Radioddity GD-73.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>     <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x00000-0x22014</th></tr>\n *  <tr><td>0x00000</td> <td>0x00061</td> <td>0x0061</td>  <td>Basic info, see\n *    @c GD73Codeplug::InformationElement</td></tr>\n *  <tr><td>0x00061</td> <td>0x0010b</td> <td>0x00aa</td>  <td>Radio settings, see\n *    @c GD73Codeplug::SettingsElement</td></tr>\n *  <tr><td>0x0010b</td> <td>0x00d4c</td> <td>0x0c41</td>  <td>Zone bank, see\n *    @c GD73Codeplug::ZoneBankElement</td></tr>\n *  <tr><td>0x00d4c</td> <td>0x1254e</td> <td>0x11802</td> <td>Channel bank, see\n *    @c GD73Codeplug::ChannelBankElement</td></tr>\n *  <tr><td>0x125ff</td> <td>0x1c201</td> <td>0x9c02</td>  <td>Contact bank, see\n *    @c GD73Codeplug::ContactBankElement</td></tr>\n *  <tr><td>0x1c201</td> <td>0x21310</td> <td>0x510f</td>  <td>Group list bank, see\n *    @c GD73Codeplug::GroupListBankElement</td></tr>\n *  <tr><td>0x21310</td> <td>0x21911</td> <td>0x0601</td>  <td>Scan list bank, see\n *    @c GD73Codeplug::ScanListBankElement</td></tr>\n *  <tr><td>0x21911</td> <td>0x2191f</td> <td>0x000e</td>  <td>DMR settings, see\n *    @c GD73Codeplug::DMRSettingsElement</td></tr>\n *  <tr><td>0x2191f</td> <td>0x2196f</td> <td>0x0050</td>  <td>16 encryption keys, see\n *    @c GD73Codeplug::EncryptionKeyBankElement</td></tr>\n *  <tr><td>0x2196f</td> <td>0x21e80</td> <td>0x0511</td>  <td>Message bank, see\n *    @c GD73Codeplug::MessageBankElement</td></tr>\n *  <tr><td>0x21e80</td> <td>0x21e94</td> <td>0x0014</td>  <td>4 DTMF systems, see\n *    @c GD73Codeplug::DTMFSystemBankElement</td></tr>\n *  <tr><td>0x21e94</td> <td>0x21f24</td> <td>0x0090</td>  <td>16 DTMF numbers, see\n *    @c GD73Codeplug::DTMFNumberBankElement</td></tr>\n *  <tr><td>0x21f24</td>  <td>0x21fc4</td> <td>0x00a0</td>  <td>32 DTMF PTT settings, see\n *    @c GD73Codeplug::DTMFPTTSettingBankElement</td></tr>\n *  <tr><td>0x21fc4</td>  <td>0x22014</td> <td>0x0050</td>  <td>Unused, filled with 0x00</td></tr>\n * </table>\n *\n * @ingroup gd73 */\nclass GD73Codeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements the information element.\n   *\n   * Memory representation of the element (size 000h bytes):\n   * @verbinclude gd73_timestamp.txt */\n  class InformationElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    InformationElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    InformationElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0061; }\n\n    /** Returns the frequency range, supported by the radio. */\n    FrequencyRange frequencyRange() const;\n    /** Overrides the frequency range settings. */\n    void setFrequencyRange(const FrequencyRange &range);\n\n    /** Returns the timestamp of the last programming. */\n    QDateTime timestamp() const;\n    /** Sets the timestamp of the last programming. */\n    void setTimestamp(const QDateTime &timestamp);\n\n    /** Returns the serial number as a string. */\n    QString serial() const;\n    /** Returns the model name. */\n    QString modelName() const;\n    /** Returns the device id. */\n    QString deviceID() const;\n    /** Returns the model number as a string. */\n    QString modelNumber() const;\n    /** Returns the software version as a string. */\n    QString softwareVersion() const;\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum length of serial number. */\n      static constexpr unsigned int serial()                                { return 16; }\n      /** Maximum length of model name. */\n      static constexpr unsigned int modelName()                             { return 16; }\n      /** Maximum length of device id. */\n      static constexpr unsigned int deviceID()                              { return 16; }\n      /** Maximum length of model number. */\n      static constexpr unsigned int modelNumber()                           { return 16; }\n      /** Maximum length of software version. */\n      static constexpr unsigned int softwareVersion()                       { return 16; }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int frequencyRange()                        { return 0x0000; }\n      static constexpr unsigned int dateCentury()                           { return 0x0001; }\n      static constexpr unsigned int dateYear()                              { return 0x0002; }\n      static constexpr unsigned int dateMonth()                             { return 0x0003; }\n      static constexpr unsigned int dateDay()                               { return 0x0004; }\n      static constexpr unsigned int dateHour()                              { return 0x0005; }\n      static constexpr unsigned int dateMinute()                            { return 0x0006; }\n      static constexpr unsigned int dateSecond()                            { return 0x0007; }\n      static constexpr unsigned int serial()                                { return 0x0011; }\n      static constexpr unsigned int modelName()                             { return 0x0021; }\n      static constexpr unsigned int deviceID()                              { return 0x0031; }\n      static constexpr unsigned int modelNumber()                           { return 0x0041; }\n      static constexpr unsigned int softwareVersion()                       { return 0x0051; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements one of the 5 one-touch settings elements.\n   *\n   * Memory representation of the element (size 0005h bytes):\n   * @verbinclude gd73_one_touch_element.txt */\n  class OneTouchSettingElement: public Element\n  {\n  public:\n    /** Possible one-touch actions. */\n    enum class Action {\n      Call = 0, Message = 1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    OneTouchSettingElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    OneTouchSettingElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0005; }\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contact()                               { return 0x0001; }\n      static constexpr unsigned int action()                                { return 0x0003; }\n      static constexpr unsigned int message()                               { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the radio settings.\n   *\n   * Memory representation within the binary codeplug (size: 00aah):\n   * @verbinclude gd73_settings_element.txt.\n   */\n  class SettingsElement: public Element\n  {\n  public:\n    /** Possible channel display modes. */\n    enum class ChannelDisplayMode {\n      Name = 0, Frequency = 1\n    };\n    /** Possible boot display modes. */\n    enum class BootDisplayMode {\n      Off = 0, Text = 1, Image = 2, Both = 3\n    };\n    /** Possible programmable key function. */\n    struct KeyFunction {\n    public:\n      /** Encodes the given function. */\n      static uint8_t encode(RadioddityButtonSettingsExtension::Function func);\n      /** Decodes the given function code. */\n      static RadioddityButtonSettingsExtension::Function decode(uint8_t code);\n    protected:\n      /** Possible function codes. */\n      enum Code {\n        None=0, RadioEnable=1, RadioCheck=2, RadioDisable=3, PowerLevel=4,\n        Monitor=5, EmergencyOn=6, EmergencyOff=7, ZoneSwitch=8, ToggleScan=9, ToggleVOX=10,\n        OneTouch1=11, OneTouch2=12, OneTouch3=13, OneTouch4=14, OneTouch5=15, ToggleTalkaround=16,\n        LoneWorker=17, TBST=18, CallSwell=19\n      };\n    };\n    /** Possible languages. */\n    enum class Language {\n      Chinese=0, English=1\n    };\n\n  protected:\n    /** Hidden constructor. */\n    SettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    SettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the settings element. */\n    static constexpr unsigned int size() { return 0x00aa; }\n\n    /** Returns the radio name. */\n    QString name() const;\n    /** Sets the radio name. */\n    void setName(const QString &name);\n\n    /** Returns the radio ID. */\n    unsigned int dmrID() const;\n    /** Sets the radio ID. */\n    void setDMRID(unsigned int id);\n\n    /** Returns the menu language. */\n    Language language() const;\n    /** Sets the menu language. */\n    void setLanguage(Language lang);\n\n    /** Returns the VOX level [0,10]. */\n    Level vox() const;\n    /** Sets the VOX level [0,10]. */\n    void setVOX(Level level);\n\n    /** Returns the squelch level [0,10]. */\n    Level squelch() const;\n    /** Sets the squelch level [0,10]. */\n    void setSquelch(Level level);\n\n    /** Returns @c true, if a transmit time-out is set. */\n    bool totIsSet() const;\n    /** Returns the transmit time-out. */\n    Interval tot() const;\n    /** Sets the transmit time-out. */\n    void setTOT(const Interval &interval);\n    /** Disables transmit time-out. */\n    void clearTOT();\n\n    /** Returns @c true if the TX interrupt is enabled. */\n    bool txInterruptedEnabled() const;\n    /** Enables/disables the TX interrupt. */\n    void enableTXInterrupt(bool enable);\n\n    /** Returns @c true if power save is enabled. */\n    bool powerSaveEnabled() const;\n    /** Enables/disables power save. */\n    void enablePowerSave(bool enable);\n    /** Returns the power-save time-out. */\n    Interval powerSaveTimeout() const;\n    /** Sets the power-save time-out. */\n    void setPowerSaveTimeout(const Interval &interval);\n\n    /** Returns @c true, if the read lock is enabled. */\n    bool readLockEnabled() const;\n    /** Enables/disables read lock. */\n    void enableReadLock(bool enable);\n    /** Returns the read-lock pin (1-6 digits as ASCII). */\n    QString readLockPin() const;\n    /** Sets the read-lock pin (1-6 digits as ASCII). */\n    void setReadLockPin(const QString &pin);\n\n    /** Returns @c true, if the write lock is enabled. */\n    bool writeLockEnabled() const;\n    /** Enables/disables write lock. */\n    void enableWriteLock(bool enable);\n    /** Returns the write-lock pin (1-6 digits as ASCII). */\n    QString writeLockPin() const;\n    /** Sets the write-lock pin (1-6 digits as ASCII). */\n    void setWriteLockPin(const QString &pin);\n\n    /** Returns the channel display mode. */\n    ChannelDisplayMode channelDisplayMode() const;\n    /** Sets the channel display mode. */\n    void setChannelDisplayMode(ChannelDisplayMode mode);\n\n    /** Returns the DMR microphone gain [1,10]. */\n    Level dmrMicGain() const;\n    /** Sets the DMR microphone gain [1,10]. */\n    void setDMRMicGain(Level gain);\n    /** Returns the FM microphone gain [1,10]. */\n    Level fmMicGain() const;\n    /** Sets the FM microphone gain [1,10]. */\n    void setFMMicGain(Level gain);\n\n    /** Returns the lone-worker response time-out. */\n    Interval loneWorkerResponseTimeout() const;\n    /** Sets the lone-worker response time-out. */\n    void setLoneWorkerResponseTimeout(const Interval &interval);\n    /** Returns the lone-worker remind period. */\n    Interval loneWorkerRemindPeriod() const;\n    /** Sets the lone-worker remind period. */\n    void setLoneWorkerRemindPeriod(const Interval &interval);\n\n    /** Returns the boot display mode. */\n    BootSettings::BootDisplay bootDisplayMode() const;\n    /** Sets the boot display mode. */\n    void setBootDisplayMode(BootSettings::BootDisplay mode);\n    /** Returns the first line of the boot text. */\n    QString bootTextLine1() const;\n    /** Sets the first line of the boot text. */\n    void setBootTextLine1(const QString &line);\n    /** Returns the second line of the boot text. */\n    QString bootTextLine2() const;\n    /** Sets the second line of the boot text. */\n    void setBootTextLine2(const QString &line);\n\n    /** Returns @c true if the key tones are enabled. */\n    bool keyToneEnabled() const;\n    /** Enables/disables the key tones. */\n    void enableKeyTone(bool enable);\n    /** Returns the key-tone volume [0-13]. */\n    Level keyToneVolume() const;\n    /** Sets the key-tone volume. */\n    void setKeyToneVolume(Level vol);\n    /** Returns @c true if the low-battery warn tone is enabled. */\n    bool lowBatteryToneEnabled() const;\n    /** Enables/disables the low-battery warn tone. */\n    void enableLowBatteryTone(bool enable);\n    /** Returns the low-battery warn-tone volume [0-13]. */\n    unsigned int lowBatteryToneVolume() const;\n    /** Sets the low-battery warn-tone volume. */\n    void setLowBatteryToneVolume(unsigned int vol);\n\n    /** Returns the long-press duration. */\n    Interval longPressDuration() const;\n    /** Sets the long-press duration. */\n    void setLongPressDuration(const Interval &interval);\n    /** Long-press function of programmable key 1. */\n    RadioddityButtonSettingsExtension::Function keyFunctionLongPressP1() const;\n    /** Sets the long-press function of the programmable key 1. */\n    void setKeyFunctionLongPressP1(RadioddityButtonSettingsExtension::Function function);\n    /** Short-press function of programmable key 1. */\n    RadioddityButtonSettingsExtension::Function keyFunctionShortPressP1() const;\n    /** Sets the short-press function of the programmable key 1. */\n    void setKeyFunctionShortPressP1(RadioddityButtonSettingsExtension::Function function);\n    /** Long-press function of programmable key 2. */\n    RadioddityButtonSettingsExtension::Function keyFunctionLongPressP2() const;\n    /** Sets the long-press function of the programmable key 2. */\n    void setKeyFunctionLongPressP2(RadioddityButtonSettingsExtension::Function function);\n    /** Short-press function of programmable key 2. */\n    RadioddityButtonSettingsExtension::Function keyFunctionShortPressP2() const;\n    /** Sets the short-press function of the programmable key 2. */\n    void setKeyFunctionShortPressP2(RadioddityButtonSettingsExtension::Function function);\n    /** Returns the n-th one-touch setting. */\n    OneTouchSettingElement oneTouch(unsigned int n);\n\n    /** Updates the given config. */\n    bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the settings from the given config */\n    bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits of the settings. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int name() { return 16; }\n      /** Valid VOX sensitivity levels. */\n      static constexpr Range<unsigned int> vox() { return {1, 4}; }\n      /** Valid squelch sensitivity levels. */\n      static constexpr Range<unsigned int> squelch() { return {1, 9}; }\n      /** Valid range for mic gains. */\n      static constexpr Range<unsigned int> micGain() { return {1, 6}; }\n\n      /** Transmit time-out range. */\n      static constexpr TimeRange tot() {\n        return TimeRange{Interval::fromSeconds(20), Interval::fromSeconds(500)};\n      }\n      /** Power-save timeout. */\n      static constexpr TimeRange powerSaveTimeout() {\n        return TimeRange{Interval::fromSeconds(10), Interval::fromSeconds(60)};\n      }\n      /** Maximum read/write lock pin size. */\n      static constexpr unsigned int pin() { return 6; }\n      /** Lone-worker response time-out range. */\n      static constexpr TimeRange loneWorkerResponse() {\n        return TimeRange{Interval::fromMinutes(1), Interval::fromMinutes(480)};\n      }\n      /** Lone-worker remind period range. */\n      static constexpr TimeRange loneWorkerRemindPeriod() {\n        return TimeRange{Interval::fromSeconds(10), Interval::fromSeconds(200)};\n      }\n      /** Maximum length of the boot text lines. */\n      static constexpr unsigned int bootTextLine() { return 16; }\n      /** Value range for tone-volumes. */\n      static constexpr Range<unsigned int> toneVolume() { return {0,13}; }\n      /** Long-press duration range. */\n      static constexpr TimeRange longPressDuration() {\n        return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(31)};\n      }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int dmrId()                                 { return 0x0020; }\n      static constexpr unsigned int language()                              { return 0x0024; }\n      static constexpr unsigned int voxLevel()                              { return 0x0026; }\n      static constexpr unsigned int squelchLevel()                          { return 0x0027; }\n      static constexpr unsigned int tot()                                   { return 0x0028; }\n      static constexpr unsigned int txInterrupt()                           { return 0x0029; }\n      static constexpr unsigned int powerSave()                             { return 0x002a; }\n      static constexpr unsigned int powerSaveTimeout()                      { return 0x002b; }\n      static constexpr unsigned int readLockEnable()                        { return 0x002c; }\n      static constexpr unsigned int writeLockEnable()                       { return 0x002d; }\n      static constexpr unsigned int channelDisplayMode()                    { return 0x002f; }\n      static constexpr unsigned int readLockPin()                           { return 0x0030; }\n      static constexpr unsigned int writeLockPin()                          { return 0x0036; }\n      static constexpr unsigned int dmrMicGain()                            { return 0x003d; }\n      static constexpr unsigned int fmMicGain()                             { return 0x003f; }\n      static constexpr unsigned int loneWorkerResponseTimeout()             { return 0x0040; }\n      static constexpr unsigned int loneWorkerReminderPeriod()              { return 0x0042; }\n      static constexpr unsigned int bootDisplayMode()                       { return 0x0043; }\n      static constexpr unsigned int bootTextLine1()                         { return 0x0044; }\n      static constexpr unsigned int bootTextLine2()                         { return 0x0064; }\n      static constexpr unsigned int keyToneEnable()                         { return 0x0084; }\n      static constexpr unsigned int keyToneVolume()                         { return 0x0085; }\n      static constexpr unsigned int lowBatToneEnable()                      { return 0x0086; }\n      static constexpr unsigned int lowBatToneVolume()                      { return 0x0087; }\n      static constexpr unsigned int longPressDuration()                     { return 0x0088; }\n      static constexpr unsigned int progFuncKey1ShortPress()                { return 0x008b; }\n      static constexpr unsigned int progFuncKey1LongPress()                 { return 0x008c; }\n      static constexpr unsigned int progFuncKey2ShortPress()                { return 0x008d; }\n      static constexpr unsigned int progFuncKey2LongPress()                 { return 0x008e; }\n      static constexpr unsigned int oneTouchSettings()                      { return 0x0090; }\n      static constexpr unsigned int betweenOneTouchSettings()               { return OneTouchSettingElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a single zone within the binary codeplug.\n   *\n   * Memory representation of the zone (size 0031h):\n   * @verbinclude gd73_zone_element.txt */\n  class ZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneElement(uint8_t *ptr);\n\n    /** Returns the size of the zone element. */\n    static constexpr unsigned int size() { return 0x0031; }\n\n    /** Returns the name of the zone. */\n    QString name() const;\n    /** Sets the name of the zone. */\n    void setName(const QString &name);\n\n    /** Decodes the zone element. */\n    Zone *toZone(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the decoded zone */\n    bool linkZone(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the given zone. */\n    bool encode(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength()                            { return 8; }\n      /** Maximum number of channels per zone. */\n      static constexpr unsigned int channelCount()                          { return 16; }\n    };\n\n  protected:\n    /** Internal offsets within the zone element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int channeCount()                           { return 0x0010; }\n      static constexpr unsigned int channelIndices()                        { return 0x0011; }\n      static constexpr unsigned int betweenChannelIndices()                 { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of zones.\n   *\n   * See also @c GD73Codeplug::ZoneElement.\n   *\n   * Memory representation of the zone bank (size ????h bytes):\n   * @verbinclude gd73_zone_bank.txt */\n  class ZoneBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneBankElement(uint8_t *ptr);\n\n    /** Returns the size of the zone bank element. */\n    static constexpr unsigned int size() { return 0x0c41; }\n\n    /** Creates all encoded zones, also updates the context. */\n    bool createZones(Context &ctx, const ErrorStack &err);\n    /** Links all decoded zones. */\n    bool linkZones(Context &ctx, const ErrorStack &err);\n    /** Encodess all zones. */\n    bool encode(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits for the zone bank. */\n    struct Limit {\n      /** Maximum number of zones. */\n      static constexpr unsigned int zoneCount()                             { return 64; }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int zoneCount()                             { return 0x0000; }\n      static constexpr unsigned int zones()                                 { return 0x0001; }\n      static constexpr unsigned int betweenZones()                          { return ZoneElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements an FM/DMR channel.\n   *\n   * Memory representation of the channel (size ):\n   * @verbinclude gd73_channel_element.txt */\n  class ChannelElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Possible channel types. */\n    enum class Type {\n      FM = 0, DMR = 1\n    };\n    /** Possible admit criteria. */\n    enum class Admit {\n      Always = 0, CC_CTCSS = 1, Free=2\n    };\n\n  public:\n    /** Constructor. */\n    ChannelElement(uint8_t *ptr);\n\n    /** Returns the size of the channel element. */\n    static constexpr unsigned int size() { return 0x0046; }\n\n    /** Returns the name of the channel. */\n    QString name() const;\n    /** Sets the channel name. */\n    void setName(const QString &name);\n\n    /** Returns the bandwidth of the channel. */\n    FMChannel::Bandwidth bandwidth() const;\n    /** Sets the bandwidth. */\n    void setBandwidth(FMChannel::Bandwidth bandwidth);\n\n    /** Returns @c true, if a scan list index is set. */\n    bool hasScanListIndex() const;\n    /** Returns the index of the scan list. */\n    unsigned int scanListIndex() const;\n    /** Sets the scan list index. */\n    void setScanListIndex(unsigned int idx);\n    /** Clears the scan list index. */\n    void clearScanListIndex();\n\n    /** Returns the channel type. */\n    Type type() const;\n    /** Sets the channel type. */\n    void setType(Type type);\n\n    /** Returns @c true if talkaround is enabled. */\n    bool talkaroundEnabled() const;\n    /** Enable/disable talkaround. */\n    void enableTalkaround(bool enable);\n\n    /** Returns @c true if RX only is enabled. */\n    bool rxOnly() const;\n    /** Enables/disables RX only. */\n    void enableRXOnly(bool enable);\n\n    /** Returns @c true if scan auto-start is enabled. */\n    bool scanAutoStartEnabled() const;\n    /** Enables/disables scan auto-start. */\n    void enableScanAutoStart(bool enable);\n\n    /** Returns the RX frequency. */\n    Frequency rxFrequency() const;\n    /** Sets the RX frequency. */\n    void setRXFrequency(const Frequency &f);\n    /** Returns the TX frequency. */\n    Frequency txFrequency() const;\n    /** Sets the TX frequency. */\n    void setTXFrequency(const Frequency &f);\n\n    /** Returns @c true if channel has DTMF PTT settings index. */\n    bool hasDTMFPTTSettingsIndex() const;\n    /** Returns the DTMF PTT settings index. */\n    unsigned int dtmfPTTSettingsIndex() const;\n    /** Sets the DTMF PTT settings index. */\n    void setDTMFPTTSettingsIndex(unsigned int idx);\n    /** Resets the DTMF PTT settings index. */\n    void clearDTMFPTTSettingsIndex();\n\n\n    /** Returns the power setting. */\n    Channel::Power power() const;\n    /** Sets the power. */\n    void setPower(Channel::Power power);\n\n    /** Returns the admit criterion. */\n    Admit admit() const;\n    /** Sets the admit criterion. */\n    void setAdmit(Admit admit);\n\n    /** Returns the RX tone. */\n    SelectiveCall rxTone() const;\n    /** Sets the RX tone. */\n    void setRXTone(const SelectiveCall &code);\n    /** Returns the TX tone. */\n    SelectiveCall txTone() const;\n    /** Sets the TX tone. */\n    void setTXTone(const SelectiveCall &code);\n\n    /** Returns the time slot. */\n    DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot. */\n    void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns the color code. */\n    unsigned int colorCode() const;\n    /** Sets the color code. */\n    void setColorCode(unsigned int cc);\n\n    /** Returns @c true, if group list matches current TX contact. */\n    bool groupListMatchesContact() const;\n    /** Returns @c true, if no group list match is needed (monitor). */\n    bool groupListAllMatch() const;\n    /** Returns the group list index. */\n    unsigned int groupListIndex() const;\n    /** Sets the group list index. */\n    void setGroupListIndex(unsigned int idx);\n    /** Enables, that no group list match is needed (monitor). */\n    void setGroupListAllMatch();\n    /** Enables, that the group list matches the current TX contact. */\n    void setGroupListMatchesContact();\n\n    /** Returns @c true, if the transmit contact is set. */\n    bool hasTXContact() const;\n    /** Returns the tx contact index. */\n    unsigned int txContactIndex() const;\n    /** Sets the transmit contact index. */\n    void setTXContactIndex(unsigned int idx);\n    /** Clears the transmit contact index. */\n    void clearTXContactIndex();\n\n    /** Returns @c true if an emergency system index is set. */\n    bool hasEmergencySystemIndex() const;\n    /** Returns the emergency system index. */\n    unsigned int emergencySystemIndex() const;\n    /** Sets the emergency system index. */\n    void setEmergencySystemIndex(unsigned int idx);\n    /** Clears the emergency system index. */\n    void clearEmergencySystemIndex();\n\n    /** Returns @c true if an encryption key index is set. */\n    bool hasEncryptionKeyIndex() const;\n    /** Returns the encryption key index. */\n    unsigned int encryptionKeyIndex() const;\n    /** Sets the encryption key index. */\n    void setEncryptionKeyIndex(unsigned int idx);\n    /** Clears the encryption key index. */\n    void clearEncryptionKeyIndex();\n\n    /** Decodes the channel. */\n    Channel *toChannel(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links decoded channel. */\n    bool linkChannel(Channel *ch, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the given channel. */\n    bool encode(Channel *ch, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the channel. */\n    struct Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength()                            { return 16; }\n    };\n\n  protected:\n    /** Internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int bandwidth()                             { return 0x0020; }\n      static constexpr unsigned int scanList()                              { return 0x0021; }\n      static constexpr unsigned int channelType()                           { return 0x0022; }\n      static constexpr unsigned int talkaround()                            { return 0x0023; }\n      static constexpr unsigned int rxOnly()                                { return 0x0024; }\n      static constexpr unsigned int scanAutoStart()                         { return 0x0026; }\n      static constexpr unsigned int rxFrequency()                           { return 0x0027; }\n      static constexpr unsigned int txFrequency()                           { return 0x002b; }\n      static constexpr unsigned int dtmfPTTSettingsIndex()                  { return 0x002f; }\n      static constexpr unsigned int power()                                 { return 0x0030; }\n      static constexpr unsigned int admid()                                 { return 0x0031; }\n      static constexpr unsigned int rxToneMode()                            { return 0x0034; }\n      static constexpr unsigned int rxCTCSS()                               { return 0x0035; }\n      static constexpr unsigned int rxDCS()                                 { return 0x0036; }\n      static constexpr unsigned int txToneMode()                            { return 0x0037; }\n      static constexpr unsigned int txCTCSS()                               { return 0x0038; }\n      static constexpr unsigned int txDCS()                                 { return 0x0039; }\n      static constexpr unsigned int timeslot()                              { return 0x003c; }\n      static constexpr unsigned int colorcode()                             { return 0x003d; }\n      static constexpr unsigned int groupListIndex()                        { return 0x003e; }\n      static constexpr unsigned int contactIndex()                          { return 0x0040; }\n      static constexpr unsigned int emergencySystemIndex()                  { return 0x0042; }\n      static constexpr unsigned int encryptionKeyIndex()                    { return 0x0044; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of channels within the binary codeplug.\n   *\n   * See @c GD73Codeplug::ChannelElement for details on how the channels are encoded.\n   *\n   * Memory representation of the channel bank (size 11802h bytes):\n   * @verbinclude gd73_channel_bank.txt */\n  class ChannelBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ChannelBankElement(uint8_t *ptr);\n\n    /** Returns the size of the channel bank. */\n    static constexpr unsigned int size() { return 0x11802; }\n\n    /** Creates the encoded channels, also updates context. */\n    bool createChannels(Context &ctx, const ErrorStack &err);\n    /** Link all decoded channels. */\n    bool linkChannels(Context &ctx, const ErrorStack &err);\n    /** Encodes all indexed channels. */\n    bool encode(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits for the channel bank. */\n    struct Limit {\n      /** Maximum number of channels. */\n      static constexpr unsigned int channelCount()                          { return 1024; }\n    };\n\n  protected:\n    /** Internal offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int channelCount()                          { return 0x0000; }\n      static constexpr unsigned int channels()                              { return 0x0002; }\n      static constexpr unsigned int betweenChannels()                       { return ChannelElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the contact element.\n   *\n   * Memory representation of the contact (size 9c02h bytes):\n   * @verbinclude gd73_contact_bank.txt */\n  class ContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x025; }\n\n    /** Returns the name of the contact. */\n    QString name() const;\n    /** Sets the name of the contact. */\n    void setName(const QString &name);\n\n    /** Returns the contact type. */\n    DMRContact::Type type() const;\n    /** Sets the contact type. */\n    void setType(DMRContact::Type type);\n\n    /** Returns the DMR ID. */\n    unsigned int id() const;\n    /** Sets the DMR ID. */\n    void setID(unsigned int id);\n\n    /** Decodes the contact. */\n    DMRContact *toContact(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the given contact. */\n    bool encode(const DMRContact *contact, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength()                            { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the contact. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int type()                                  { return 0x0020; }\n      static constexpr unsigned int id()                                    { return 0x0021; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the contact bank within the codeplug.\n   *\n   * See @c GD73Codeplug::ContactElement for contact encoding.\n   *\n   * Memory representation of the contact bank (size 9c02h bytes):\n   * @verbinclude gd73_contact_bank.txt */\n  class ContactBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x9c02; }\n\n    /** Adds all encoded contacts, also updates the context */\n    bool createContacts(Context &ctx, const ErrorStack &err);\n    /** Encodes all defined contacts. */\n    bool encode(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of contacts. */\n      static constexpr unsigned int contactCount()                          { return 1024; }\n    };\n\n  protected:\n    /** Some internal offsets within the contact bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contactCount()                          { return 0x0000; }\n      static constexpr unsigned int contacts()                              { return 0x0802; }\n      static constexpr unsigned int betweenContacts()                       { return ContactElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Encodes a group list.\n   *\n   * Memory representation of the group list (size 0053h bytes):\n   * @verbinclude gd73_group_list_element.txt */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0053; }\n\n    /** Returns the name of the group list. */\n    QString name() const;\n    /** Sets the name of the group list. */\n    void setName(const QString &name);\n\n    /** Returns the number of entries in the group list. */\n    unsigned int members() const;\n    /** Returns @c true, if the i-th member is set. */\n    bool hasMember(unsigned int i) const;\n    /** Returns the i-th member index. */\n    unsigned int memberIndex(unsigned int i) const;\n\n    /** Decodes the group list. */\n    RXGroupList *toGroupList(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the given RX group list. */\n    bool linkGroupList(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the group list. */\n    bool encode(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength()                          { return 8; }\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount()                         { return 33; }\n    };\n\n  protected:\n    /** Some internal offsets within the group list. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int memberCount()                           { return 0x0010; }\n      static constexpr unsigned int members()                               { return 0x0011; }\n      static constexpr unsigned int betweenMembers()                        { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Encodes the bank of group lists.\n   *\n   * See @c GD73Codeplug::GroupListElement for group list encoding.\n   *\n   * Memory representation of group list bank (size 510fh bytes):\n   * @verbinclude gd73_group_list_bank.txt */\n  class GroupListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x510f; }\n\n    /** Create all encoded group lists, also update context. */\n    bool createGroupLists(Context &ctx, const ErrorStack &err);\n    /** Link all decoded group lists. */\n    bool linkGroupLists(Context &ctx, const ErrorStack &err);\n    /** Encode group lists. */\n    bool encode(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount()                         { return 250; }\n    };\n\n  protected:\n    /** Some internal offsets within the group list bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int memberCount()                           { return 0x0000; }\n      static constexpr unsigned int members()                               { return 0x0001; }\n      static constexpr unsigned int betweenMembers()                        { return GroupListElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a scan list.\n   *\n   * Memory representation of the scan list (size 005fh bytes):\n   * @verbinclude gd73_scan_list_element.txt */\n  class ScanListElement: public Element\n  {\n  public:\n    /** Possible priority/revert channel modes. */\n    enum class ChannelMode {\n      None=0, Fixed=1, Selected=2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ScanListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x005f; }\n\n    /** Returns the name of the scan list. */\n    QString name() const;\n    /** Sets the name of the scan list. */\n    void setName(const QString &name);\n\n    /** Returns the primary channel mode. */\n    ChannelMode primaryChannelMode() const;\n    /** Sets the primary channel mode. */\n    void setPrimaryChannelMode(ChannelMode mode);\n    /** Returns @c true, if a primary zone is set. */\n    bool hasPrimaryZoneIndex() const;\n    /** Returns the primary zone index. */\n    unsigned int primaryZoneIndex() const;\n    /** Sets the primary zone index. */\n    void setPrimaryZoneIndex(unsigned int idx);\n    /** Clears the primary zone index. */\n    void clearPrimaryZoneIndex();\n    /** Returns @c true, if a primary channel is set. */\n    bool hasPrimaryChannelIndex() const;\n    /** Returns the primary channel index. */\n    unsigned int primaryChannelIndex() const;\n    /** Sets the primary channel index. */\n    void setPrimaryChannelIndex(unsigned int idx);\n    /** Clears the primary channel index. */\n    void clearPrimaryChannelIndex();\n\n    /** Returns the secondary channel mode. */\n    ChannelMode secondaryChannelMode() const;\n    /** Sets the secondary channel mode. */\n    void setSecondaryChannelMode(ChannelMode mode);\n    /** Returns @c true, if a secondary zone is set. */\n    bool hasSecondaryZoneIndex() const;\n    /** Returns the secondary zone index. */\n    unsigned int secondaryZoneIndex() const;\n    /** Sets the secondary zone index. */\n    void setSecondaryZoneIndex(unsigned int idx);\n    /** Clears the secondary zone index. */\n    void clearSecondaryZoneIndex();\n    /** Returns @c true, if a secondary channel is set. */\n    bool hasSecondaryChannelIndex() const;\n    /** Returns the secondary channel index. */\n    unsigned int secondaryChannelIndex() const;\n    /** Sets the secondary channel index. */\n    void setSecondaryChannelIndex(unsigned int idx);\n    /** Clears the secondary channel index. */\n    void clearSecondaryChannelIndex();\n\n    /** Returns the revert channel mode. */\n    ChannelMode revertChannelMode() const;\n    /** Sets the revert channel mode. */\n    void setRevertChannelMode(ChannelMode mode);\n    /** Returns @c true, if a revert zone is set. */\n    bool hasRevertZoneIndex() const;\n    /** Returns the revert zone index. */\n    unsigned int revertZoneIndex() const;\n    /** Sets the revert zone index. */\n    void setRevertZoneIndex(unsigned int idx);\n    /** Clears the revert zone index. */\n    void clearRevertZoneIndex();\n    /** Returns @c true, if a revert channel is set. */\n    bool hasRevertChannelIndex() const;\n    /** Returns the revert channel index. */\n    unsigned int revertChannelIndex() const;\n    /** Sets the revert channel index. */\n    void setRevertChannelIndex(unsigned int idx);\n    /** Clears the revert channel index. */\n    void clearRevertChannelIndex();\n\n    /** Returns the RX hold time. */\n    Interval rxHoldTime() const;\n    /** Sets the RX hold time. */\n    void setRXHoldTime(const Interval &interval);\n    /** Returns the TX hold time. */\n    Interval txHoldTime() const;\n    /** Sets the TX hold time. */\n    void setTXHoldTime(const Interval &interval);\n\n    /** Constructs a ScanList from this element. */\n    ScanList *toScanList(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links a decoded scan list. */\n    bool linkScanList(ScanList *lst, Context&ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the scan list. */\n    bool encode(ScanList *lst, Context&ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength()                          { return 8; }\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount()                         { return 32; }\n      /** The range of hold times. */\n      static TimeRange holdTime() {\n        return TimeRange{Interval::fromSeconds(0), Interval::fromSeconds(10)};\n      }\n    };\n\n  protected:\n    /** Some internal offsets within the scan list bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()                                  { return 0x0000; }\n      static constexpr unsigned int memberCount()                           { return 0x0010; }\n      static constexpr unsigned int members()                               { return 0x0011; }\n      static constexpr unsigned int betweenMembers()                        { return 0x0002; }\n      static constexpr unsigned int priChannel1Mode()                       { return 0x0051; }\n      static constexpr unsigned int priChannel2Mode()                       { return 0x0052; }\n      static constexpr unsigned int priChannel1Zone()                       { return 0x0053; }\n      static constexpr unsigned int priChannel2Zone()                       { return 0x0054; }\n      static constexpr unsigned int priChannel1Channel()                    { return 0x0055; }\n      static constexpr unsigned int priChannel2Channel()                    { return 0x0057; }\n      static constexpr unsigned int txChannelMode()                         { return 0x0059; }\n      static constexpr unsigned int txChannelZone()                         { return 0x005a; }\n      static constexpr unsigned int txChannelChannel()                      { return 0x005b; }\n      static constexpr unsigned int holdTime()                              { return 0x005d; }\n      static constexpr unsigned int txHoldTime()                            { return 0x005e; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of scan lists.\n   *\n   * See @c GD73Codeplug::ScanListElement for the encoding of the single scan lists.\n   *\n   * Memory representation of the scan list bank (size 0601h bytes):\n   * @verbinclude gd73_scan_list_bank.txt */\n  class ScanListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x601; }\n\n    /** Creates all encoded scan lists, also updates context. */\n    bool createScanLists(Context &ctx, const ErrorStack &err);\n    /** Links all decoded scan lists. */\n    bool linkScanLists(Context &ctx, const ErrorStack &err);\n    /** Encodes all scan lists. */\n    bool encode(Context &ctx, const ErrorStack &err);\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount()                           { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the scan list bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int memberCount()                           { return 0x0000; }\n      static constexpr unsigned int members()                               { return 0x0011; }\n      static constexpr unsigned int betweenMembers()                        { return ScanListElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the DMR settings element.\n   *\n   * Memory representation of the settings (size 000eh bytes):\n   * @verbinclude gd73_dmr_settings_element.txt */\n  class DMRSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DMRSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DMRSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x000e; }\n\n    /** Returns the call hang time (private & group). */\n    Interval callHangTime() const;\n    /** Sets the call hang time (private & group). */\n    void setCallHangTime(const Interval &intv);\n\n    /** Returns the active wait time. */\n    Interval activeWaitTime() const;\n    /** Sets the active wait time. */\n    void setActiveWaitTime(const Interval &interval);\n    /** Returns the number of active reties. */\n    unsigned int activeRetries() const;\n    /** Sets the number of active retries. */\n    void setActiveRetries(unsigned int count);\n\n    /** Returns the number of TX preambles. */\n    unsigned int txPreambles() const;\n    /** Sets the number of TX preambles. */\n    void setTXPreambles(unsigned int count);\n\n    /** Returns @c true, if decoding of 'disable radio' is enabled. */\n    bool decodeDisableRadioEnabled() const;\n    /** Enables/disables decoding of 'disable radio'. */\n    void enableDecodeDisableRadio(bool enable);\n    /** Returns @c true, if decoding of 'radio check' is enabled. */\n    bool decodeRadioCheckEnabled() const;\n    /** Enables/disables decoding of 'radio check'. */\n    void enableDecodeRadioCheck(bool enable);\n    /** Returns @c true, if decoding of 'enable radio' is enabled. */\n    bool decodeEnableRadioEnabled() const;\n    /** Enables/disables decoding of 'enable radio'. */\n    void enableDecodeEnableRadio(bool enable);\n\n    /** Updates the settings within the config. */\n    bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes the settings from the given config */\n    bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The range of call hang times. */\n      static constexpr TimeRange callHangTime() { return TimeRange{\n          Interval::fromSeconds(1), Interval::fromSeconds(90)\n        }; }\n      /** The range of active wait times. */\n      static constexpr TimeRange activeWaitTime() { return TimeRange{\n          Interval::fromMilliseconds(120), Interval::fromMilliseconds(600)\n        }; }\n      /** The range of active retries. */\n      static constexpr IntRange activeRetires() { return IntRange{ 1, 10}; }\n      /** The maximum number of TX preambles. */\n      static constexpr IntRange txPreambles() { return IntRange{ 0, 63}; }\n    };\n\n  protected:\n    /** Some internal offsets within the scan list bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int callHangTime()                          { return 0x0000; }\n      static constexpr unsigned int activeWaitTime()                        { return 0x0001; }\n      static constexpr unsigned int activeRetries()                         { return 0x0002; }\n      static constexpr unsigned int txPreambles()                           { return 0x0003; }\n      static constexpr unsigned int decodeDisableRadio()                    { return 0x0004; }\n      static constexpr unsigned int decodeCheckRadio()                      { return 0x0005; }\n      static constexpr unsigned int decodeEnableRadio()                     { return 0x0006; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the encryption key element.\n   *\n   * Memory representation (size 0005h bytes):\n   * @verbinclude gd73_encryption_key_element.txt */\n  class EncryptionKeyElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EncryptionKeyElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    EncryptionKeyElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x005; }\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the key size in bits. */\n    unsigned int keySize() const;\n    /** Sets the key size in bits. */\n    void setKeySize(unsigned int size);\n\n    /** Decodes the encryption key. */\n    BasicEncryptionKey *createEncryptionKey(const ErrorStack &err=ErrorStack()) const;\n    /** Encodes the encryption key. */\n    bool encodeEncryptionKey(BasicEncryptionKey *key, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Internal used offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int size()        { return 0x0000; }\n      static constexpr unsigned int key()         { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the encryption key-bank.\n   *\n   * Memory representation (size 0050h bytes):\n   * @verbinclude gd73_encryption_key_bank.txt */\n  class EncryptionKeyBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EncryptionKeyBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    EncryptionKeyBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0050; }\n\n    /** Decodes and create encryption keys. */\n    bool createEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Encodes encryption keys. */\n    bool encodeEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of keys. */\n      static constexpr unsigned int keys()        { return 16; }\n    };\n\n  protected:\n    /** Internal used offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int keys()        { return 0x0000; }\n      static constexpr unsigned int betweenKeys() { return EncryptionKeyElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a message.\n   * Memory representation (size 0051h bytes):\n   * @verbinclude gd73_message_element.txt */\n  class MessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x051; }\n\n    /** Returns the message text. */\n    QString text() const;\n    /** Set message text. */\n    void setText(const QString &message);\n\n    /** Sets a message element from an SMS message. */\n    bool encode(SMSTemplate *message, const ErrorStack &err=ErrorStack());\n    /** Creates a SMS template from this message. */\n    SMSTemplate *decode(const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum message length. */\n      static constexpr unsigned int messageLength()  { return 40; }\n    };\n\n  protected:\n    /** Internal used offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int size()        { return 0x0000; }\n      static constexpr unsigned int text()        { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the message bank element.\n   *\n   * See @c GD73Codeplug::MessageElement for encoding of the single messages.\n   *\n   * Memory representation of the bank (size 0511h bytes):\n   * @verbinclude gd73_message_bank.txt */\n  class MessageBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x511; }\n\n    /** Returns the member count. */\n    unsigned int memberCount() const;\n    /** Sets the member count. */\n    void setMemberCount(unsigned int count);\n\n    /** Returns the i-th message. */\n    MessageElement message(unsigned int i);\n\n    /** Updates the SMS extension by decoding all defined messages. */\n    bool decode(SMSExtension *ext, const ErrorStack &err=ErrorStack());\n    /** Encodes all messages defined within the SMS extension. */\n    bool encode(const SMSExtension *ext, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount()                           { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the message bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int memberCount()                           { return 0x0000; }\n      static constexpr unsigned int members()                               { return 0x0001; }\n      static constexpr unsigned int betweenMembers()                        { return MessageElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a single DTMF system.\n   * Memory representation of the system (size 0005h bytes):\n   * @verbinclude gd73_dtmf_system_element.txt */\n  class DTMFSystemElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFSystemElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFSystemElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x005; }\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum preamble duration in ms. */\n      static constexpr Interval preambleDuration() { return Interval::fromMilliseconds(1000); }\n      /** Range for tone duration. */\n      static constexpr TimeRange toneDuration() { return TimeRange{\n          Interval::fromMilliseconds(30), Interval::fromMilliseconds(1900)\n        }; }\n      /** Range for pause duration. */\n      static constexpr TimeRange pauseDuration() { return TimeRange{\n          Interval::fromMilliseconds(30), Interval::fromMilliseconds(1900)\n        }; }\n      /** Rang of dead time. */\n      static constexpr TimeRange deadTime() { return TimeRange{\n          Interval::fromMilliseconds(200), Interval::fromSeconds(33)\n        }; }\n    };\n\n  protected:\n    /** Internal used offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int sidetone()                              { return 0x0000; }\n      static constexpr unsigned int preambleDuration()                      { return 0x0001; }\n      static constexpr unsigned int toneDuration()                          { return 0x0002; }\n      static constexpr unsigned int pauseDuration()                         { return 0x0003; }\n      static constexpr unsigned int deadTime()                              { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of 4 DTMF systems.\n   * @c GD73Codeplug::DTMFSystemElement for encoding of each system. */\n  class DTMFSystemBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFSystemBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFSystemBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0014; }\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The number of members. */\n      static constexpr unsigned int memberCount()                           { return 4; }\n    };\n\n  protected:\n    /** Some internal offsets within the message bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members()                               { return 0x0000; }\n      static constexpr unsigned int betweenMembers()                        { return DTMFSystemElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a single DTMF number.\n   * Memory representation of the DTMF number (size 000ah bytes):\n   * @verbinclude gd73_dtmf_code_element.txt */\n  class DTMFNumberElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFNumberElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFNumberElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x000a; }\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** Maximum number of digita. */\n      static constexpr unsigned int digits() { return 16; }\n    };\n\n  protected:\n    /** Internal used offsets within the bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int count()                                 { return 0x0000; }\n      static constexpr unsigned int digits()                                { return 0x0001; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of 16 DTMF numbers.\n   * @c GD73Codeplug::DTMFNumberElement for encoding of each system. */\n  class DTMFNumberBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFNumberBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFNumberBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The number of members. */\n      static constexpr unsigned int memberCount()                           { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the number bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members()                               { return 0x0000; }\n      static constexpr unsigned int betweenMembers()                        { return DTMFNumberElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a single DTMF PTT setting.\n   * Memory representation of the DTMF PTT setting (size 0005h bytes):\n   * @verbinclude gd73_dtmf_ptt_settings.txt */\n  class DTMFPTTSettingElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFPTTSettingElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFPTTSettingElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0005; }\n\n  protected:\n    /** Internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int systemIndex()                           { return 0x0000; }\n      static constexpr unsigned int pttIDType()                             { return 0x0001; }\n      static constexpr unsigned int pttIDMode()                             { return 0x0002; }\n      static constexpr unsigned int connectIDIndex()                        { return 0x0003; }\n      static constexpr unsigned int disconnectIDIndex()                     { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the bank of 32 DTMF PTT settings.\n   * @c GD73Codeplug::DTMFPTTSettingElement for encoding of each element. */\n  class DTMFPTTSettingBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFPTTSettingBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFPTTSettingBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00a0; }\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The number of members. */\n      static constexpr unsigned int memberCount()                           { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets within the number bank. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int members()                               { return 0x0000; }\n      static constexpr unsigned int betweenMembers()                        { return DTMFPTTSettingElement::size(); }\n      /// @endcond\n    };\n  };\n\npublic:\n  /** Default constructor. */\n  explicit GD73Codeplug(QObject *parent = nullptr);\n\n  Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n  bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n  bool encode(Config *config, const Flags &flags=Flags(), const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Decodes the time-stamp field. */\n  virtual bool decodeTimestamp(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes the time-stamp field. */\n  virtual bool encodeTimestamp(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Creates messages. */\n  virtual bool createMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode messages. */\n  virtual bool encodeMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Decodes the settings fields (generic & DMR). */\n  virtual bool decodeSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode settings fields (generic & DMR settings). */\n  virtual bool encodeSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Creates contacts. */\n  virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode contacts. */\n  virtual bool encodeContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Creates DTMF contacts. */\n  virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Create group lists. */\n  virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link group lists. */\n  virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode group lists. */\n  virtual bool encodeGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Create encryption keys. */\n  virtual bool createEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode encryption keys. */\n  virtual bool encodeEncryptionKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Create channels. */\n  virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link channels. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode channels. */\n  virtual bool encodeChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Create zones. */\n  virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link zones. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode zones. */\n  virtual bool encodeZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Create scan lists. */\n  virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Link zones. */\n  virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encode zones. */\n  virtual bool encodeScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Internal used offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int timestamp()                               { return 0x00000; }\n    static constexpr unsigned int settings()                                { return 0x00061; }\n    static constexpr unsigned int zones()                                   { return 0x0010b; }\n    static constexpr unsigned int channels()                                { return 0x00d4c; }\n    static constexpr unsigned int contacts()                                { return 0x125ff; }\n    static constexpr unsigned int groupLists()                              { return 0x1c201; }\n    static constexpr unsigned int scanLists()                               { return 0x21310; }\n    static constexpr unsigned int dmrSettings()                             { return 0x21911; }\n    static constexpr unsigned int encryptionKeys()                          { return 0x2191f; }\n    static constexpr unsigned int messages()                                { return 0x2196f; }\n    static constexpr unsigned int dtmfSystems()                             { return 0x21e80; }\n    static constexpr unsigned int dtmfNumbers()                             { return 0x21e94; }\n    static constexpr unsigned int dtmfPTTSettings()                         { return 0x21f24; }\n    /// @endcond\n  };\n};\n\n\n#endif // GD73CODEPLUG_HH\n"
  },
  {
    "path": "lib/gd73_filereader.cc",
    "content": "#include \"gd73_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_ADDR 0x00000000\n#define SEGMENT0_SIZE 0x00022014\n\nbool\nGD73FileReader::read(const QString &filename, GD73Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (SEGMENT0_SIZE != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not \" << SEGMENT0_SIZE << \" bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/gd73_filereader.hh",
    "content": "#ifndef GD73FILEREADER_HH\n#define GD73FILEREADER_HH\n\n#include \"gd73_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug\n * data as written to the device. This makes the decoding of the manufacturer codeplug files very\n * easy. Some memory regions, however, are not written to the deivice although they are present in\n * the codeplug file.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th></tr>\n *  <tr><td>0x00000</td> <td>0x22149</td>  <td>0x22149</td></tr>\n * </table>\n *\n * @ingroup gd73 */\nclass GD73FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err The error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, GD73Codeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // GD73FILEREADER_HH\n"
  },
  {
    "path": "lib/gd73_interface.cc",
    "content": "#include \"gd73_interface.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n#define BLOCK_SIZE 0x35\n\nGD73Interface::GD73Interface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : C7000Device(descriptor, err, parent), RadioInterface()\n{\n  Packet request, response;\n  if (nullptr == _dev) {\n    errMsg(err) << \"Cannot initialize GD73 interface: C7000 interface not open.\";\n    return;\n  }\n\n  request = Packet(0x01, 0x04);\n  if (! sendRecv(request, response, err)) {\n    errMsg(err) << \"Cannot enter programming mode.\";\n    C7000Device::close();\n  }\n  logDebug() << \"Entered prog mode. Response: \" << response.payload().toHex() << \".\";\n}\n\nbool\nGD73Interface::isOpen() const {\n  return C7000Device::isOpen();\n}\n\nRadioInfo\nGD73Interface::identifier(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return RadioInfo::byID(RadioInfo::GD73);\n}\n\nvoid\nGD73Interface::close() {\n  C7000Device::close();\n}\n\nbool\nGD73Interface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err);\n  return true;\n}\n\nbool\nGD73Interface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  if ((addr%BLOCK_SIZE) || (nbytes!=BLOCK_SIZE)) {\n    errMsg(err) << \"Address and size must align with block size of 35h\";\n    return false;\n  }\n\n  C7000Device::Packet request, response;\n  QByteArray payload; payload.resize(2);\n  *((uint16_t *)payload.data()) = qToLittleEndian((uint16_t)(addr/BLOCK_SIZE));\n  payload.append((char *)data, nbytes);\n  request = C7000Device::Packet(0x01, 0x00, 0x0f, payload);\n  if (! sendRecv(request, response, err)) {\n    errMsg(err) << \"Cannot send write command.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nGD73Interface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return true;\n}\n\nbool\nGD73Interface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err);\n  _lastSequence = 0xffff;\n  logDebug() << \"Start codeplug read, seqnr=\" << Qt::hex << _lastSequence << \"h.\";\n  return true;\n}\n\nbool\nGD73Interface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  if ((addr%BLOCK_SIZE) || (nbytes!=BLOCK_SIZE)) {\n    errMsg(err) << \"Address and size must align with block size of 35h\";\n    return false;\n  }\n\n  //logDebug() << \"Read \" << nbytes << \"bytes from address \" << Qt::hex << addr << \"h.\";\n\n  uint16_t seqNum = addr/BLOCK_SIZE;\n  if (uint16_t(_lastSequence+1) != seqNum) {\n    errMsg(err) << \"Out-of-sequence read: Expected seqnr. \" << uint16_t(_lastSequence+1)\n                << \" got \" << seqNum << \".\";\n    return false;\n  }\n\n  C7000Device::Packet request, response;\n  if (0xffff == _lastSequence) {\n    // Request start-of-read\n    request = C7000Device::Packet(0x01, 0x02);\n  } else {\n    QByteArray payload(2,0); *((uint16_t *)payload.data()) = qToLittleEndian(_lastSequence);\n    request = C7000Device::Packet(0x04, 0x01, 0x0f, payload);\n  }\n\n  if (! sendRecv(request, response, err)) {\n    errMsg(err) << \"Cannot read codeplug from device.\";\n    return false;\n  }\n\n  _lastSequence = qFromLittleEndian(*(uint16_t *)response.payload().data());\n  memcpy(data, response.payload().data()+2, nbytes);\n\n  return true;\n}\n\nbool\nGD73Interface::read_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  _lastSequence = 0xffff;\n  return true;\n}\n\n"
  },
  {
    "path": "lib/gd73_interface.hh",
    "content": "#ifndef GD73INTERFACE_HH\n#define GD73INTERFACE_HH\n\n#include \"c7000device.hh\"\n#include \"radiointerface.hh\"\n\n/** Implements the communication interface to the GD-73.\n * @ingroup gd73 */\nclass GD73Interface: public C7000Device, public RadioInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new interface to GD73A/E radios. If a matching device was found, @c isOpen\n   * returns @c true. */\n  GD73Interface(const USBDeviceDescriptor &descriptor,\n                const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  bool isOpen() const;\n\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  void close();\n\nprotected:\n  /** Name of the radio. */\n  QString _identifier;\n  /** Last received/send sequence number. */\n  uint16_t _lastSequence;\n};\n\n#endif // GD73INTERFACE_HH\n"
  },
  {
    "path": "lib/gd73_limits.cc",
    "content": "#include \"gd73_limits.hh\"\n#include \"gd73_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"roamingzone.hh\"\n\n\nGD73Limits::GD73Limits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = false;\n  _callSignDBImplemented  = false;\n  _numCallSignDBEntries   = 0;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, GD73Codeplug::SettingsElement::Limit::bootTextLine(), RadioLimitString::Unicode) },\n        { \"introLine2\", new RadioLimitString(-1, GD73Codeplug::SettingsElement::Limit::bootTextLine(), RadioLimitString::Unicode) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitIgnored(RadioLimitIssue::Silent) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1, GD73Codeplug::SettingsElement::Limit::name(), RadioLimitString::Unicode) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n        /// @todo check default radio ID.\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, GD73Codeplug::ContactBankElement::Limit::contactCount(), new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, GD73Codeplug::ContactElement::Limit::nameLength(), RadioLimitString::Unicode) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                (unsigned)DMRContact::GroupCall,\n                (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, GD73Codeplug::GroupListBankElement::Limit::memberCount(), new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, GD73Codeplug::GroupListElement::Limit::nameLength(), RadioLimitString::Unicode) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, GD73Codeplug::GroupListElement::Limit::memberCount()) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, GD73Codeplug::ChannelBankElement::Limit::channelCount(),\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, GD73Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"openGD77\", new RadioLimitIgnored()},\n              {\"tyt\", new RadioLimitIgnored()}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, GD73Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, GD73Codeplug::ZoneBankElement::Limit::zoneCount(), new RadioLimitSingleZone(\n          GD73Codeplug::ZoneElement::Limit::channelCount(), {\n            { \"name\", new RadioLimitString(1, GD73Codeplug::ZoneElement::Limit::nameLength(), RadioLimitString::Unicode) },\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Check scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, GD73Codeplug::ScanListBankElement::Limit::memberCount(), new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, GD73Codeplug::ScanListElement::Limit::nameLength(), RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, GD73Codeplug::ScanListElement::Limit::memberCount(), Channel::staticMetaObject) }\n        }));\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList(\n         BasicEncryptionKey::staticMetaObject,\n         0, GD73Codeplug::EncryptionKeyBankElement::Limit::keys(),\n         new RadioLimitObject {\n           {\"name\", new RadioLimitIgnored()},\n           {\"key\", new RadioLimitStringRegEx(\"^[0-9a-fA-F]{2,8}$\", RadioLimitIssue::Critical)}\n         })}\n      });\n\n  /* Ignore positioning systems. */\n  add(\"positioning\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n\n}\n"
  },
  {
    "path": "lib/gd73_limits.hh",
    "content": "#ifndef GD73LIMITS_HH\n#define GD73LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the limits for the Radioddity GD77.\n * @ingroup gd73 */\nclass GD73Limits: public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit GD73Limits(QObject *parent=nullptr);\n};\n\n#endif // GD73LIMITS_HH\n"
  },
  {
    "path": "lib/gd77.cc",
    "content": "#include \"gd77.hh\"\n#include \"gd77_limits.hh\"\n\n#include \"logger.hh\"\n#include \"config.hh\"\n\n\n#define BSIZE           32\n\nRadioLimits * GD77::_limits = nullptr;\n\n\nGD77::GD77(RadioddityInterface *device, QObject *parent)\n  : RadioddityRadio(device, parent), _name(\"Radioddity GD-77\"), _codeplug(), _callsigns()\n{\n  // pass...\n}\n\nconst QString &\nGD77::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nGD77::limits() const {\n  if (nullptr == _limits)\n    _limits = new GD77Limits();\n  return *_limits;\n}\n\nconst Codeplug &\nGD77::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nGD77::codeplug() {\n  return _codeplug;\n}\n\nRadioInfo\nGD77::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::GD77, \"gd77\", \"GD-77\", \"Radioddity\", {RadioddityInterface::interfaceInfo()});\n}\n\n\nbool\nGD77::startUploadCallsignDB(UserDatabase *db, bool blocking, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  logDebug() << \"Start call-sign DB upload to \" << name() << \"...\";\n  _errorStack = err;\n\n  if (StatusIdle != _task) {\n    errMsg(err) << \"Cannot upload to radio, radio is not idle.\";\n    return false;\n  }\n\n  // Assemble call-sign db from user DB\n  logDebug() << \"Encode call-signs into db.\";\n  _callsigns.encode(db, selection);\n\n  _task = StatusUploadCallsigns;\n  if (blocking) {\n    logDebug() << \"Upload call-sign DB in this thread (blocking).\";\n    run();\n    return (StatusIdle == _task);\n  }\n\n  //if (_dev && _dev->isOpen())\n  // _dev->moveToThread(this);\n\n  // start thread for upload\n  logDebug() << \"Upload call-sign DB in separate thread.\";\n  start();\n\n  return true;\n}\n\nbool\nGD77::uploadCallsigns()\n{\n  emit uploadStarted();\n\n  // Check every segment in the codeplug\n  if (! _callsigns.isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot upload call-sign DB: Not aligned with block-size \" << BSIZE << \".\";\n    return false;\n  }\n\n  logDebug() << \"Call-sign DB upload started...\";\n\n  size_t totb = _callsigns.memSize();\n  unsigned bcount = 0;\n  for (int n=0; n<_callsigns.image(0).numElements(); n++) {\n    unsigned addr = _callsigns.image(0).element(n).address();\n    unsigned size = _callsigns.image(0).element(n).data().size();\n    unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n    for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n      RadioddityInterface::MemoryBank bank = (\n            (0x10000 > (b0+b)*BSIZE) ? RadioddityInterface::MEMBANK_CALLSIGN_LOWER : RadioddityInterface::MEMBANK_CALLSIGN_UPPER );\n      if (! _dev->write(bank, ((b0+b)*BSIZE)&0xffff,\n                        _callsigns.data((b0+b)*BSIZE, 0), BSIZE, _errorStack))\n      {\n        errMsg(_errorStack) << \"Cannot write block \" << (b0+b) << \".\";\n        return false;\n      }\n      emit uploadProgress(float(bcount*100)/totb);\n    }\n  }\n\n\n  _dev->write_finish();\n  return true;\n}\n\n\n"
  },
  {
    "path": "lib/gd77.hh",
    "content": "/** @defgroup gd77 Radioddity GD-77\n * Device specific classes for Radioddity GD-77 and GD-77S.\n *\n * \\image html gd77.jpg \"GD-77\" width=200px\n * \\image latex gd77.jpg \"GD-77\" width=200px\n *\n * @ingroup radioddity */\n\n#ifndef GD77_HH\n#define GD77_HH\n\n#include \"radioddity_radio.hh\"\n#include \"radioddity_interface.hh\"\n#include \"gd77_codeplug.hh\"\n#include \"gd77_callsigndb.hh\"\n\n\n/** Implements an USB interface to the Radioddity GD-77(S) VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * @ingroup gd77 */\nclass GD77 : public RadioddityRadio\n{\n\tQ_OBJECT\n\npublic:\n\t/** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit GD77(RadioddityInterface *device=nullptr, QObject *parent=nullptr);\n\n\tconst QString &name() const;\n  const RadioLimits &limits() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\npublic slots:\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, bool blocking=false,\n                             const CallsignDB::Flags &selection=CallsignDB::Flags(),\n                             const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Implements the actual callsign DB upload process. */\n  bool uploadCallsigns();\n\nprotected:\n  /** The device identifier. */\n\tQString _name;\n  /** The codeplug. */\n  GD77Codeplug _codeplug;\n  /** The actual binary callsign DB representation. */\n  GD77CallsignDB _callsigns;\n\nprivate:\n  /** Holds the singleton instance of the radio limits for this radio. */\n  static RadioLimits *_limits;\n};\n\n#endif // GD77_HH\n"
  },
  {
    "path": "lib/gd77_callsigndb.cc",
    "content": "#include \"gd77_callsigndb.hh\"\n#include \"utils.hh\"\n#include \"userdatabase.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n#define OFFSET_USERDB       0x00000\n\n#define USERDB_MAX_ENTRIES  10920\n#define USERDB_MAX_ENTRIES_PER_BANK 5460\n#define BLOCK_SIZE          32\n\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77CallsignDB::userdb_entry_t\n * ******************************************************************************************** */\nGD77CallsignDB::userdb_entry_t::userdb_entry_t() {\n  clear();\n}\n\nvoid\nGD77CallsignDB::userdb_entry_t::clear() {\n  memset(this, 0, sizeof(userdb_entry_t));\n}\n\nuint32_t\nGD77CallsignDB::userdb_entry_t::getNumber() const {\n  return decode_dmr_id_bcd_le((uint8_t *)&number);\n}\nvoid\nGD77CallsignDB::userdb_entry_t::setNumber(uint32_t number) {\n  encode_dmr_id_bcd_le((uint8_t *)&(this->number), number);\n}\n\nQString\nGD77CallsignDB::userdb_entry_t::getName() const {\n  return decode_ascii((const uint8_t *)name, 7, 0x00);\n}\nvoid\nGD77CallsignDB::userdb_entry_t::setName(const QString &name) {\n  encode_ascii((uint8_t *)(this->name), name, 7, 0x00);\n}\n\nvoid\nGD77CallsignDB::userdb_entry_t::fromEntry(const UserDatabase::User &user) {\n  clear();\n  setNumber(user.id);\n  setName(user.call);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77CallsignDB::userdb_t\n * ******************************************************************************************** */\nGD77CallsignDB::userdb_t::userdb_t() {\n  clear();\n}\n\nvoid\nGD77CallsignDB::userdb_t::clear() {\n  memcpy(magic, \"ID-V001\\0\", 8);\n  count = 0;\n}\n\nvoid\nGD77CallsignDB::userdb_t::setSize(uint32_t n) {\n  count = qToLittleEndian(std::min(n, uint32_t(USERDB_MAX_ENTRIES)));\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77CallsignDB\n * ******************************************************************************************** */\nGD77CallsignDB::GD77CallsignDB(QObject *parent)\n  : CallsignDB(parent)\n{\n  addImage(\"GD77 call-sign database\");\n}\n\nGD77CallsignDB::~GD77CallsignDB() {\n  // pass...\n}\n\nbool\nGD77CallsignDB::encode(UserDatabase *calldb, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Limit entries to USERDB_NUM_ENTRIES\n  qint64 n = std::min(calldb->count(), qint64(USERDB_MAX_ENTRIES));\n  if (selection.hasCountLimit())\n    n = std::min(n, (qint64)selection.countLimit());\n  // If there are no entries -> done.\n  if (0 == n)\n    return true;\n\n  // Select first n entries and sort them in ascending order of their IDs\n  logDebug() << \"Select first \" << n << \" entries out off \" << calldb->count() << \".\";\n  QVector<UserDatabase::User> users;\n  for (unsigned i=0; i<n; i++)\n    users.append(calldb->user(i));\n  logDebug() << \"Sort selected w.r.t their ID in ascending order.\";\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n\n  // Allocate segment for user db if requested\n  size_t size = align_size(sizeof(userdb_t)+n*sizeof(userdb_entry_t), BLOCK_SIZE);\n  logDebug() << \"Allocate 0x\" << QString::number(size,16) << \" bytes for call-sign DB.\";\n  this->image(0).addElement(OFFSET_USERDB, size);\n\n  // Encode user DB\n  userdb_t *userdb = (userdb_t *)this->data(OFFSET_USERDB);\n  userdb->clear(); userdb->setSize(n);\n  userdb_entry_t *db = (userdb_entry_t *)this->data(OFFSET_USERDB+sizeof(userdb_t), 0);\n  for (unsigned i=0; i<n; i++) {\n    db[i].fromEntry(users[i]);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/gd77_callsigndb.hh",
    "content": "#ifndef GD77CALLSIGNDB_HH\n#define GD77CALLSIGNDB_HH\n\n#include \"callsigndb.hh\"\n#include \"userdatabase.hh\"\n\n/** Represents and encodes the binary format for the call-sign database within the radio.\n *\n * The call-sign DB gets stored at a separate memory bank (no. 3) at address @c 0x0000. The encoded\n * DB consists of the DB header followed by the DB entries. The number of entries is limited to\n * 10920 leading to a max DB size of 0x20000 bytes. These 0x20000 bytes are organized into two banks\n * bank 3 contains 0x00000-0x0ffff and bank 4 contains 0x10000-0x1ffff.\n *\n * The header is encoded as:\n * @verbinclude gd77_callsign_db_header.txt\n * The number of DB entries is encoded as a 32bit little-endian integer.\n *\n * Each DB entry is encoded as:\n * @verbinclude gd77_callsign_db_entry.txt\n * The DMR ID gets encoded a 8 BCD digits in little endian while the name is encoded as up-to 8\n * ASCII bytes 0-terminated and padded.\n *\n * @ingroup gd77 */\nclass GD77CallsignDB : public CallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Represents a user-db entry within the binary codeplug. */\n  struct __attribute__((packed)) userdb_entry_t {\n    uint32_t number;                    ///< DMR ID stored in BCD little-endian.\n    char name[8];                       ///< Call or name, up to 8 ASCII chars, 0x00 padded.\n\n    /** Constructor. */\n    userdb_entry_t();\n    /** Resets the entry. */\n    void clear();\n\n    /** Returns the DMR ID for the entry. */\n    uint32_t getNumber() const;\n    /** Sets the DMR ID for the entry. */\n    void setNumber(uint32_t number);\n\n    /** Returns the name of the entry. */\n    QString getName() const;\n    /** Sets the name of the entry. */\n    void setName(const QString &name);\n\n    /** Constructs an entry from the given user. */\n    void fromEntry(const UserDatabase::User &user);\n  };\n\n  /** Represents the binary call-sign database header.\n   *\n   * Memory representation of the call-sign DB header (size: 0x0c bytes):\n   * @verbinclude gd77_callsign_db_header.txt\n   **/\n  struct __attribute__((packed)) userdb_t {\n    char magic[8];                      ///< Fixed string 'ID-V001\\0'\n    uint32_t count;                     ///< Number of contacts in DB, 32bit little-endian.\n\n    /** Constructor. */\n    userdb_t();\n    /** Resets the header. */\n    void clear();\n\n    /** Sets the number of entries in the call-sign DB. */\n    void setSize(uint32_t n);\n  };\n\n\npublic:\n  /** Constructor. */\n  explicit GD77CallsignDB(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~GD77CallsignDB();\n\n  /** Encodes as many entries as possible of the given user-database. */\n  virtual bool encode(UserDatabase *calldb, const Flags &selection=Flags(),\n                      const ErrorStack &err=ErrorStack());\n};\n\n#endif // GD77CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/gd77_codeplug.cc",
    "content": "#include \"gd77_codeplug.hh\"\n#include \"config.hh\"\n#include \"channel.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include <QDateTime>\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::ChannelElement\n * ******************************************************************************************** */\nGD77Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : RadioddityCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : RadioddityCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\nvoid\nGD77Codeplug::ChannelElement::clear() {\n  RadioddityCodeplug::ChannelElement::clear();\n  setUInt8(0x0028, 0x00);\n  setARTSMode(ARTS_OFF);\n  setSTEAngle(STE_FREQUENCY);\n}\n\nGD77Codeplug::ChannelElement::ARTSMode\nGD77Codeplug::ChannelElement::artsMode() const {\n  return ARTSMode(getUInt2(0x0030,0));\n}\nvoid\nGD77Codeplug::ChannelElement::setARTSMode(ARTSMode mode) {\n  setUInt2(0x0030, 0, (unsigned)mode);\n}\n\nGD77Codeplug::ChannelElement::STEAngle\nGD77Codeplug::ChannelElement::steAngle() const {\n  return STEAngle(getUInt2(0x0032,6));\n}\nvoid\nGD77Codeplug::ChannelElement::setSTEAngle(STEAngle angle) {\n  setUInt2(0x0032, 6, (unsigned)angle);\n}\n\nGD77Codeplug::ChannelElement::PTTId\nGD77Codeplug::ChannelElement::pttIDMode() const {\n  return PTTId(getUInt2(0x0032, 2));\n}\nvoid\nGD77Codeplug::ChannelElement::setPTTIDMode(PTTId mode) {\n  setUInt2(0x0032, 2, (unsigned)mode);\n}\n\nbool\nGD77Codeplug::ChannelElement::squelchIsTight() const {\n  return getBit(0x0033, 0);\n}\nvoid\nGD77Codeplug::ChannelElement::enableTightSquelch(bool enable) {\n  setBit(0x0033, 0, enable);\n}\n\nbool\nGD77Codeplug::ChannelElement::loneWorker() const {\n  return getBit(0x0033, 4);\n}\nvoid\nGD77Codeplug::ChannelElement::enableLoneWorker(bool enable) {\n  setBit(0x0033, 4, enable);\n}\n\nbool\nGD77Codeplug::ChannelElement::autoscan() const {\n  return getBit(0x0033, 5);\n}\nvoid\nGD77Codeplug::ChannelElement::enableAutoscan(bool enable) {\n  setBit(0x0033, 5, enable);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::ContactElement\n * ******************************************************************************************** */\nGD77Codeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size)\n  : RadioddityCodeplug::ContactElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : RadioddityCodeplug::ContactElement(ptr)\n{\n  // pass...\n}\n\nvoid\nGD77Codeplug::ContactElement::clear() {\n  markValid(false);\n}\n\nbool\nGD77Codeplug::ContactElement::isValid() const {\n  uint8_t validFlag = getUInt8(Offset::validFlag());\n  return RadioddityCodeplug::ContactElement::isValid() && (0x00 != validFlag);\n}\nvoid\nGD77Codeplug::ContactElement::markValid(bool valid) {\n  setUInt8(Offset::validFlag(), valid ? 0xff : 0x00);\n}\nbool\nGD77Codeplug::ContactElement::fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err) {\n  if (! RadioddityCodeplug::ContactElement::fromContactObj(obj, ctx, err))\n    return false;\n  markValid();\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::ScanListElement\n * ******************************************************************************************** */\nGD77Codeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size)\n  : RadioddityCodeplug::ScanListElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : RadioddityCodeplug::ScanListElement(ptr)\n{\n  // pass...\n}\n\nvoid\nGD77Codeplug::ScanListElement::clear() {\n  RadioddityCodeplug::ScanListElement::clear();\n  setBit(0x0f, 0, true);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::ScanListBankElement\n * ******************************************************************************************** */\nGD77Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, unsigned size)\n  : RadioddityCodeplug::ScanListBankElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr)\n  : RadioddityCodeplug::ScanListBankElement(ptr, size())\n{\n  // pass...\n}\n\nvoid\nGD77Codeplug::ScanListBankElement::clear() {\n  memset(_data, 0, Limit::scanListCount());\n}\n\nuint8_t *\nGD77Codeplug::ScanListBankElement::get(unsigned n) const {\n  return _data+Offset::scanLists() + n*ScanListElement::size();\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::GroupListElement\n * ******************************************************************************************** */\nGD77Codeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size)\n  : RadioddityCodeplug::GroupListElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : RadioddityCodeplug::GroupListElement(ptr, size())\n{\n  // pass...\n}\n\nbool\nGD77Codeplug::GroupListElement::linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0; (i<Limit::memberCount()) && (i<ncnt); i++) {\n    if (ctx.has<DMRContact>(member(i))) {\n      lst->addContact(ctx.get<DMRContact>(member(i)));\n    } else {\n      errMsg(err) << \"Cannot link group list '\" << lst->name()\n                  << \"': Member index \" << member(i) << \" does not refer to a digital contact.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nGD77Codeplug::GroupListElement::fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  setName(lst->name());\n\n  int j = 0;\n  // Iterate over all entries in the codeplug\n  for (unsigned int i=0; i<Limit::memberCount(); i++) {\n    if (lst->count() > j) {\n      // Skip non-private-call entries\n      while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) {\n        logWarn() << \"Contact '\" << lst->contact(i)->name() << \"' in group list '\" << lst->name()\n                  << \"' is not a group call. Skip entry.\";\n        j++;\n      }\n      setMember(i, ctx.index(lst->contact(j))); j++;\n    } else {\n      // Clear entry.\n      clearMember(i);\n    }\n  }\n\n  return false;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug::GroupListBankElement\n * ******************************************************************************************** */\nGD77Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, unsigned size)\n  : RadioddityCodeplug::GroupListBankElement(ptr, size)\n{\n  // pass...\n}\n\nGD77Codeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : RadioddityCodeplug::GroupListBankElement(ptr, size())\n{\n  // pass...\n}\n\nuint8_t *\nGD77Codeplug::GroupListBankElement::get(unsigned n) const {\n  if ((Offset::groupLists() + (n+1)*GroupListElement::size())>_size) {\n    logFatal() << \"Cannot resolve group list at index \" << n << \": Overflow.\";\n    return nullptr;\n  }\n  return _data + Offset::groupLists() + n*GroupListElement::size();\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of GD77Codeplug\n * ******************************************************************************************** */\nGD77Codeplug::GD77Codeplug(QObject *parent)\n  : RadioddityCodeplug(parent)\n{\n  addImage(\"Radioddity GD77 Codeplug\");\n  image(0).addElement(0x00080, 0x07b80);\n  image(0).addElement(0x08000, 0x16b00);\n}\n\nvoid\nGD77Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(Offset::settings())).clear();\n}\n\nbool\nGD77Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  GeneralSettingsElement el(data(Offset::settings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n  return el.fromConfig(ctx, err);\n}\n\nbool\nGD77Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n}\n\nvoid\nGD77Codeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(Offset::buttons())).clear();\n}\nbool\nGD77Codeplug::encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ButtonSettingsElement(data(Offset::buttons())).encode(ctx, err);\n}\nbool\nGD77Codeplug::decodeButtonSettings(Context &ctx, const ErrorStack &err) {\n  return ButtonSettingsElement(data(Offset::buttons())).decode(ctx, err);\n}\n\nvoid\nGD77Codeplug::clearMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\nbool\nGD77Codeplug::encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err);\n}\nbool\nGD77Codeplug::decodeMessages(Context &ctx, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).decode(ctx, err);\n}\n\nvoid\nGD77Codeplug::clearScanLists() {\n  ScanListBankElement bank(data(Offset::scanListBank())); bank.clear();\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++)\n    ScanListElement(bank.get(i)).clear();\n}\n\nbool\nGD77Codeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (i >= ctx.count<ScanList>()) {\n      bank.enable(i, false); continue;\n    }\n    if (! ScanListElement(bank.get(i)).fromScanListObj(ctx.get<ScanList>(i+1), ctx, err))\n      return false;\n    bank.enable(i, true);\n  }\n\n  return true;\n}\n\nbool\nGD77Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ScanListElement el(bank.get(i));\n    ScanList *scan = el.toScanListObj(ctx, err);\n    if (nullptr == scan) {\n      errMsg(err) << \"Cannot decode scan list at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->scanlists()->add(scan);\n    ctx.add(scan, i+1);\n  }\n\n  return true;\n}\n\nbool\nGD77Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    if (! ScanListElement(bank.get(i)).linkScanListObj(ctx.get<ScanList>(i+1), ctx, err))\n      return false;\n  }\n\n  return true;\n}\n\nvoid\nGD77Codeplug::clearChannels() {\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr); bank.clear();\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++)\n      ChannelElement(bank.get(i)).clear();\n  }\n}\n\nbool\nGD77Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr); bank.clear();\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      ChannelElement el(bank.get(i));\n      if (c < ctx.count<Channel>()) {\n        if (! el.fromChannelObj(ctx.get<Channel>(c+1), ctx, err)) {\n          errMsg(err) << \"Cannot encode channel \" << c << \" (\" << i << \" of bank \" << b <<\").\";\n          return false;\n        }\n        bank.enable(i,true);\n      } else {\n        el.clear();\n        bank.enable(i, false);\n      }\n    }\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr);\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      if (! bank.isEnabled(i))\n        continue;\n      Channel *ch = ChannelElement(bank.get(i)).toChannelObj(ctx, err);\n      if (nullptr == ch) {\n        errMsg(err) << \"Cannot decode channel at index \" << i << \".\";\n        return false;\n      }\n      ctx.config()->channelList()->add(ch); ctx.add(ch, c+1);\n    }\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr);\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      if (! bank.isEnabled(i))\n        continue;\n      if (!ChannelElement(bank.get(i)).linkChannelObj(ctx.get<Channel>(c+1), ctx, err)) {\n        errMsg(err) << \"Cannot link channel '\" << ctx.get<Channel>(c+1)->name()\n                    << \"' at index \" << i << \".\";\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\nvoid\nGD77Codeplug::clearMenuSettings() {\n  MenuSettingsElement(data(Offset::menuSettings())).clear();\n}\n\n\nvoid\nGD77Codeplug::clearBootSettings() {\n  BootSettingsElement(data(Offset::bootSettings())).clear();\n  BootTextElement(data(Offset::bootText())).clear();\n}\n\nbool\nGD77Codeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  BootSettingsElement(data(Offset::bootSettings())).encode(ctx, err);\n  BootTextElement(data(Offset::bootText())).fromConfig(ctx, err);\n  return true;\n}\n\nbool\nGD77Codeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) {\n  BootSettingsElement(data(Offset::bootSettings())).decode(ctx, err);\n  BootTextElement(data(Offset::bootText())).updateConfig(ctx, err);\n  return true;\n}\n\nvoid\nGD77Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(Offset::vfoA())).clear();\n  VFOChannelElement(data(Offset::vfoB())).clear();\n}\n\nvoid\nGD77Codeplug::clearZones() {\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  bank.clear();\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++)\n    ZoneElement(bank.get(i)).clear();\n}\n\nbool\nGD77Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  ZoneBankElement bank(data(Offset::zoneBank()));\n\n  // Pack Zones\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    ZoneElement z(bank.get(i));\n    if (! ctx.has<Zone>(i+1)) {\n      bank.enable(i, false);\n      continue;\n    }\n\n    // Construct from Zone obj\n    Zone *zone = ctx.get<Zone>(i+1);\n    z.fromZoneObjA(zone, ctx);\n    bank.enable(i, true);\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ZoneElement z(bank.get(i));\n    Zone *zone = z.toZoneObj(ctx);\n    ctx.config()->zones()->add(zone);\n    ctx.add(zone, i+1);\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ZoneElement z(bank.get(i));\n    Zone *zone = ctx.get<Zone>(i+1);\n    if (! z.linkZoneObj(zone, ctx)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nGD77Codeplug::clearContacts() {\n  for (unsigned int i=0; i<Limit::contactCount(); i++)\n    ContactElement(data(Offset::contacts() + i*ContactElement::size())).clear();\n}\n\nbool\nGD77Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    ContactElement el(data(Offset::contacts() + i*ContactElement::size()));\n    el.clear();\n    if (i >= ctx.count<DigitalContact>())\n      continue;\n    if (! el.fromContactObj(ctx.get<DMRContact>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot encode contact at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nGD77Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    ContactElement el(data(Offset::contacts() + i*ContactElement::size()));\n    if (! el.isValid())\n      continue;\n\n    DMRContact *cont = el.toContactObj(ctx, err);\n    if (nullptr == cont) {\n      errMsg(err) << \"Cannot decode contact at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(cont, i+1);\n    ctx.config()->contacts()->add(cont);\n  }\n\n  return true;\n}\n\nvoid\nGD77Codeplug::clearDTMFContacts() {\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++)\n    DTMFContactElement(data(Offset::dtmfContacts() + i*DTMFContactElement::size())).clear();\n}\n\nbool\nGD77Codeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++) {\n    DTMFContactElement el(data(Offset::dtmfContacts() + i*DTMFContactElement::size()));\n    el.clear();\n    if (i >= ctx.count<DTMFContact>())\n      continue;\n    el.fromContactObj(ctx.get<DTMFContact>(i), ctx, err);\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++) {\n    DTMFContactElement el(data(Offset::dtmfContacts()+i*DTMFContactElement::size()));\n    // If contact is disabled\n    if (! el.isValid())\n      continue;\n    DTMFContact *cont = el.toContactObj(ctx, err);\n    if (nullptr == cont ) {\n      errMsg(err) << \"Cannot decode DTMF contact at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(cont, i+1); ctx.config()->contacts()->add(cont);\n  }\n  return true;\n}\n\nvoid\nGD77Codeplug::clearGroupLists() {\n  GroupListBankElement bank(data(Offset::groupListBank())); bank.clear();\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++)\n    GroupListElement(bank.get(0)).clear();\n}\n\nbool\nGD77Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  GroupListBankElement bank(data(Offset::groupListBank())); bank.clear();\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (i >= ctx.count<RXGroupList>())\n      continue;\n    GroupListElement el(bank.get(i));\n    el.fromRXGroupListObj(ctx.get<RXGroupList>(i+1), ctx, err);\n    bank.setContactCount(i, ctx.get<RXGroupList>(i+1)->count());\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  GroupListBankElement bank(data(Offset::groupListBank()));\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    GroupListElement el(bank.get(i));\n    RXGroupList *list = el.toRXGroupListObj(ctx, err);\n    ctx.config()->rxGroupLists()->add(list);\n    ctx.add(list, i+1);\n  }\n  return true;\n}\n\nbool\nGD77Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  GroupListBankElement bank(data(Offset::groupListBank()));\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    GroupListElement el(bank.get(i));\n    if (! el.linkRXGroupListObj(bank.contactCount(i), ctx.get<RXGroupList>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot link group list '\" << ctx.get<RXGroupList>(i+1)->name() << \"'.\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\nvoid\nGD77Codeplug::clearEncryption() {\n  EncryptionElement enc(data(Offset::encryption()));\n  enc.clear();\n}\n\nbool\nGD77Codeplug::encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  clearEncryption();\n  EncryptionElement enc(data(Offset::encryption()));\n  return enc.fromCommercialExt(ctx.config()->commercialExtension(), ctx, err);\n}\n\nbool\nGD77Codeplug::createEncryption(Context &ctx, const ErrorStack &err) {\n  EncryptionElement enc(data(Offset::encryption()));\n  if (EncryptionElement::PrivacyType::None == enc.privacyType())\n    return true;\n  return enc.updateCommercialExt(ctx, err);\n}\n\nbool\nGD77Codeplug::linkEncryption(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n"
  },
  {
    "path": "lib/gd77_codeplug.hh",
    "content": "#ifndef GD77_CODEPLUG_HH\n#define GD77_CODEPLUG_HH\n\n#include \"radioddity_codeplug.hh\"\n#include \"signaling.hh\"\n\n\n/** Represents, encodes and decodes the device specific codeplug for a Radioddity GD-77.\n *\n * The GD-77 & GD-77S codeplugs are almost identical to the Radioddity/Baofeng @c RD5RCodeplug, in fact\n * the memory layout (see below) and almost all of the single components of the codeplug are encoded in\n * exactly the same way. Obviously, when Baofeng and Radioddity joint to create the RD5R,\n * Radioddity provided the firmware. However, there are some small subtle differences between\n * these two codeplug formats, requiring a separate class for the GD-77. For example, the contacts\n * and scan-lists swapped the addresses and the @c channel_t encoding analog and digital channels\n * for the codeplugs are identical except for the squelch settings. Thanks for that!\n *\n * @section gd77ver Matching firmware versions\n * This class implements the codeplug for the firmware version @b 4.03.06. The codeplug format usually\n * does not change much with firmware revisions, in particular not with older radios. Unfortunately,\n * it is not possible to detect the firmware version running on the device. Consequenly, only the\n * newest firmware version is supported. However, older revisions may still work.\n *\n * @section gd77cpl Codeplug structure within radio\n * The memory representation of the codeplug within the radio is divided into two segments.\n * The first segment starts at the address 0x00080 and ends at 0x07c00 while the second section\n * starts at 0x08000 and ends at 0x1e300.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x00080-0x07c00</th></tr>\n *  <tr><td>0x00080</td> <td>0x000e0</td> <td>0x0070</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x000e0</td> <td>0x000ec</td> <td>0x000c</td> <td>General settings, see @c RadioddityCodeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x000ec</td> <td>0x00108</td> <td>0x0028</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x00108</td> <td>0x00128</td> <td>0x0020</td> <td>Button settings, see @c RadioddityCodeplug::ButtonSettingsElement.</td></tr>\n *  <tr><td>0x00128</td> <td>0x01370</td> <td>0x1248</td> <td>32 preset message texts, see @c RadioddityCodeplug::MessageBankElement.</td></tr>\n *  <tr><td>0x01370</td> <td>0x01790</td> <td>0x0420</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x01790</td> <td>0x02dd0</td> <td>0x1640</td> <td>64 scan lists, see @c GD77Codeplug::ScanListBankElement and @c GD77Codeplug::ScanListElement</td></tr>\n *  <tr><td>0x02dd0</td> <td>0x02f88</td> <td>0x01b8</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x02f88</td> <td>0x03388</td> <td>0x0400</td> <td>DTMF contacts, see RadioddityCodeplug::DTMFContactElement.</td></tr>\n *  <tr><td>0x03388</td> <td>0x03780</td> <td>0x03f8</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x03780</td> <td>0x05390</td> <td>0x1c10</td> <td>First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement and GD77Codeplug::ChannelElement</td></tr>\n *  <tr><td>0x05390</td> <td>0x07518</td> <td>0x2188</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07518</td> <td>0x07538</td> <td>0x0020</td> <td>Boot settings, see @c RadioddityCodeplug::BootSettingsElement.</td></tr>\n *  <tr><td>0x07538</td> <td>0x07540</td> <td>0x0008</td> <td>Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.</td></tr>\n *  <tr><td>0x07540</td> <td>0x07560</td> <td>0x0020</td> <td>2 intro lines, @c RadioddityCodeplug::BootTextElement.</td></tr>\n *  <tr><td>0x07560</td> <td>0x07590</td> <td>0x0030</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07590</td> <td>0x075c8</td> <td>0x0038</td> <td>VFO A settings @c RadioddityCodeplug::VFOChannelElement</td></tr>\n *  <tr><td>0x075c8</td> <td>0x07600</td> <td>0x0038</td> <td>VFO B settings @c RadioddityCodeplug::VFOChannelElement</td></tr>\n *  <tr><td>0x07600</td> <td>0x07c00</td> <td>0x0600</td> <td>??? Unknown ???</td></tr>\n *\n *  <tr><th colspan=\"4\">Second segment 0x08000-0x1e300</th></tr>\n *  <tr><td>0x08000</td> <td>0x08010</td> <td>0x0010</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x08010</td> <td>0x0af10</td> <td>0x2f00</td> <td>68 zones of 80 channels each, see @c RadioddityCodeplug::ZoneBankElement @c RadioddityCodeplug::ZoneElement.</td></tr>\n *  <tr><td>0x0af10</td> <td>0x0b1b0</td> <td>0x02a0</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x0b1b0</td> <td>0x17620</td> <td>0xc470</td> <td>Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement, @c GD77Codeplug::ChannelElement.</td></tr>\n *  <tr><td>0x17620</td> <td>0x1d620</td> <td>0x6000</td> <td>1024 contacts, see @c GD77Codeplug::ContactElement.</td></tr>\n *  <tr><td>0x1d620</td> <td>0x1eaa0</td> <td>0x1480</td> <td>64 RX group lists, see @c GD77Codeplug::GroupListBankElement, @c GD77Codeplug::GroupListElement.</td></tr>\n *  <tr><td>0x1eaa0</td> <td>0x1eb00</td> <td>0x0060</td> <td>??? Unknown ???</td></tr>\n * </table>\n * @ingroup gd77 */\nclass GD77Codeplug: public RadioddityCodeplug\n{\n\tQ_OBJECT\n\npublic:\n  /** Channel representation within the binary codeplug.\n   *\n   * Each channel requires 0x38b:\n   * @verbinclude gd77_channel.txt */\n  class ChannelElement: public RadioddityCodeplug::ChannelElement\n  {\n  public:\n    /** ARTS send. */\n    enum ARTSMode {\n      ARTS_OFF      = 0,\n      ARTS_TX       = 1,\n      ARTS_RX       = 2,\n      ARTS_BOTH     = 3\n    };\n\n    /** STE angle. */\n    enum STEAngle {\n      STE_FREQUENCY = 0,              ///< STE Frequency.\n      STE_120DEG    = 1,              ///< 120 degree.\n      STE_180DEG    = 2,              ///< 180 degree.\n      STE_240DEG    = 3               ///< 240 degree.\n    };\n\n    /** PTT ID send. */\n    enum PTTId {\n      PTTID_OFF     = 0,\n      PTTID_START   = 1,\n      PTTID_END     = 2,\n      PTTID_BOTH    = 3\n    };\n\n  protected:\n    /** Hidden Constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the ARTS mode. */\n    virtual ARTSMode artsMode() const;\n    /** Sets the ARTS mode. */\n    virtual void setARTSMode(ARTSMode mode);\n\n    /** Returns the STE angle. */\n    virtual STEAngle steAngle() const;\n    /** Sets the STE angle. */\n    virtual void setSTEAngle(STEAngle angle);\n\n    /** Returns the PTT ID mode. */\n    virtual PTTId pttIDMode() const;\n    /** Sets the PTT ID mode. */\n    virtual void setPTTIDMode(PTTId mode);\n\n    /** Returns @c true if the squech type is tight. */\n    virtual bool squelchIsTight() const;\n    /** Enables/disables tight squelch. */\n    virtual void enableTightSquelch(bool enable);\n\n    /** Returns @c true if lone worker is enabled. */\n    virtual bool loneWorker() const;\n    /** Enables/disables lone worker. */\n    virtual void enableLoneWorker(bool enable);\n\n    /** Returns @c true if auto scan is enabled. */\n    virtual bool autoscan() const;\n    /** Enables/disables auto scan. */\n    virtual void enableAutoscan(bool enable);\n  };\n\n  /** Specific codeplug representation of a DMR contact for the GD77.\n   *\n   * Memory layout of the contact (0x18b):\n   * @verbinclude gd77_contact.txt\n   */\n  class ContactElement: public RadioddityCodeplug::ContactElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n\n    /** Resets the contact. */\n    void clear();\n\n    /** Returns @c true if the contact is valid. */\n    bool isValid() const;\n    /** Marks the entry as valid/invalid. */\n    virtual void markValid(bool valid=true);\n\n    bool fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: RadioddityCodeplug::ContactElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int validFlag() { return 0x0017; }\n      /// @endcond\n    };\n  };\n\n  /** Represents an RX group list within the codeplug.\n   *\n   * The group list is encoded as (size 0x50b):\n   * @verbinclude gd77_grouplist.txt */\n  class GroupListElement: public RadioddityCodeplug::GroupListElement\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0050; }\n\n    virtual bool linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()) const;\n    virtual bool fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for the group list. */\n    struct Limit: public RadioddityCodeplug::GroupListElement {\n      static constexpr unsigned int memberCount() { return 32; }  ///< Maximum number of entries.\n    };\n  };\n\n  /** Table of RX group lists.\n   *\n   * The RX group list table constsis of a table of number of members per group list and the actual\n   * list of RX group lists. The former also acts as a byte map for valid RX group lists. If 0, the\n   * group list is disabled, if 1 the group list is empty, etc. So the entry is N+1, where N is the\n   * number of entries per group list.\n   *\n   * Encoding of group list table:\n   * @verbinclude gd77_grouplistbank.txt*/\n  class GroupListBankElement: public RadioddityCodeplug::GroupListBankElement\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    GroupListBankElement(uint8_t *ptr);\n\n    /** The size of the group list bank. */\n    static constexpr unsigned int size() { return 0x1480; }\n\n    uint8_t *get(unsigned n) const;\n\n  public:\n    /** Some limits for the group list bank. */\n    struct Limit: public RadioddityCodeplug::GroupListBankElement::Limit {\n      static constexpr unsigned int groupListCount() { return 64; }   ///< Number of group lists.\n    };\n  };\n\n  /** Represents a single scan list within the GD77 codeplug.\n   *\n   * Encoding of scan list (size: 0x58b):\n   * @verbinclude gd77_scanlist.txt */\n  class ScanListElement: public RadioddityCodeplug::ScanListElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n\n    /** Resets the scan list. */\n    void clear();\n  };\n\n  /** Bank of scan lists for the GD77.\n   *\n   * Encoding of scan list table (size 0x1640b):\n   * @verbinclude gd77_scanlistbank.txt */\n  class ScanListBankElement: public RadioddityCodeplug::ScanListBankElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    ScanListBankElement(uint8_t *ptr);\n\n    /** The size of the scan list bank. */\n    static constexpr unsigned int size() { return 0x1640; }\n\n    /** Clears the scan list bank. */\n    void clear();\n\n    uint8_t *get(unsigned n) const;\n\n  public:\n    /** Some limits for the scan list bank. */\n    struct Limit: public RadioddityCodeplug::ScanListBankElement::Limit {\n      static constexpr unsigned int scanListCount() { return 64; } ///< Maximum number of scan lists.\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset: public RadioddityCodeplug::ScanListBankElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bytemap()   { return 0x0000; }\n      static constexpr unsigned int scanLists() { return 0x0040; }\n      /// @endcond\n    };\n  };\n\npublic:\n  /** Constructs an empty codeplug for the GD-77. */\n\texplicit GD77Codeplug(QObject *parent=nullptr);\n\npublic:\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearMessages();\n  bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n  bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFContacts();\n  bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearMenuSettings();\n\n  void clearBootSettings();\n  bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearVFOSettings();\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearEncryption();\n  bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool createEncryption(Context &ctx, const ErrorStack &err);\n  bool linkEncryption(Context &ctx, const ErrorStack &err);\n\npublic:\n  /** Some limits for the GD77 codeplug. */\n  struct Limit {\n    static constexpr unsigned int dtmfContactCount() { return 32; }   ///< Maximum number of DTMF contacts.\n    static constexpr unsigned int channelBankCount() { return 8; }    ///< Number of channel banks.\n    static constexpr unsigned int channelCount()     { return 1024; } ///< Maximum number of channels.\n    static constexpr unsigned int contactCount()     { return 1024; } ///< Maximum number of contacts.\n  };\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int settings()        { return 0x0000e0; }\n    static constexpr unsigned int buttons()         { return 0x000108; }\n    static constexpr unsigned int messages()        { return 0x000128; }\n    static constexpr unsigned int encryption()      { return 0x001370; }\n    static constexpr unsigned int scanListBank()    { return 0x001790; }\n    static constexpr unsigned int dtmfContacts()    { return 0x002f88; }\n    static constexpr unsigned int channelBank0()    { return 0x003780; } // Channels 1-128\n    static constexpr unsigned int bootSettings()    { return 0x007518; }\n    static constexpr unsigned int bootText()        { return 0x007540; }\n    static constexpr unsigned int menuSettings()    { return 0x007538; }\n    static constexpr unsigned int vfoA()            { return 0x007590; }\n    static constexpr unsigned int vfoB()            { return 0x0075c8; }\n    static constexpr unsigned int zoneBank()        { return 0x008010; }\n    static constexpr unsigned int channelBank1()    { return 0x00b1b0; } // Channels 129-1024\n    static constexpr unsigned int contacts()        { return 0x017620; }\n    static constexpr unsigned int groupListBank()   { return 0x01d620; }\n    /// @endcond\n  };\n};\n\n#endif // GD77_CODEPLUG_HH\n"
  },
  {
    "path": "lib/gd77_filereader.cc",
    "content": "#include \"gd77_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_ADDR 0x00000080\n#define SEGMENT0_SIZE 0x00007b80\n#define SEGMENT1_ADDR 0x00008000\n#define SEGMENT1_SIZE 0x00016300\n\nbool\nGD77FileReader::read(const QString &filename, GD77Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (131072 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 131072 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  if (! file.seek(SEGMENT1_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  ptr = (char *)codeplug->data(SEGMENT1_ADDR);\n  n = SEGMENT1_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/gd77_filereader.hh",
    "content": "#ifndef GD77FILEREADER_HH\n#define GD77FILEREADER_HH\n\n#include \"gd77_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug\n * data as written to the device. This makes the decoding of the manufacturer codeplug files very\n * easy. Some memory regions, however, are not written to the deivice although they are present in\n * the codeplug file.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th></tr>\n *  <tr><td>0x00080</td> <td>0x07c00</td>  <td>0x07b80</td></tr>\n *  <tr><td>0x08000</td> <td>0x1e300</td>  <td>0x16300</td></tr>\n * </table>\n *\n * @ingroup gd77 */\nclass GD77FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err The error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, GD77Codeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // GD77FILEREADER_HH\n"
  },
  {
    "path": "lib/gd77_limits.cc",
    "content": "#include \"gd77_limits.hh\"\n#include \"gd77_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"roamingzone.hh\"\n\n\nGD77Limits::GD77Limits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 10920;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(GD77Codeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n        /// @todo check default radio ID.\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 1024, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                (unsigned)DMRContact::GroupCall,\n                (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 32, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"number\", new RadioLimitStringRegEx(\"^[0-9A-Fa-f]+$\") }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 76, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 1024, // < up to 1024 channels\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"openGD77\", new RadioLimitIgnored()},\n              {\"tyt\", new RadioLimitIgnored()}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          16, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Ignore scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, 64, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 15, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 32, Channel::staticMetaObject) }\n        }));\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList(\n         BasicEncryptionKey::staticMetaObject,\n         0, RadioddityCodeplug::EncryptionElement::Limit::keyCount(),\n         new RadioLimitObject {\n           {\"name\", new RadioLimitIgnored()},\n           {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{8}\")}\n         })}\n      });\n\n  /* Ignore positioning systems. */\n  add(\"positioning\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n}\n"
  },
  {
    "path": "lib/gd77_limits.hh",
    "content": "#ifndef GD77LIMITS_HH\n#define GD77LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the limits for the Radioddity GD77.\n * @ingroup gd77 */\nclass GD77Limits: public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit GD77Limits(QObject *parent=nullptr);\n};\n\n#endif // GD77LIMITS_HH\n"
  },
  {
    "path": "lib/gnsssettings.cc",
    "content": "#include \"gnsssettings.hh\"\n#include \"utils.hh\"\n\n\nGNSSSettings::GNSSSettings(QObject *parent)\n  : ConfigExtension{parent}, _fixedPositionEnabled(false), _fixedPosition(),\n    _systems(System::GPS), _units(Units::Metric)\n{\n  // pass...\n}\n\n\nConfigItem *\nGNSSSettings::clone() const {\n  auto obj = new GNSSSettings();\n  if (! obj->copy(*this)) {\n    delete obj;\n    return nullptr;\n  }\n  return obj;\n}\n\n\nbool\nGNSSSettings::fixedPositionEnabled() const {\n  return _fixedPosition.isValid() && (_fixedPositionEnabled || _systems.testFlag(System::Fixed));\n}\n\nvoid\nGNSSSettings::enableFixedPosition(bool use) {\n  if (_fixedPositionEnabled == use)\n    return;\n  _fixedPositionEnabled = use;\n  emit modified(this);\n}\n\n\nconst QGeoCoordinate &\nGNSSSettings::fixedPosition() const {\n  return _fixedPosition;\n}\n\nvoid\nGNSSSettings::setFixedPosition(const QGeoCoordinate &pos) {\n  if (_fixedPosition == pos)\n    return;\n  _fixedPosition = pos;\n  emit modified(this);\n}\n\n\nQString\nGNSSSettings::fixedPositionLocator() const {\n  return deg2loc(fixedPosition(), 8);\n}\n\nvoid\nGNSSSettings::setFixedPositionLocator(const QString &locator) {\n  setFixedPosition(loc2deg(locator));\n}\n\n\nGNSSSettings::Systems\nGNSSSettings::systems() const {\n  return _systems;\n}\n\nvoid\nGNSSSettings::setSystems(Systems systems) {\n  if (_systems == systems)\n    return;\n  _systems = systems;\n  emit modified(this);\n}\n\n\nGNSSSettings::Units\nGNSSSettings::units() const {\n  return _units;\n}\n\nvoid\nGNSSSettings::setUnits(Units units) {\n  if (_units == units)\n    return;\n  _units = units;\n  emit modified(this);\n}\n"
  },
  {
    "path": "lib/gnsssettings.hh",
    "content": "#ifndef GNSSSETTINGS_HH\n#define GNSSSETTINGS_HH\n\n#include \"configobject.hh\"\n#include <QGeoCoordinate>\n\n/** Some common global GNSS settings. */\nclass GNSSSettings : public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"Some common global GNSS settings.\");\n\n  Q_CLASSINFO(\"fixedPositionEnabledDescription\", \"If enabled, the fixed position is used instead of the GNSS position.\")\n  Q_PROPERTY(bool fixedPositionEnabled READ fixedPositionEnabled WRITE enableFixedPosition FINAL)\n\n  Q_CLASSINFO(\"fixedPositionDescription\", \"Some fixed position (locator).\")\n  Q_PROPERTY(QString fixedPosition READ fixedPositionLocator WRITE setFixedPositionLocator FINAL)\n\n  Q_CLASSINFO(\"systemsDescription\", \"Enabled GNSSs.\")\n  Q_PROPERTY(Systems systems READ systems WRITE setSystems FINAL)\n\n  Q_CLASSINFO(\"unitsDescrption\", \"Specifies unit system.\")\n  Q_PROPERTY(Units units READ units WRITE setUnits FINAL)\n\npublic:\n  enum class Units {\n    Metric, Archaic\n  };\n  Q_ENUM(Units)\n\n  enum class System {\n    Fixed = 0, GPS = 1, Glonass = 2, Galileo = 4,  Beidou = 8\n  };\n  Q_DECLARE_FLAGS(Systems, System)\n  Q_FLAGS(Systems)\n\n\npublic:\n  explicit GNSSSettings(QObject *parent = nullptr);\n\n  ConfigItem *clone() const override;\n\n  bool fixedPositionEnabled() const;\n  void enableFixedPosition(bool use);\n\n  const QGeoCoordinate &fixedPosition() const;\n  void setFixedPosition(const QGeoCoordinate &pos);\n  QString fixedPositionLocator() const;\n  void setFixedPositionLocator(const QString &locator);\n\n  Systems systems() const;\n  void setSystems(Systems systems);\n\n  Units units() const;\n  void setUnits(Units units);\n\nprotected:\n  bool _fixedPositionEnabled;\n  QGeoCoordinate _fixedPosition;\n  Systems _systems;\n  Units _units;\n};\n\nQ_DECLARE_OPERATORS_FOR_FLAGS(GNSSSettings::Systems)\n\nnamespace YAML\n{\n  /** Implements the conversion to and from YAML::Node. */\n  template<>\n  struct convert<QGeoCoordinate>\n  {\n    /** Serializes the frequency. */\n    static Node encode(const QGeoCoordinate& rhs) {\n      if (! rhs.isValid())\n        return Node(YAML::Null);\n\n      Node list;\n      list[\"longitude\"] = rhs.longitude();\n      list[\"latitude\"]  = rhs.latitude();\n      if (std::isfinite(rhs.altitude()))\n        list[\"altitude\"]  = rhs.altitude();\n      return list;\n    }\n\n    /** Parses the frequency. */\n    static bool decode(const Node& node, QGeoCoordinate& rhs) {\n      if (node.IsNull()) {\n        rhs = QGeoCoordinate();\n        return true;\n      }\n      if (! node.IsMap())\n        return false;\n      if ((!node[\"longitude\"]) || (!node[\"longitude\"].IsScalar()))\n        return false;\n      if ((!node[\"latitude\"]) || (!node[\"latitude\"].IsScalar()))\n        return false;\n      rhs.setLongitude(node[\"longitude\"].as<double>(std::numeric_limits<double>::quiet_NaN()));\n      rhs.setLatitude(node[\"latitude\"].as<double>(std::numeric_limits<double>::quiet_NaN()));\n      if (node[\"altitude\"] && node[\"altitude\"].IsScalar())\n        rhs.setAltitude(node[\"altitude\"].as<double>(std::numeric_limits<double>::quiet_NaN()));\n      return true;\n    }\n  };\n}\n\n#endif // GNSSSETTINGS_HH\n"
  },
  {
    "path": "lib/gpssystem.cc",
    "content": "#include \"gpssystem.hh\"\n#include \"contact.hh\"\n#include \"channel.hh\"\n#include \"logger.hh\"\n#include <QRegularExpressionMatch>\n\n\n/* ********************************************************************************************* *\n * Implementation of PositionReportingSystem\n * ********************************************************************************************* */\nPositionReportingSystem::PositionReportingSystem(QObject *parent)\n  : ConfigObject(parent), _period(Interval::infinity())\n{\n  // pass...\n}\n\nPositionReportingSystem::PositionReportingSystem(const QString &name, const Interval &period, QObject *parent)\n  : ConfigObject(name, parent), _period(period)\n{\n  // pass...\n}\n\nPositionReportingSystem::~PositionReportingSystem() {\n  // pass...\n}\n\n\nbool\nPositionReportingSystem::periodDisabled() const {\n  return !_period.isFinite();\n}\n\nInterval\nPositionReportingSystem::period() const {\n  return _period;\n}\n\nvoid\nPositionReportingSystem::setPeriod(const Interval &period) {\n  if (_period == period)\n    return;\n  _period = period;\n  emit modified(this);\n}\n\nvoid\nPositionReportingSystem::disablePeriod() {\n  _period = Interval::infinity();\n}\n\nbool\nPositionReportingSystem::populate(YAML::Node &node, const ConfigItem::Context &context, const ErrorStack &err) {\n  if (! ConfigObject::populate(node, context, err))\n    return false;\n\n  if (! periodDisabled())\n    node[\"period\"] = _period.format().toStdString();\n\n  return true;\n}\n\nbool\nPositionReportingSystem::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse positioning system: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node pos = node.begin()->second;\n  if (pos && (! pos[\"period\"])) {\n    logWarn() << pos.Mark().line << \":\" << pos.Mark().column\n              << \": Positioning system has no period.\";\n  } else if (pos && pos[\"period\"].IsScalar()) {\n    Interval period;\n    if (! period.parse(QString::fromStdString(pos[\"period\"].as<std::string>()), Interval::Format::Seconds))\n      disablePeriod();\n    else\n      setPeriod(period);\n  }\n\n  return ConfigObject::parse(pos, ctx, err);\n}\n\nbool\nPositionReportingSystem::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  return ConfigObject::link(node.begin()->second, ctx, err);\n}\n\nvoid\nPositionReportingSystem::onReferenceModified() {\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRAPRSSystem\n * ********************************************************************************************* */\nDMRAPRSSystem::DMRAPRSSystem(QObject *parent)\n  : PositionReportingSystem(parent), _contact(), _revertChannel()\n{\n  // Allow revert channel to take a reference to the SelectedChannel singleton\n  _revertChannel.allow(SelectedChannel::get()->metaObject());\n  // Register '!selected' tag for revert channel\n  Context::setTag(staticMetaObject.className(), \"revert\",\n                  \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  // By default, selected channel is revert channel\n  resetRevertChannel();\n\n  // Connect signals\n  connect(&_contact, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_revertChannel, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nDMRAPRSSystem::DMRAPRSSystem(const QString &name, DMRContact *contact,\n                     DMRChannel *revertChannel, const Interval &period,\n                     QObject *parent)\n  : PositionReportingSystem(name, period, parent), _contact(), _revertChannel()\n{\n  // Allow revert channel to take a reference to the SelectedChannel singleton\n  _revertChannel.allow(SelectedChannel::get()->metaObject());\n  // Register '!selected' tag for revert channel\n  Context::setTag(staticMetaObject.className(), \"revert\",\n                  \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n\n  // Set references.\n  _contact.set(contact);\n  setRevertChannel(revertChannel);\n\n  // Connect signals\n  connect(&_contact, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n  connect(&_revertChannel, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nConfigItem *\nDMRAPRSSystem::clone() const {\n  DMRAPRSSystem *sys = new DMRAPRSSystem();\n  if (! sys->copy(*this)) {\n    sys->deleteLater();\n    return nullptr;\n  }\n  return sys;\n}\n\nbool\nDMRAPRSSystem::hasContact() const {\n  return ! _contact.isNull();\n}\n\nDMRContact *\nDMRAPRSSystem::contact() const {\n  return _contact.as<DMRContact>();\n}\n\nvoid\nDMRAPRSSystem::setContact(DMRContact *contact) {\n  _contact.set(contact);\n}\n\n\nconst DMRContactReference *\nDMRAPRSSystem::contactRef() const {\n  return &_contact;\n}\n\nDMRContactReference *\nDMRAPRSSystem::contactRef() {\n  return &_contact;\n}\n\n\nbool\nDMRAPRSSystem::hasRevertChannel() const {\n  return _revertChannel.is<DMRChannel>();\n}\n\nDMRChannel *\nDMRAPRSSystem::revertChannel() const {\n  return _revertChannel.as<DMRChannel>();\n}\n\nvoid\nDMRAPRSSystem::setRevertChannel(DMRChannel *channel) {\n  if (nullptr == channel)\n    resetRevertChannel();\n  else\n    _revertChannel.set(channel);\n}\n\nvoid\nDMRAPRSSystem::resetRevertChannel() {\n  _revertChannel.set(SelectedChannel::get());\n}\n\n\nconst DMRChannelReference*\nDMRAPRSSystem::revertChannelRef() const {\n  return &_revertChannel;\n}\n\nDMRChannelReference*\nDMRAPRSSystem::revertChannelRef() {\n  return &_revertChannel;\n}\n\nYAML::Node\nDMRAPRSSystem::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = PositionReportingSystem::serialize(context, err);\n  if (node.IsNull())\n    return node;\n  YAML::Node type; type[\"dmr\"] = node;\n  return type;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of FMAPRSSystem\n * ********************************************************************************************* */\nFMAPRSSystem::FMAPRSSystem(QObject *parent)\n  : PositionReportingSystem(parent), _channel(), _destination(), _destSSID(0),\n    _source(), _srcSSID(0), _path(), _icon(Icon::None), _message(),\n    _anytone(nullptr)\n{\n  // Allow revert channel to take a reference to the SelectedChannel singleton\n  _channel.allow(SelectedChannel::get()->metaObject());\n  // Register '!selected' tag for revert channel\n  Context::setTag(staticMetaObject.className(), \"revert\",\n                  \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  // By default, selected channel is revert channel\n  resetRevertChannel();\n\n  // Connect to channel reference\n  connect(&_channel, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nFMAPRSSystem::FMAPRSSystem(const QString &name, FMChannel *channel, const QString &dest, unsigned destSSID,\n                       const QString &src, unsigned srcSSID, const QString &path, Icon icon, const QString &message,\n                       const Interval &period, QObject *parent)\n  : PositionReportingSystem(name, period, parent), _channel(), _destination(dest), _destSSID(destSSID),\n    _source(src), _srcSSID(srcSSID), _path(path), _icon(icon), _message(message),\n    _anytone(nullptr)\n{\n  // Allow revert channel to take a reference to the SelectedChannel singleton\n  _channel.allow(SelectedChannel::get()->metaObject());\n  // Register '!selected' tag for revert channel\n  Context::setTag(staticMetaObject.className(), \"revert\",\n                  \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n\n  // Set revert channel\n  setRevertChannel(channel);\n\n  // Connect to channel reference\n  connect(&_channel, SIGNAL(modified()), this, SLOT(onReferenceModified()));\n}\n\nbool\nFMAPRSSystem::copy(const ConfigItem &other) {\n  const FMAPRSSystem *sys = other.as<FMAPRSSystem>();\n  if ((nullptr == sys) || (! PositionReportingSystem::copy(other)))\n    return false;\n  _destination = sys->destination();\n  _destSSID = sys->_destSSID;\n  _source = sys->_source;\n  _srcSSID = sys->_srcSSID;\n  _path = sys->_path;\n  return true;\n}\n\nConfigItem *\nFMAPRSSystem::clone() const {\n  FMAPRSSystem *sys = new FMAPRSSystem();\n  if (! sys->copy(*this)) {\n    sys->deleteLater();\n    return nullptr;\n  }\n  return sys;\n}\n\n\nbool\nFMAPRSSystem::hasRevertChannel() const {\n  return _channel.is<FMChannel>();\n}\n\nFMChannel *\nFMAPRSSystem::revertChannel() const {\n  return _channel.as<FMChannel>();\n}\n\nvoid\nFMAPRSSystem::setRevertChannel(FMChannel *channel) {\n  if (nullptr == channel)\n    resetRevertChannel();\n  else\n    _channel.set(channel);\n}\n\nvoid\nFMAPRSSystem::resetRevertChannel() {\n  _channel.set(SelectedChannel::get());\n}\n\n\nconst FMChannelReference *\nFMAPRSSystem::revertChannelRef() const {\n  return &_channel;\n}\n\nFMChannelReference *\nFMAPRSSystem::revertChannelRef() {\n  return &_channel;\n}\n\n\nconst QString &\nFMAPRSSystem::destination() const {\n  return _destination;\n}\n\nunsigned\nFMAPRSSystem::destSSID() const {\n  return _destSSID;\n}\n\nvoid\nFMAPRSSystem::setDestination(const QString &call, unsigned ssid) {\n  _destination = call;\n  _destSSID = ssid;\n}\n\nvoid\nFMAPRSSystem::setDestination(const QString &call) {\n  _destination = call;\n}\n\nvoid\nFMAPRSSystem::setDestSSID(unsigned int ssid) {\n  _destSSID = ssid;\n}\n\n\nconst QString &\nFMAPRSSystem::source() const {\n  return _source;\n}\n\nunsigned\nFMAPRSSystem::srcSSID() const {\n  return _srcSSID;\n}\n\nvoid\nFMAPRSSystem::setSource(const QString &call, unsigned ssid) {\n  _source = call;\n  _srcSSID = ssid;\n}\n\nvoid\nFMAPRSSystem::setSource(const QString &call) {\n  _source = call;\n}\n\nvoid\nFMAPRSSystem::setSrcSSID(unsigned ssid) {\n  _srcSSID = ssid;\n}\n\n\nconst QString &\nFMAPRSSystem::path() const {\n  return _path;\n}\nvoid\nFMAPRSSystem::setPath(const QString &path) {\n  _path = path.toUpper();\n  _path.replace(\" \",\"\");\n}\n\nFMAPRSSystem::Icon\nFMAPRSSystem::icon() const {\n  return _icon;\n}\nvoid\nFMAPRSSystem::setIcon(Icon icon) {\n  _icon = icon;\n}\n\nconst QString &\nFMAPRSSystem::message() const {\n  return _message;\n}\nvoid\nFMAPRSSystem::setMessage(const QString &msg) {\n  _message = msg;\n  emit modified(this);\n}\n\nAnytoneFMAPRSSettingsExtension *\nFMAPRSSystem::anytoneExtension() const {\n  return _anytone;\n}\nvoid\nFMAPRSSystem::setAnytoneExtension(AnytoneFMAPRSSettingsExtension *ext) {\n  if (_anytone) {\n    _anytone->deleteLater();\n    _anytone = nullptr;\n  }\n  if (ext) {\n    _anytone = ext;\n    ext->setParent(this);\n    connect(ext, SIGNAL(modified(ConfigItem *)), this, SIGNAL(modified(ConfigItem *)));\n  }\n}\n\n\nYAML::Node\nFMAPRSSystem::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = PositionReportingSystem::serialize(context, err);\n  if (node.IsNull())\n    return node;\n  YAML::Node type; type[\"aprs\"] = node;\n  return type;\n}\n\nbool\nFMAPRSSystem::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  if (! PositionReportingSystem::populate(node, context, err))\n    return false;\n\n  node[\"destination\"] = QString(\"%1-%2\").arg(_destination).arg(_destSSID).toStdString();\n  node[\"source\"] = QString(\"%1-%2\").arg(_source).arg(_srcSSID).toStdString();\n\n  QStringList path;\n  QRegularExpression pattern(\"([A-Za-z0-9]+-[0-9]+)\");\n  int idx = 0;\n  auto match = pattern.match(_path, idx);\n  while (match.hasMatch()) {\n    path.append(match.captured(1));\n    idx += match.capturedLength(1);\n    match = pattern.match(_path, idx);\n  }\n\n  if (path.count()) {\n    YAML::Node list = YAML::Node(YAML::NodeType::Sequence);\n    list.SetStyle(YAML::EmitterStyle::Flow);\n    foreach (QString call, path) {\n      list.push_back(call.toStdString());\n    }\n    node[\"path\"] = list;\n  }\n\n  return true;\n}\n\n\nbool\nFMAPRSSystem::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse APRS system: Expected object with one child.\";\n    return false;\n  }\n\n  YAML::Node sys = node.begin()->second;\n  if (sys[\"source\"] && sys[\"source\"].IsScalar()) {\n    QString source = QString::fromStdString(sys[\"source\"].as<std::string>());\n    QRegularExpression pattern(\"^([A-Z0-9]+)-(1?[0-9])$\");\n    auto match = pattern.match(source);\n    if (! match.hasMatch()) {\n      errMsg(err) << sys.Mark().line << \":\" << sys.Mark().column\n                  << \": Cannot parse APRS system: '\" << source << \"' not a valid source call and SSID.\";\n      return false;\n    }\n    setSource(match.captured(1), match.captured(2).toUInt());\n  } else {\n    errMsg(err) << sys.Mark().line << \":\" << sys.Mark().column\n                << \": Cannot parse APRS system: No source call+SSID specified.\";\n    return false;\n  }\n\n  if (sys[\"destination\"] && sys[\"destination\"].IsScalar()) {\n    QString dest = QString::fromStdString(sys[\"destination\"].as<std::string>());\n    QRegularExpression pattern(\"^([A-Z0-9]+)-(1?[0-9])$\");\n    auto match = pattern.match(dest);\n    if (! match.hasMatch()) {\n      errMsg(err) << sys.Mark().line << \":\" << sys.Mark().column\n                  << \": Cannot parse APRS system: '\" << dest << \"' not a valid destination call and SSID.\";\n      return false;\n    }\n    setDestination(match.captured(1), match.captured(2).toUInt());\n  } else {\n    errMsg(err) << sys.Mark().line << \":\" << sys.Mark().column\n                << \": Cannot parse APRS system: No destination call+SSID specified.\";\n    return false;\n  }\n\n  if (sys[\"path\"] && sys[\"path\"].IsSequence()) {\n    QStringList path;\n    for (YAML::const_iterator it=sys[\"path\"].begin(); it!=sys[\"path\"].end(); it++) {\n      if (it->IsScalar())\n        path.append(QString::fromStdString(it->as<std::string>()));\n    }\n    setPath(path.join(\",\"));\n  }\n\n  return PositionReportingSystem::parse(node, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GPSSystems table\n * ********************************************************************************************* */\nPositionReportingSystems::PositionReportingSystems(QObject *parent)\n  : ConfigObjectList(PositionReportingSystem::staticMetaObject, parent)\n{\n  // pass...\n}\n\nPositionReportingSystem *\nPositionReportingSystems::system(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<PositionReportingSystem>();\n  return nullptr;\n}\n\nint\nPositionReportingSystems::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<PositionReportingSystem>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nPositionReportingSystems::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n  if (! node)\n    return nullptr;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create positioning system: Expected object with one child.\";\n    return nullptr;\n  }\n\n  QString type = QString::fromStdString(node.begin()->first.as<std::string>());\n  if (\"dmr\" == type) {\n    return new DMRAPRSSystem();\n  } else if (\"aprs\"==type) {\n    return new FMAPRSSystem();\n  }\n\n  errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n              << \": Cannot create positioning system: Unknown type '\" << type << \"'.\";\n\n  return nullptr;\n}\n\n\n"
  },
  {
    "path": "lib/gpssystem.hh",
    "content": "#ifndef GPSSYSTEM_H\n#define GPSSYSTEM_H\n\n#include \"configreference.hh\"\n#include <QAbstractTableModel>\n#include \"anytone_extension.hh\"\n\nclass Config;\nclass DMRContact;\nclass DMRChannel;\nclass FMChannel;\n\n\n/** Base class of the position reporting systems, that is APRS and DMR position reporting system.\n * @ingroup conf */\nclass PositionReportingSystem: public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"aprs\")\n\n  /** The update period in seconds. */\n  Q_PROPERTY(Interval period READ period WRITE setPeriod SCRIPTABLE false)\n\nprotected:\n  /** Default constructor. */\n  explicit PositionReportingSystem(QObject *parent=nullptr);\n  /** Hidden constructor.\n   * The PositionReportingSystem class is not instantiated directly, use either @c GPSSystem or\n   * @c APRSSystem instead.\n   * @param name Specifies the name of the system.\n   * @param period Specifies the auto-update period in seconds.\n   * @param parent Specified the QObject parent object. */\n  PositionReportingSystem(const QString &name, const Interval &period=Interval::fromMinutes(5),\n                          QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~PositionReportingSystem();\n\n  /** Returns @c true, if the period is disabled. */\n  bool periodDisabled() const;\n  /** Returns the update period in seconds. */\n  Interval period() const;\n  /** Sets the update period in seconds. */\n  void setPeriod(const Interval &period);\n  /** Disable update period. */\n  void disablePeriod();\n\npublic:\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  bool populate(YAML::Node &node, const ConfigItem::Context &context, const ErrorStack &err=ErrorStack());\n\nprotected slots:\n  /** Gets called, whenever a reference is modified. */\n  void onReferenceModified();\n\nprotected:\n  /** Holds the update period in seconds. */\n  Interval _period;\n};\n\n\n/** This class represents a DMR position reporting system within the codeplug.\n * @ingroup conf */\nclass DMRAPRSSystem : public PositionReportingSystem\n{\n  Q_OBJECT\n\n  /** References the destination contact. */\n  Q_PROPERTY(DMRContactReference* contact READ contactRef)\n  /** References the revertRef channel. */\n  Q_PROPERTY(DMRChannelReference* revert READ revertChannelRef)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit DMRAPRSSystem(QObject *parent=nullptr);\n  /** Constructor.\n   *\n   * Please note, that a contact needs to be set in order for the GPS system to work properly.\n   *\n   * @param name Specifies the name of the GPS system.\n   * @param contact Specifies the contact, the GPS position is sent to.\n   * @param revertChannel Specifies the channel on which the GPS is sent on. If @c nullptr, the GPS\n   * data is sent on the current channel.\n   * @param period Specifies the update period in seconds.\n   * @param parent Specifies the QObject parent object. */\n  DMRAPRSSystem(const QString &name, DMRContact *contact=nullptr,\n                DMRChannel *revertChannel = nullptr, const Interval &period=Interval::fromMinutes(5),\n                QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if a contact is set for the GPS system. */\n  bool hasContact() const;\n  /** Returns the destination contact for the GPS information or @c nullptr if not set. */\n  DMRContact *contact() const;\n  /** Sets the destination contact for the GPS information. */\n  void setContact(DMRContact *contactObj);\n  /** Returns the reference to the destination contactRef. */\n  const DMRContactReference *contactRef() const;\n  /** Returns the reference to the destination contactRef. */\n  DMRContactReference *contactRef();\n\n  /** Returns @c true if the GPS system has a revert channel set. If not, the GPS information will\n   * be sent on the current channel. */\n  bool hasRevertChannel() const;\n  /** Returns the revert channel for the GPS information or @c nullptr if not set. */\n  DMRChannel *revertChannel() const;\n  /** Sets the revert channel for the GPS information to be sent on. */\n  void setRevertChannel(DMRChannel *channel);\n  /** Resets the revert channel to the current channel. */\n  void resetRevertChannel();\n\n  /** Returns a reference to the revertChannelRef channel. */\n  const DMRChannelReference *revertChannelRef() const;\n  /** Returns a reference to the revertChannelRef channel. */\n  DMRChannelReference *revertChannelRef();\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the destination contact for the GPS information. */\n  DMRContactReference _contact;\n  /** Holds the revert channel on which the GPS information is sent on. */\n  DMRChannelReference _revertChannel;\n};\n\n\n/** Represents an APRS system within the generic config.\n * @ingroup conf */\nclass FMAPRSSystem: public PositionReportingSystem\n{\n  Q_OBJECT\n\n  /** The transmit channel. */\n  Q_PROPERTY(FMChannelReference* revert READ revertChannelRef)\n  /** The destination call. */\n  Q_PROPERTY(QString destination READ destination WRITE setDestination SCRIPTABLE false)\n  /** The destination SSID. */\n  Q_PROPERTY(unsigned int destSSID READ destSSID WRITE setDestSSID SCRIPTABLE false)\n  /** The source call. */\n  Q_PROPERTY(QString source READ source WRITE setSource SCRIPTABLE false)\n  /** The source SSID. */\n  Q_PROPERTY(unsigned int srcSSID READ srcSSID WRITE setSrcSSID SCRIPTABLE false)\n  /** The APRS path as a string. */\n  Q_PROPERTY(QString path READ path WRITE setPath SCRIPTABLE false)\n\n  /** The APRS icon. */\n  Q_PROPERTY(Icon icon READ icon WRITE setIcon)\n  /** An optional text message. */\n  Q_PROPERTY(QString message READ message WRITE setMessage)\n  /** Anytone specific settings. */\n  Q_PROPERTY(AnytoneFMAPRSSettingsExtension *anytone READ anytoneExtension WRITE setAnytoneExtension)\n\npublic:\n  static const unsigned PRIMARY_TABLE   = (0<<8);   ///< Primary icon table flag.\n  static const unsigned SECONDARY_TABLE = (1<<8);   ///< Secondary icon table flag.\n  static const unsigned TABLE_MASK      = (3<<8);   ///< Bitmask for icon table flags.\n  static const unsigned ICON_MASK       = 0x7f;     ///< Bitmask for the icon table entry.\n\n  /** All implemented APRS icons. */\n  enum class Icon {\n    PoliceStation = (PRIMARY_TABLE | 0), None, Digipeater, Phone, DXCluster, HFGateway, SmallPlane,\n    MobileSatelliteStation, WheelChair, Snowmobile, RedCross, BoyScout, Home, X, RedDot,\n    Circle0, Circle1, Circle2, Circle3, Circle4, Circle5, Circle6, Circle7, Circle8, Circle9,\n    Fire, Campground, Motorcycle, RailEngine, Car, FileServer, HCFuture, AidStation, BBS, Canoe,\n    Eyeball = (PRIMARY_TABLE | 36), Tractor, GridSquare, Hotel, TCPIP, School = (PRIMARY_TABLE | 42),\n    Logon, MacOS, NTSStation, Balloon, PoliceCar, TBD, RV, Shuttle, SSTV, Bus, ATV, WXService, Helo,\n    Yacht, Windows, Jogger, Triangle, PBBS, LargePlane, WXStation, DishAntenna, Ambulance, Bike,\n    ICP, FireStation, Horse, FireTruck, Glider, Hospital, IOTA, Jeep, SmallTruck, Laptop, MicE,\n    Node, EOC, Rover, Grid, Antenna, PowerBoat, TruckStop, TruckLarge, Van, Water, XAPRS, Yagi,\n    Shelter\n  };\n  Q_ENUM(Icon)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit FMAPRSSystem(QObject *parent=nullptr);\n  /** Constructor for a APRS system.\n   * @param name Specifies the name of the APRS system. This property is just a name, it does not\n   *        affect the radio configuration.\n   * @param channel Specifies the transmit channel. This property is not optional. A transmit\n   *        channel must be specified to obtain a working APRS system.\n   * @param dest Specifies the destination call, APRS messages are sent to. Usually 'WIDE3' is a\n   *        reasonable setting.\n   * @param destSSID Specifies the destination SSID. Usually 3 is a reasonable choice.\n   * @param src Specifies the source call, usually you call has to be entered here.\n   * @param srcSSID The source SSID, usually 7 is a reasonable choice of handhelds.\n   * @param path Specifies the APRS path string.\n   * @param icon Specifies the map icon to send.\n   * @param message An optional message to send.\n   * @param period Specifies the auto-update period in seconds.\n   * @param parent Specifies the QObject parent object. */\n  FMAPRSSystem(const QString &name, FMChannel *channel, const QString &dest, unsigned destSSID,\n               const QString &src, unsigned srcSSID, const QString &path=\"\", Icon icon=Icon::Jogger,\n               const QString &message=\"\", const Interval &period=Interval::fromMinutes(5),\n               QObject *parent=nullptr);\n\n  bool copy(const ConfigItem &other);\n  ConfigItem *clone() const;\n\n  /** Returns @c true if a revert channel is set. If false, the APRS information is send on the\n   * current channel. */\n  bool hasRevertChannel() const;\n  /** Returns the transmit channel of the APRS system. */\n  FMChannel *revertChannel() const;\n  /** Sets the transmit channel of the APRS system. */\n  void setRevertChannel(FMChannel *revertChannel);\n  /** Resets the revert channel to the current one */\n  void resetRevertChannel();\n\n  /** Returns a reference to the revertChannelRef channel. */\n  const FMChannelReference *revertChannelRef() const;\n  /** Returns a reference to the revertChannelRef channel. */\n  FMChannelReference *revertChannelRef();\n\n  /** Returns the destination call. */\n  const QString &destination() const;\n  /** Returns the destination SSID. */\n  unsigned destSSID() const;\n  /** Sets the destination call and SSID. */\n  void setDestination(const QString &call, unsigned ssid);\n  /** Sets the destination call. */\n  void setDestination(const QString &call);\n  /** Sets the destination SSID. */\n  void setDestSSID(unsigned ssid);\n\n  /** Returns the source call. */\n  const QString &source() const;\n  /** Returns the source SSID. */\n  unsigned srcSSID() const;\n  /** Sets the source call and SSID. */\n  void setSource(const QString &call, unsigned ssid);\n  /** Sets the source call. */\n  void setSource(const QString &call);\n  /** Sets the source SSID. */\n  void setSrcSSID(unsigned ssid);\n\n  /** Returns the APRS path. */\n  const QString &path() const;\n  /** Sets the APRS path. */\n  void setPath(const QString &path);\n\n  /** Returns the map icon. */\n  Icon icon() const;\n  /** Sets the map icon. */\n  void setIcon(Icon icon);\n\n  /** Returns the optional message. */\n  const QString &message() const;\n  /** Sets the optional APRS message text. */\n  void setMessage(const QString &msg);\n\n  /** Returns the Anytone settings extension, if set. */\n  AnytoneFMAPRSSettingsExtension *anytoneExtension() const;\n  /** Sets the Anytone settings extension. */\n  void setAnytoneExtension(AnytoneFMAPRSSettingsExtension *ext);\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** A weak reference to the transmit channel. */\n  FMChannelReference _channel;\n  /** Holds the destination call. */\n  QString _destination;\n  /** Holds the destination SSID. */\n  unsigned _destSSID;\n  /** Holds the source call. */\n  QString _source;\n  /** Holds the source SSID. */\n  unsigned _srcSSID;\n  /** Holds the APRS path string. */\n  QString _path;\n  /** Holds the map icon. */\n  Icon _icon;\n  /** Holds the optional message. */\n  QString _message;\n  /** Owns the Anytone settings extension. */\n  AnytoneFMAPRSSettingsExtension *_anytone;\n};\n\n\n/** The list of positioning systems.\n * @ingroup conf */\nclass PositionReportingSystems: public ConfigObjectList\n{\nQ_OBJECT\n\npublic:\n  /** Constructs an empty list of GPS systems. */\n  explicit PositionReportingSystems(QObject *parent=nullptr);\n\n  /** Returns the positioning system at the specified index. */\n  PositionReportingSystem *system(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n#endif // GPSSYSTEM_H\n"
  },
  {
    "path": "lib/hid_libusb.cc",
    "content": "#include \"hid_libusb.hh\"\n#include \"logger.hh\"\n\n#define HID_INTERFACE   0                   // interface index\n#define TIMEOUT_MSEC    500                 // receive timeout\n#define MAX_RETRY       20                  // Number of retries\n\n/* ********************************************************************************************* *\n * Implementation of HIDevice::Descriptor\n * ********************************************************************************************* */\nHIDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device)\n  : USBDeviceDescriptor(info, USBDeviceHandle(bus, device))\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of HIDevice\n * ********************************************************************************************* */\nHIDevice::HIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : QObject(parent), _ctx(nullptr), _dev(nullptr), _transfer(nullptr)\n{\n  if (USBDeviceInfo::Class::HID != descr.interfaceClass()) {\n    errMsg(err) << \"Cannot connect to HID device using a non HID descriptor: \"\n                << descr.description() << \".\";\n    return;\n  }\n\n  int error = libusb_init(&_ctx);\n  if (error < 0) {\n    errMsg(err) << \"Libusb init failed (\" << error << \"): \"\n                << libusb_strerror((enum libusb_error) error) << \".\";\n    return;\n  }\n\n  int num=0;\n  libusb_device **lst;\n  libusb_device *dev=nullptr;\n  if (0 > (num = libusb_get_device_list(_ctx, &lst))) {\n    errMsg(err) << \"Cannot obtain list of USB devices.\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  logDebug() << \"Try to detect USB HID interface \" << descr.description() << \".\";\n  USBDeviceHandle addr = descr.device().value<USBDeviceHandle>();\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    if (addr.bus != libusb_get_bus_number(lst[i]))\n      continue;\n    if (addr.device != libusb_get_device_address(lst[i]))\n      continue;\n    libusb_device_descriptor usb_descr;\n    if (0 > libusb_get_device_descriptor(lst[i],&usb_descr))\n      continue;\n    if (descr.vendorId() != usb_descr.idVendor)\n      continue;\n    if (descr.productId() != usb_descr.idProduct)\n      continue;\n    logDebug() << \"Matching device found at bus \" << addr.bus << \", device \" << addr.device\n               << \" with vendor ID \" << QString::number(usb_descr.idVendor, 16)\n               << \" and product ID \" << QString::number(usb_descr.idProduct, 16) << \".\";\n    libusb_ref_device(lst[i]); dev = lst[i];\n  }\n  // Unref all devices and free list, matching device was referenced earlier\n  libusb_free_device_list(lst, 1);\n\n  if (nullptr == dev) {\n    errMsg(err) << \"No matching device found: \" << descr.description() << \".\";\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n    return;\n  }\n\n  if (0 > (error = libusb_open(dev, &_dev))) {\n    errMsg(err) << \"Cannot open device \" << descr.description()\n                << \": \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_unref_device(dev);\n    libusb_exit(_ctx);\n    _ctx = nullptr;\n  }\n\n  if (libusb_kernel_driver_active(_dev, 0)) {\n    error = libusb_detach_kernel_driver(_dev, 0);\n    if (error < 0) {\n      logWarn() << \"Failed to detach kernel from HID interface (\" << error\n                << \"): \" << libusb_strerror((enum libusb_error) error) <<\n                   \". Device claim will likely fail.\";\n    }\n  }\n\n  error = libusb_claim_interface(_dev, HID_INTERFACE);\n  if (error < 0) {\n    errMsg(err) << \"Failed to claim HID interface (\" << error\n                << \"): \" << libusb_strerror((enum libusb_error) error) << \".\";\n    libusb_close(_dev);\n    libusb_exit(_ctx);\n    _dev = nullptr;\n    _ctx = nullptr;\n  }\n}\n\nHIDevice::~HIDevice() {\n  close();\n}\n\nQList<USBDeviceDescriptor>\nHIDevice::detect(uint16_t vid, uint16_t pid) {\n  QList<USBDeviceDescriptor> res;\n\n  int error, num;\n  libusb_context *ctx;\n  if (0 > (error = libusb_init(&ctx))) {\n    logError() << \"Libusb init failed (\" << error << \"): \"\n               << libusb_strerror((enum libusb_error) error) << \".\";\n    return res;\n  }\n\n  libusb_device **lst;\n  if (0 == (num = libusb_get_device_list(ctx, &lst))) {\n    logDebug() << \"No USB devices found at all.\";\n    // unref devices and free list\n    libusb_free_device_list(lst, 1);\n    return res;\n  }\n\n  logDebug() << \"Search for HID interfaces matching VID:PID \"\n             << QString::number(vid, 16) << \":\" << QString::number(pid, 16) << \".\";\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    libusb_device_descriptor descr;\n    libusb_get_device_descriptor(lst[i], &descr);\n    if ((vid == descr.idVendor) && (pid == descr.idProduct)) {\n      logDebug() << \"Found device on bus=\" << libusb_get_bus_number(lst[i])\n                 << \", device=\" << libusb_get_device_address(lst[i])\n                 << \" matching \" << QString::number(vid, 16) << \":\"\n                 << QString::number(pid, 16) << \".\";\n      res.append(HIDevice::Descriptor(\n                   USBDeviceInfo(USBDeviceInfo::Class::HID, vid, pid),\n                   libusb_get_bus_number(lst[i]),\n                   libusb_get_device_address(lst[i])));\n    }\n  }\n\n  libusb_free_device_list(lst, 1);\n  return res;\n}\n\nbool\nHIDevice::isOpen() const {\n  return (nullptr != _ctx) && (nullptr != _dev);\n}\n\nvoid\nHIDevice::close() {\n  if (nullptr == _ctx)\n    return;\n\n  logDebug() << \"Closing HIDevice.\";\n\n  if (nullptr != _transfer) {\n    libusb_free_transfer(_transfer);\n    _transfer = nullptr;\n  }\n\n  if (nullptr != _dev) {\n    libusb_release_interface(_dev, HID_INTERFACE);\n    libusb_close(_dev);\n    _dev = nullptr;\n  }\n\n  libusb_exit(_ctx);\n  _ctx = nullptr;\n}\n\nbool\nHIDevice::hid_send_recv(const unsigned char *data, unsigned nbytes,\n                        unsigned char *rdata, unsigned rlength, const ErrorStack &err) {\n  unsigned char buf[42];\n  unsigned char reply[42];\n  int reply_len;\n\n  memset(buf, 0, sizeof(buf));\n  buf[0] = 1;\n  buf[1] = 0;\n  buf[2] = nbytes;\n  buf[3] = nbytes >> 8;\n  if (nbytes > 0)\n    memcpy(buf+4, data, nbytes);\n  nbytes += 4;\n\n  reply_len = write_read(buf, sizeof(buf), reply, sizeof(reply));\n  if (reply_len < 0) {\n    err.take(_cbError);\n    return false;\n  }\n\n  if (reply_len != sizeof(reply)) {\n    errMsg(err) << \"Short read: \" << reply_len\n                << \" bytes instead of \" << (int)sizeof(reply) << \"!\";\n    return false;\n  }\n  if (reply[0] != 3 || reply[1] != 0 || reply[3] != 0) {\n    errMsg(err) << \"Incorrect reply!\";\n    return false;\n  }\n  if (reply[2] != rlength) {\n    errMsg(err) << \"Incorrect reply length \" << reply[2]\n                << \", expected \" << rlength << \".\";\n    return false;\n  }\n\n  memcpy(rdata, reply+4, rlength);\n  return true;\n}\n\n\nint\nHIDevice::write_read(const unsigned char *data, unsigned length,\n                     unsigned char *reply, unsigned rlength, const ErrorStack &err)\n{\n  if (! _transfer) {\n    // Allocate transfer descriptor on first invocation.\n    _transfer = libusb_alloc_transfer(0);\n  }\n\n  libusb_fill_interrupt_transfer(\n        _transfer, _dev,\n        LIBUSB_RECIPIENT_INTERFACE | LIBUSB_ENDPOINT_IN,\n        reply, rlength, read_callback, this, TIMEOUT_MSEC);\n\n  size_t nretry = 0;\nagain:\n  _nbytes_received = 0;\n  libusb_submit_transfer(_transfer);\n\n  int result = libusb_control_transfer(\n        _dev,\n        LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,\n        0x09/*HID Set_Report*/, (2/*HID output*/ << 8) | 0,\n        HID_INTERFACE, (unsigned char*)data, length, TIMEOUT_MSEC);\n\n  if (result < 0) {\n    err.take(_cbError);\n    errMsg(err) << \"Error \" << result << \" transmitting data via control transfer: \"\n                << libusb_strerror((enum libusb_error) result) << \".\";\n    _transfer = nullptr;\n    return -1;\n  }\n\n  while (_nbytes_received == 0) {\n    result = libusb_handle_events(_ctx);\n    if (result < 0) {\n      /* Break out of this loop only on fatal error.*/\n      if (result != LIBUSB_ERROR_BUSY &&\n          result != LIBUSB_ERROR_TIMEOUT &&\n          result != LIBUSB_ERROR_OVERFLOW &&\n          result != LIBUSB_ERROR_INTERRUPTED)\n      {\n        err.take(_cbError);\n        errMsg(err) << \"Error \" <<result << \" receiving data via interrupt transfer: \"\n                    << libusb_strerror((enum libusb_error) result) << \".\";\n        return result;\n      }\n    }\n  }\n\n  if ((_nbytes_received == LIBUSB_ERROR_TIMEOUT) && (nretry < MAX_RETRY)) {\n    if (0 == nretry)\n      logDebug() << \"HID (libusb): timeout. Retry...\";\n    nretry++;\n    goto again;\n  } else if (nretry >= MAX_RETRY) {\n    logError() << \"HID (libusb): Retry limit of \" << MAX_RETRY << \" exceeded.\";\n  }\n\n  return _nbytes_received;\n}\n\n\nvoid\nHIDevice::read_callback(struct libusb_transfer *t)\n{\n  HIDevice *self = (HIDevice *)t->user_data;\n\n  switch (t->status) {\n  case LIBUSB_TRANSFER_COMPLETED:\n    memcpy(self->_receive_buf, t->buffer, t->actual_length);\n    self->_nbytes_received = t->actual_length;\n    break;\n\n  case LIBUSB_TRANSFER_CANCELLED:\n    self->_nbytes_received = LIBUSB_ERROR_INTERRUPTED;\n    errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_INTERRUPTED);\n    break;\n\n  case LIBUSB_TRANSFER_NO_DEVICE:\n    self->_nbytes_received = LIBUSB_ERROR_NO_DEVICE;\n    errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_NO_DEVICE);\n    break;\n\n  case LIBUSB_TRANSFER_TIMED_OUT:\n    self->_nbytes_received = LIBUSB_ERROR_TIMEOUT;\n    errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_TIMEOUT);\n    break;\n\n  default:\n    self->_nbytes_received = LIBUSB_ERROR_IO;\n    errMsg(self->_cbError) << libusb_error_name(LIBUSB_ERROR_IO);\n    break;\n  }\n}\n"
  },
  {
    "path": "lib/hid_libusb.hh",
    "content": "#ifndef HID_MACOS_HH\n#define HID_MACOS_HH\n\n#include <QObject>\n#include <libusb.h>\n#include \"errorstack.hh\"\n#include \"radiointerface.hh\"\n\n/** Implements the HID radio interface using libusb.\n * @ingroup rif */\nclass HIDevice: public QObject\n{\n\tQ_OBJECT\n\npublic:\n  /** Specialization to address a HI device uniquely. */\n  class Descriptor: public USBDeviceDescriptor\n  {\n  public:\n    /** Constructor from interface info, bus number and device address. */\n    Descriptor(const USBDeviceInfo &info, uint8_t bus, uint8_t device);\n  };\n\n\npublic:\n  /** Connects to the device with given vendor and product ID. */\n  HIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n\tvirtual ~HIDevice();\n\n  /** Returns @c true if the connection is established. */\n\tbool isOpen() const;\n  /** Send command/data to the device and store response in @c rdata.\n   * @param data Pointer to the command/data to send.\n   * @param nbytes The number of bytes to send.\n   * @param rdata Pointer to receive buffer.\n   * @param rlength Size of receive buffer.\n   * @param err Passes an error stack to put error messages on. */\n  bool hid_send_recv(const unsigned char *data, unsigned nbytes,\n                     unsigned char *rdata, unsigned rlength, const ErrorStack &err=ErrorStack());\n\n  /** Close connection to device. */\n\tvoid close();\n\npublic:\n  /** Finds all HID interfaces with the specified VID/PID combination. */\n  static QList<USBDeviceDescriptor> detect(uint16_t vid, uint16_t pid);\n\nprotected:\n  /** Internal used implementation of send_recv(). */\n  int write_read(const unsigned char *data, unsigned length,\n                 unsigned char *reply, unsigned rlength, const ErrorStack &err=ErrorStack());\n  /** Callback for response data. */\n  static void read_callback(struct libusb_transfer *t);\n\nprotected:\n  /** libusb context. */\n  libusb_context *_ctx;\n  /** libusb device. */\n  libusb_device_handle *_dev;\n\t/** libusb async transfer descriptor. */\n\tstruct libusb_transfer *_transfer;\n\t/** Receive buffer. */\n\tunsigned char _receive_buf[42];\n\t/** Receive result. */\n\tvolatile int _nbytes_received;\n  /** Internal used error stack for the static callback function. */\n  ErrorStack _cbError;\n};\n\n#endif // HID_MACOS_HH\n"
  },
  {
    "path": "lib/hid_macos.cc",
    "content": "#include \"hid_macos.hh\"\n#include <IOKit/IOKitLib.h>\n#include <IOKit/IOCFPlugIn.h>\n#include <IOKit/usb/IOUSBLib.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <logger.hh>\n\n\n/* ********************************************************************************************* *\n * Implementation of HIDevice::Descriptor\n * ********************************************************************************************* */\nHIDevice::Descriptor::Descriptor(const USBDeviceInfo &info, uint32_t locationId, uint16_t device)\n  : USBDeviceDescriptor(info, USBDeviceHandle((locationId>>24), device, locationId))\n{\n  // pass...\n}\n\n/* ********************************************************************************************* *\n * Implementation of HIDevice\n * ********************************************************************************************* */\nHIDevice::HIDevice(const USBDeviceDescriptor &desc, const ErrorStack &err, QObject *parent)\n  : QObject(parent), _dev(nullptr)\n{\n  // Create the USB HID Manager.\n  _HIDManager = IOHIDManagerCreate(kCFAllocatorDefault,\n                                   kIOHIDOptionsTypeNone);\n\n  // Create an empty matching dictionary for filtering USB devices in our HID manager.\n  CFMutableDictionaryRef matchDict = CFDictionaryCreateMutable(\n        kCFAllocatorDefault, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);\n\n  // Specify the USB device manufacturer and product in our matching dictionary.\n  int locationId = desc.device().value<USBDeviceHandle>().locationId;\n  CFDictionarySetValue(matchDict, CFSTR(kIOHIDLocationIDKey), CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &locationId));\n\n  // Apply the matching to our HID manager.\n  IOHIDManagerSetDeviceMatching(_HIDManager, matchDict);\n  CFRelease(matchDict);\n\n  // The HID manager will use callbacks when specified USB devices are connected/disconnected.\n  IOHIDManagerRegisterDeviceMatchingCallback(_HIDManager, &callback_open, this);\n  IOHIDManagerRegisterDeviceRemovalCallback(_HIDManager, &callback_close, this);\n\n  // Add the HID manager to the main run loop\n  IOHIDManagerScheduleWithRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);\n\n  // Open the HID manager\n  IOReturn IOReturn = IOHIDManagerOpen(_HIDManager, kIOHIDOptionsTypeNone);\n  if (IOReturn != kIOReturnSuccess) {\n    errMsg(err) << \"Cannot open HID manager for USB device \"\n                << QString::number(locationId,16) << \".\";\n    IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);\n    _HIDManager = nullptr;\n    return;\n  }\n\n  // Run loop until device found.\n  for (int k=0; k<4; k++) {\n    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0);\n    if (_dev)\n      return;\n    usleep(10000);\n  }\n\n  errMsg(err) << \"Cannot open USB device \"\n              << QString::number(locationId,16) << \".\";\n  IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);\n  IOHIDManagerClose(_HIDManager, kIOHIDOptionsTypeNone);\n  _HIDManager = nullptr;\n}\n\nHIDevice::~HIDevice() {\n  if (_dev)\n    close();\n  if (_HIDManager) {\n    IOHIDManagerUnscheduleFromRunLoop(_HIDManager, CFRunLoopGetMain(), kCFRunLoopDefaultMode);\n    IOHIDManagerClose(_HIDManager, kIOHIDOptionsTypeNone);\n  }\n}\n\nQList<USBDeviceDescriptor>\nHIDevice::detect(uint16_t vid, uint16_t pid)\n{\n  /* Get the IO registry which has the system information for connected hardware. */\n  io_registry_entry_t entry = IORegistryGetRootEntry(kIOMasterPortDefault);\n  if (entry == 0)\n    return QList<USBDeviceDescriptor>();\n\n  /* Get an iterator for the USB plane. */\n  io_iterator_t iter    = 0;\n  kern_return_t kret = IORegistryEntryCreateIterator(entry, kIOUSBPlane, kIORegistryIterateRecursively, &iter);\n  if (kret != KERN_SUCCESS || iter == 0)\n    return QList<USBDeviceDescriptor>();\n\n  QList<USBDeviceDescriptor> devices;\n\n  io_service_t service = 0;\n  while ((service = IOIteratorNext(iter))) {\n    IOCFPlugInInterface  **plug  = NULL;\n    IOUSBDeviceInterface **dev   = NULL;\n    io_string_t            path;\n    SInt32                 score = 0;\n    IOReturn               ioret;\n\n    /* Pull out IO Plugins for each iterator. */\n    kret = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plug, &score);\n    IOObjectRelease(service);\n    if (kret != KERN_SUCCESS || plug == NULL)\n      continue;\n\n    /* Get an IOUSBDeviceInterface for each USB device from the IO Plugin object. */\n    ioret = (*plug)->QueryInterface(plug, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (void **)&dev);\n    (*plug)->Release(plug);\n    if (ioret != kIOReturnSuccess || dev == NULL)\n       continue;\n\n    /* Print out the path in the IO Plane the device is at. */\n    if (IORegistryEntryGetPath(service, kIOServicePlane, path) != KERN_SUCCESS) {\n      (*dev)->Release(dev);\n      continue;\n    }\n\n    UInt16 dev_vendor, dev_product, dev_addr;\n    UInt32 dev_location;\n    if ( ((*dev)->GetDeviceVendor(dev, &dev_vendor) != kIOReturnSuccess) || (dev_vendor != vid) )\n      continue;\n    if ( ((*dev)->GetDeviceProduct(dev, &dev_product) != kIOReturnSuccess) || (dev_product != pid) )\n      continue;\n    if ((*dev)->GetLocationID(dev, &dev_location) != kIOReturnSuccess)\n      continue;\n    if ((*dev)->GetDeviceAddress(dev, &dev_addr) != kIOReturnSuccess)\n      continue;\n\n    logDebug() << \"Found device with vid:pid \" << QString::number(vid, 16)\n               << \":\" << QString::number(pid, 16) << \" at \"\n               << QString::number(dev_location, 16) << \".\";\n\n    devices.append(HIDevice::Descriptor(\n                     USBDeviceInfo(USBDeviceInfo::Class::HID,\n                                   vid, pid),\n                     dev_location, dev_addr));\n    /* All done with this device. */\n    (*dev)->Release(dev);\n  }\n  IOObjectRelease(iter);\n\n  return devices;\n}\n\nbool\nHIDevice::isOpen() const {\n  return nullptr != _dev;\n}\n\n//\n// Send a request to the device.\n// Store the reply into the rdata[] array.\n// Terminate in case of errors.\n//\nbool\nHIDevice::hid_send_recv(const unsigned char *data, unsigned nbytes,\n                        unsigned char *rdata, unsigned rlength, const ErrorStack &err)\n{\n  unsigned char buf[42];\n  unsigned k;\n  IOReturn result;\n\n  if (! isOpen())\n    return false;\n\n  memset(buf, 0, sizeof(buf));\n  buf[0] = 1;\n  buf[1] = 0;\n  buf[2] = nbytes;\n  buf[3] = nbytes >> 8;\n  if (nbytes > 0)\n    memcpy(buf+4, data, nbytes);\n  nbytes += 4;\n\n  _nbytes_received = 0;\n  memset(_receive_buf, 0, sizeof(_receive_buf));\n\n  uint retrycount = 0;\nagain:\n  // Write to HID device.\n  result = IOHIDDeviceSetReport(_dev, kIOHIDReportTypeOutput, 0, buf, sizeof(buf));\n  if (result != kIOReturnSuccess) {\n    errMsg(err) << \"HID output error: \" << result << \"!\";\n    return false;\n  }\n\n  // Run application loop until reply received.\n  CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0);\n  for (k = 0; _nbytes_received <= 0; k++) {\n    usleep(100);\n    CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, 0);\n    if (k >= 1000) {\n      retrycount++;\n      if (retrycount<100)\n        goto again;\n      errMsg(err) << \"HID IO error: Exceeded max. retry count.\";\n      return false;\n    }\n  }\n  usleep(100);\n\n  if (_nbytes_received != sizeof(_receive_buf)) {\n    errMsg(err) << \"Short read: \" << _nbytes_received << \" bytes instead of \"\n                << (int)sizeof(_receive_buf) << \"!\";\n    return false;\n  }\n\n  if ((_receive_buf[0] != 3) || (_receive_buf[1] != 0) || (_receive_buf[3] != 0)) {\n    errMsg(err) << \"Incorrect reply. Expected {3,0,0}, got {\" << int(_receive_buf[0]) << \",\"\n                << int(_receive_buf[1]) << \",\" << int(_receive_buf[3]) << \"}.\";\n    return false;\n  }\n\n  if (_receive_buf[2] != rlength) {\n    errMsg(err) << \"Incorrect reply length \" << (int)_receive_buf[2]\n                << \", expected \" << rlength << \"!\";\n    return false;\n  }\n\n  memcpy(rdata, _receive_buf+4, rlength);\n\n  return true;\n}\n\n//\n// Callback: data is received from the HID device\n//\nvoid\nHIDevice::callback_input(void *context, IOReturn result, void *sender, IOHIDReportType type,\n                         uint32_t reportID, uint8_t *data, CFIndex nbytes)\n{\n  Q_UNUSED(sender);\n  Q_UNUSED(type);\n  Q_UNUSED(reportID);\n\n  HIDevice *self = reinterpret_cast<HIDevice *>(context);\n\n  if (result != kIOReturnSuccess) {\n    logError() << \"HID input error: \" << result << \".\";\n    self->close();\n    return;\n  }\n\n  if (nbytes > CFIndex(sizeof(self->_receive_buf))) {\n    logError() << \"Too large HID input: \" << (int)nbytes << \" bytes!\";\n    self->close();\n    return;\n  }\n\n  self->_nbytes_received = nbytes;\n  if (nbytes > 0)\n    memcpy(self->_receive_buf, data, nbytes);\n}\n\n//\n// Callback: device specified in the matching dictionary has been added\n//\nvoid\nHIDevice::callback_open(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef)\n{\n  Q_UNUSED(result);\n  Q_UNUSED(sender);\n\n  HIDevice *self = reinterpret_cast<HIDevice *>(context);\n  IOReturn o = IOHIDDeviceOpen(deviceRef, kIOHIDOptionsTypeSeizeDevice);\n  if (o != kIOReturnSuccess) {\n    logError() << \"Cannot open HID device!\";\n    return;\n  }\n\n  // Register input callback.\n  IOHIDDeviceRegisterInputReportCallback(deviceRef, self->_transfer_buf, sizeof(self->_transfer_buf),\n                                         callback_input, self);\n\n  self->_dev = deviceRef;\n}\n\n//\n// Callback: device specified in the matching dictionary has been removed\n//\nvoid\nHIDevice::callback_close(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef)\n{\n  Q_UNUSED(result);\n  Q_UNUSED(sender);\n\n  HIDevice *self = reinterpret_cast<HIDevice *>(context);\n\n  // De-register input callback.\n  IOHIDDeviceRegisterInputReportCallback(\n        deviceRef, self->_transfer_buf, sizeof(self->_transfer_buf), NULL, NULL);\n}\n\n//\n// Close HID device.\n//\nvoid\nHIDevice::close()\n{\n  if (! _dev)\n    return;\n  IOHIDDeviceClose(_dev, kIOHIDOptionsTypeNone);\n  _dev = nullptr;\n}\n\n"
  },
  {
    "path": "lib/hid_macos.hh",
    "content": "#ifndef HID_MACOS_HH\n#define HID_MACOS_HH\n\n#include <QObject>\n#include <IOKit/hid/IOHIDManager.h>\n#include \"errorstack.hh\"\n#include \"radiointerface.hh\"\n\n/** Implements the HID radio interface MacOS X API.\n * @ingroup rif */\nclass HIDevice: public QObject\n{\n\tQ_OBJECT\n\npublic:\n  /** Specialization to address a HI device uniquely. */\n  class Descriptor: public USBDeviceDescriptor\n  {\n  public:\n    /** Constructor from interface info, bus number and device address. */\n    Descriptor(const USBDeviceInfo &info, uint32_t locationId, uint16_t device);\n  };\n\npublic:\n  /** Opens a connection to the device with given vendor and product ID. */\n\tHIDevice(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n\tvirtual ~HIDevice();\n\n  /** Returns @c true if the connection was established. */\n\tbool isOpen() const;\n\n  /** Send command/data to the device and store response in @c rdata.\n   * @param data Pointer to the command/data to send.\n   * @param nbytes The number of bytes to send.\n   * @param rdata Pointer to receive buffer.\n   * @param rlength Size of receive buffer.\n   * @param err The stack to put error messages on. */\n\tbool hid_send_recv(const unsigned char *data, unsigned nbytes,\n                     unsigned char *rdata, unsigned rlength,\n                     const ErrorStack &err=ErrorStack());\n\n  /** Close connection to device. */\n\tvoid close();\n\npublic:\n  /** Finds all HID interfaces with the specified VID/PID combination. */\n  static QList<USBDeviceDescriptor> detect(uint16_t vid, uint16_t pid);\n\nprotected:\n  /** Internal callback for response data. */\n\tstatic void callback_input(void *context, IOReturn result, void *sender, IOHIDReportType type,\n\t                           uint32_t reportID, uint8_t *data, CFIndex nbytes);\n  /** Internal callback for device opend. */\n\tstatic void callback_open(void *context, IOReturn result, void *sender, IOHIDDeviceRef deviceRef);\n  /** Internal callback for device closed. */\n\tstatic void callback_close(void *ontext, IOReturn result, void *sender, IOHIDDeviceRef deviceRef);\n\nprotected:\n  /** Device manager. */\n  IOHIDManagerRef _HIDManager;\n\t/** Device handle. */\n\tvolatile IOHIDDeviceRef _dev;\n\t/** Device buffer. */\n\tunsigned char _transfer_buf[42];\n\t/** Receive buffer. */\n\tunsigned char _receive_buf[42];\n\t/** Receive result. */\n\tvolatile int _nbytes_received = 0;\n};\n\n#endif // HID_MACOS_HH\n"
  },
  {
    "path": "lib/intermediaterepresentation.cc",
    "content": "#include \"intermediaterepresentation.hh\"\n#include \"configobject.hh\"\n#include \"zone.hh\"\n#include \"encryptionextension.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneSplitVisitor\n * ********************************************************************************************* */\nZoneSplitVisitor::ZoneSplitVisitor()\n  : Visitor()\n{\n  // pass...\n}\n\nbool\nZoneSplitVisitor::processItem(ConfigItem *item, const ErrorStack &err) {\n  // Skip non-zones\n  if (! item->is<Zone>())\n    return Visitor::processItem(item, err);\n  Zone *zone = item->as<Zone>();\n\n  // skip zones with empty B list\n  if (0 == zone->B()->count())\n    return Visitor::processItem(item, err);\n\n  // create new zone with B list as A list, clear B list of \"old\" zone\n  Zone *newZone = new Zone();\n  while (zone->B()->count()) {\n    newZone->A()->add(zone->B()->get(0));\n    zone->B()->take(zone->B()->get(0));\n  }\n\n  // set names\n  newZone->setName(QString(\"%1 B\").arg(zone->name()));\n  zone->setName(QString(\"%1 A\").arg(zone->name()));\n\n  // add zone to list of zones\n  ConfigObjectList *lst = qobject_cast<ConfigObjectList*>(item->parent());\n  int idx = lst->indexOf(zone);\n  lst->add(newZone, idx+1);\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneMergeVisitor\n * ********************************************************************************************* */\nZoneMergeVisitor::ZoneMergeVisitor()\n  : Visitor(), _lastZone(nullptr), _mergedZones()\n{\n  // pass...\n}\n\nbool\nZoneMergeVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) {\n  if (nullptr == qobject_cast<ZoneList *>(list))\n    return Visitor::processList(list, err);\n\n  if (2 > list->count())\n    return Visitor::processList(list, err);\n\n  _mergedZones.clear();\n  _lastZone = nullptr;\n\n  if (! processItem(list->get(0), err))\n    return false;\n  _lastZone = list->get(0)->as<Zone>();\n\n  for (int i=1; i<list->count(); i++) {\n    if (! processItem(list->get(i), err))\n      return false;\n    _lastZone = list->get(i)->as<Zone>();\n  }\n\n  foreach(Zone *zone, _mergedZones) {\n    list->del(zone);\n  }\n  _mergedZones.clear();\n\n  return true;\n}\n\nbool\nZoneMergeVisitor::processItem(ConfigItem *item, const ErrorStack &err) {\n  if (! item->is<Zone>())\n    return Visitor::processItem(item, err);\n  Zone *currentZone = item->as<Zone>();\n\n  if ((! _lastZone) || (!_lastZone->name().endsWith(\" A\")) || (0 != _lastZone->B()->count()))\n    return Visitor::processItem(item, err);\n\n  if ((!currentZone->name().endsWith(\" B\")) || (0 != currentZone->B()->count()))\n    return Visitor::processItem(item, err);\n\n  while (currentZone->A()->count()) {\n    ConfigObject *ch = currentZone->A()->get(0);\n    _lastZone->B()->add(ch);\n    currentZone->A()->del(ch);\n  }\n\n  _lastZone->setName(_lastZone->name().chopped(2));\n  _mergedZones.append(currentZone);\n\n  return Visitor::processItem(item);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of AbstractObjectFilterVisitor\n * ********************************************************************************************* */\nAbstractObjectFilterVisitor::AbstractObjectFilterVisitor()\n  : Visitor()\n{\n  // pass...\n}\n\nbool\nAbstractObjectFilterVisitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) {\n  if (! propIsInstance<ConfigItem>(prop))\n    return Visitor::processProperty(item, prop, err);\n\n  if (prop.read(item).isNull())\n    return Visitor::processProperty(item, prop, err);\n\n  if (! prop.isWritable())\n    return Visitor::processProperty(item, prop, err);\n\n  ConfigItem *propItem = prop.read(item).value<ConfigItem *>();\n  if (toRemove(propItem)) {\n    if (prop.write(item, QVariant(prop.metaType())))\n      return true;\n    errMsg(err) << \"Cannot delete instance of \" << item->metaObject()->className()\n                << \" from property \" << prop.name() << \".\";\n    return false;\n  }\n\n  return Visitor::processProperty(item, prop, err);\n}\n\nbool\nAbstractObjectFilterVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) {\n  if (qobject_cast<ConfigObjectRefList *>(list))\n    return Visitor::processList(list, err);\n\n  ConfigObjectList *objList = qobject_cast<ConfigObjectList *>(list);\n  if (nullptr == objList)\n    return Visitor::processList(list, err);;\n\n  QList<ConfigObject *> filtered;\n  for (int i=0; i<objList->count(); i++) {\n    if (toRemove(objList->get(i)))\n      filtered.append(objList->get(i));\n  }\n\n  foreach (ConfigObject *item, filtered)\n    objList->del(item);\n\n  return Visitor::processList(list, err);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of ObjectFilterVisitor\n * ********************************************************************************************* */\nObjectFilterVisitor::ObjectFilterVisitor(const std::initializer_list<QMetaObject> &types)\n  : AbstractObjectFilterVisitor(), _filter(types)\n{\n  // pass...\n}\n\nbool\nObjectFilterVisitor::toRemove(ConfigItem *item) {\n  foreach (const QMetaObject &meta, _filter)\n    if (item->inherits(meta.className()))\n      return true;\n  return false;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of EncryptionKeyFilterVisitor::Filter\n * ********************************************************************************************* */\nEncryptionKeyFilterVisitor::Filter::Filter(const QMetaObject &meta_type, unsigned int bits)\n  : type(meta_type), minBits(bits), maxBits(bits)\n{\n  // pass...\n}\n\nEncryptionKeyFilterVisitor::Filter::Filter(const QMetaObject &meta_type, unsigned int min_bits, unsigned int max_bits)\n  : type(meta_type), minBits(min_bits), maxBits(max_bits)\n{\n  // pass...\n}\n\nbool\nEncryptionKeyFilterVisitor::Filter::accepts(const EncryptionKey *key) const {\n  return key->inherits(type.className()) && (key->key().size()*8>=minBits)\n      && (key->key().size()*8<=maxBits);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of EncryptionKeyFilterVisitor\n * ********************************************************************************************* */\nEncryptionKeyFilterVisitor::EncryptionKeyFilterVisitor(const std::initializer_list<Filter> &filter)\n  : AbstractObjectFilterVisitor(), _filter(filter)\n{\n  // pass...\n}\n\nbool\nEncryptionKeyFilterVisitor::toRemove(ConfigItem *item) {\n  if (! item->is<EncryptionKey>())\n    return false;\n\n  auto key = item->as<EncryptionKey>();\n  foreach (const Filter &filter, _filter) {\n    if (filter.accepts(key))\n      return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/intermediaterepresentation.hh",
    "content": "/** @defgroup ir Intermediate representation.\n * This module collects visitors applied to a codeplug before the actual encoding of the device\n * specific binary codeplug. This preprocessing step eases the encoding a lot.\n *\n * @ingroup config */\n\n#ifndef INTERMEDIATEREPRESENTATION_HH\n#define INTERMEDIATEREPRESENTATION_HH\n\n#include \"visitor.hh\"\n#include <QList>\n\nclass Zone;\nclass EncryptionKey;\n\n\n/** Simple visitor that splits Zones having A and B channels into two zones with A-lists only.\n * This is a pre-processing step for many radios, where zones consists of a single list of channels\n * and a zone is selected for each VFO separately.\n * @ingroup ir */\nclass ZoneSplitVisitor: public Visitor\n{\npublic:\n  /** Constructor. */\n  explicit ZoneSplitVisitor();\n\n  bool processItem(ConfigItem *item, const ErrorStack &err);\n};\n\n/** Simple visitor that merges zones. This is the reverse step of the @c ZoneSplitVisitor.\n * Two subsequent zones are only merged into one, if both zones have empty B lists, the name of the\n * first zone ends on \"... A\" and the name of the second ends on \"... B\". That is, if the two zones\n * where likely split by qdmr. */\nclass ZoneMergeVisitor: public Visitor\n{\npublic:\n  /** Constructor. */\n  explicit ZoneMergeVisitor();\n\n  bool processList(AbstractConfigObjectList *list, const ErrorStack &err);\n  bool processItem(ConfigItem *item, const ErrorStack &err);\n\nprotected:\n  /** The last zone visited, @c nullptr if the first zone is processed. */\n  Zone *_lastZone;\n  /** Zones to be removed. */\n  QList<Zone *> _mergedZones;\n};\n\n\n\n/** Filters instances by a test function.\n *\n * This visitor can be used to remove elements from the abstract codeplug, not supported by the\n * target device. Use @c ObjectFilterVisitor to remove instances of specific classes.\n */\nclass AbstractObjectFilterVisitor: public Visitor\n{\nprotected:\n  /** Hidden constructor. */\n  explicit AbstractObjectFilterVisitor();\n\npublic:\n  bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err);\n  bool processList(AbstractConfigObjectList *list, const ErrorStack &err);\n\nprotected slots:\n  /** Abstract test function. If this function returns @c true, the item will be removed. */\n  virtual bool toRemove(ConfigItem *item) = 0;\n};\n\n\n\n/** Filters instance by meta object.\n *\n * This visitor can be used to remove elements from the abstract codeplug, not supported by the\n * target device. */\nclass ObjectFilterVisitor: public AbstractObjectFilterVisitor\n{\npublic:\n  /** Constructor from initializer list of Qt meta objects. */\n  explicit ObjectFilterVisitor(const std::initializer_list<QMetaObject> &types);\n\nprotected slots:\n  bool toRemove(ConfigItem *item);\n\nprotected:\n  /** The list of filtered types. */\n  QList<QMetaObject> _filter;\n};\n\n\n/** Only keeps those encryption keys, that match a specific type and size. */\nclass EncryptionKeyFilterVisitor: public AbstractObjectFilterVisitor\n{\npublic:\n  struct Filter {\n    QMetaObject type;\n    unsigned int minBits;\n    unsigned int maxBits;\n\n    Filter(const QMetaObject &type, unsigned int bits);\n    Filter(const QMetaObject &type, unsigned int minBits, unsigned int maxBits);\n\n    bool accepts(const EncryptionKey *key) const;\n  };\n\npublic:\n  EncryptionKeyFilterVisitor(const std::initializer_list<Filter> &filter);\n\nprotected:\n  bool toRemove(ConfigItem *item);\n\nprotected:\n  QList<Filter> _filter;\n};\n\n#endif // INTERMEDIATEREPRESENTATION_HH\n"
  },
  {
    "path": "lib/interval.cc",
    "content": "#include \"interval.hh\"\n#include <QRegularExpression>\n\nQString\nInterval::format(Format f) const {\n  if (isNull())\n    return \"0\";\n\n  if (isInfinite())\n    return \"infinite\";\n\n  switch (f) {\n  case Format::Automatic:\n    if (0 == _duration % 60000UL)\n      return format(Format::Minutes);\n    else if (0 == _duration % 1000UL)\n      return format(Format::Seconds);\n    return format(Format::Milliseconds);\n  case Format::Minutes:\n    return QString(\"%1 min\").arg(_duration/60000UL);\n  case Format::Seconds:\n    return QString(\"%1 s\").arg(_duration/1000UL);\n  case Format::Milliseconds:\n    return QString(\"%1 ms\").arg(_duration);\n  }\n\n  return QString(\"%1 ms\").arg(_duration);\n}\n\nbool\nInterval::parse(const QString &value, Format format) {\n  if (value.toLower().startsWith(\"inf\")) {\n    _duration = std::numeric_limits<unsigned long long>::max();\n    return true;\n  }\n\n  QRegularExpression ex(R\"(\\s*([0-9]+)\\s*(min|s|ms|)\\s*)\");\n  QRegularExpressionMatch match = ex.match(value);\n  if (! match.hasMatch())\n    return false;\n\n  bool hasUnit = match.capturedLength(2);\n  QString unit = match.captured(2);\n  QString dur = match.captured(1);\n\n  if ((hasUnit && (\"s\"==unit)) || (!hasUnit && (Format::Seconds == format)))\n    _duration = dur.toULongLong()*1000ULL;\n  else if ((hasUnit && (\"min\"==unit)) || (!hasUnit && (Format::Minutes == format)))\n    _duration = dur.toULongLong()*60000ULL;\n  else\n    _duration = dur.toULongLong();\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/interval.hh",
    "content": "#ifndef INTERVAL_HH\n#define INTERVAL_HH\n\n#include <QString>\n#include <QMetaType>\n#include <yaml-cpp/yaml.h>\n\n/** Represents a time interval.\n * @ingroup utils */\nclass Interval\n{\npublic:\n  /** Possible formats. */\n  enum class Format {\n    Automatic, Milliseconds, Seconds, Minutes\n  };\n\nprotected:\n  /** Constructor from milliseconds. */\n  constexpr explicit Interval(unsigned long long ms)\n    : _duration(ms)\n  {\n    // pass...\n  }\n\npublic:\n  /** Default constructor. */\n  Interval()\n    : _duration(0)\n  {\n    // pass...\n  }\n\n  /** Copy constructor. */\n  constexpr Interval(const Interval &other)\n    : _duration(other._duration)\n  {\n    // pass...\n  }\n\n  inline Interval &operator =(const Interval &other) {   ///< Assignment.\n    _duration = other._duration; return *this;\n  }\n\n  inline bool isNull() const { return 0 == _duration; }  ///< Test for 0.\n  /** Test for infinite durations. */\n  inline bool isInfinite() const {\n    return std::numeric_limits<unsigned long long>::max() == _duration;\n  }\n  /** Test for finite values. */\n  inline bool isFinite() const { return (!isNull()) && (!isInfinite()); }\n\n  inline bool operator ==(const Interval &other) const { ///< Comparison.\n    return _duration == other._duration;\n  }\n  inline bool operator<  (const Interval &other) const {///< Comparison.\n    return _duration < other._duration;\n  }\n  inline bool operator<=  (const Interval &other) const {///< Comparison.\n    return _duration <= other._duration;\n  }\n  inline bool operator>  (const Interval &other) const {///< Comparison.\n    return _duration > other._duration;\n  }\n  inline bool operator>=  (const Interval &other) const {///< Comparison.\n    return _duration >= other._duration;\n  }\n\n  inline unsigned long long milliseconds() const { return _duration; }      ///< Unit conversion.\n  inline unsigned long long seconds() const { return _duration/1000ULL; }   ///< Unit conversion.\n  inline unsigned long long minutes() const { return _duration/60000ULL; }  ///< Unit conversion.\n\n  static inline constexpr Interval fromMilliseconds(unsigned long long ms) {          ///< Unit conversion.\n    return Interval(ms);\n  }\n  static inline constexpr Interval fromSeconds(unsigned long long s) {                ///< Unit conversion.\n    return Interval(s*1000ULL);\n  }\n  static inline constexpr Interval fromMinutes(unsigned long long min) {              ///< Unit conversion.\n    return Interval(min*60000ULL);\n  }\n\n  /** Format the frequency. */\n  QString format(Format f=Format::Automatic) const;\n  /** Parses a frequency. */\n  bool parse(const QString &value, Format defaultFormat=Format::Milliseconds);\n\npublic:\n  /** Constructs a null interval. */\n  static inline Interval null() { return Interval(0); }\n  /** Constructs an infinite interval. */\n  static inline Interval infinity() {\n    return Interval(std::numeric_limits<unsigned long long>::max());\n  }\n\nprivate:\n  /** An interval duration in ms. */\n  unsigned long long _duration;\n};\n\nQ_DECLARE_METATYPE(Interval)\n\n\nnamespace YAML\n{\n  /** Implements the conversion to and from YAML::Node. */\n  template<>\n  struct convert<Interval>\n  {\n    /** Serializes the interval. */\n    static Node encode(const Interval& rhs) {\n      return Node(rhs.format().toStdString());\n    }\n\n    /** Parses the interval. */\n    static bool decode(const Node& node, Interval& rhs) {\n      if (!node.IsScalar())\n        return false;\n      return rhs.parse(QString::fromStdString(node.as<std::string>()));\n    }\n  };\n}\n\n\n#endif // INTERVAL_HH\n"
  },
  {
    "path": "lib/level.cc",
    "content": "#include \"level.hh\"\n\n\nLevel::Level()\n  : _level(std::numeric_limits<unsigned int>::max())\n{\n  // pass...\n}\n\n\nQString\nLevel::format() const{\n  if (isNull())\n    return \"off\";\n  else if (isInvalid())\n    return \"none\";\n  return QString::number(_level);\n}\n\n\nbool\nLevel::parse(const QString &value) {\n  if (\"off\" == value.toLower().simplified()) {\n    _level = 0;\n    return true;\n  }\n\n  if (\"none\" == value.toLower().simplified()) {\n    _level = std::numeric_limits<unsigned int>::max();\n    return true;\n  }\n\n  bool ok;\n  _level = std::min(10U, value.toUInt(&ok));\n  return ok;\n}\n\n\n"
  },
  {
    "path": "lib/level.hh",
    "content": "#ifndef LEVEL_HH\n#define LEVEL_HH\n\n#include <QString>\n#include <QMetaType>\n#include <limits>\n#include <yaml-cpp/yaml.h>\n#include \"codeplug.hh\"\n\n\n\n/** Some simple class implementing a [1-10] level setting. Also supports the \"values\" null and\n * invalid. */\nclass Level\n{\nprotected:\n  /** Constructor from value. */\n  inline constexpr Level(unsigned int value) : _level(value) {}\n\npublic:\n  /** Default constructor. */\n  Level();\n\n  inline constexpr Level(const Level &other)\n    : _level(other._level)\n  {\n    // pass...\n  }\n\n  inline Level &operator =(const Level &other) = default;\n\n  inline bool isNull() const { return 0 == _level; }  ///< Test for 0.\n  /** Test for invalid level. */\n  inline bool isInvalid() const {\n    return std::numeric_limits<unsigned int>::max() == _level;\n  }\n  /** Test for finite values. That is, if the value is neither null nor invalid. */\n  inline bool isFinite() const { return (!isNull()) && (!isInvalid()); }\n\n  inline bool operator !=(const Level &other) const { ///< Comparison.\n    return _level != other._level;\n  }\n  inline bool operator ==(const Level &other) const { ///< Comparison.\n    return _level == other._level;\n  }\n  inline bool operator<  (const Level &other) const {///< Comparison.\n    return _level < other._level;\n  }\n  inline bool operator<=  (const Level &other) const {///< Comparison.\n    return _level <= other._level;\n  }\n  inline bool operator>  (const Level &other) const {///< Comparison.\n    return _level > other._level;\n  }\n  inline bool operator>=  (const Level &other) const {///< Comparison.\n    return _level >= other._level;\n  }\n\n  /** Returns the value of the level. */\n  inline unsigned int value() const { return _level; }\n  inline unsigned int mapTo(const Codeplug::Element::Limit::Range<unsigned int> &range) const {\n    if (isNull() || isInvalid())\n      return 0;\n    return Codeplug::Element::Limit::Range<unsigned int>{1,10}.mapTo(range, value());\n  }\n\n  /** Format the frequency. */\n  QString format() const;\n  /** Parses a frequency. */\n  bool parse(const QString &value);\n\npublic:\n  /** Constructs null level. */\n  inline static constexpr Level null() { return Level(0); }\n  /** Constructs an invalid level. */\n  inline static constexpr Level invalid() { return Level(std::numeric_limits<unsigned int>::max()); }\n  /** Constructs a proper level. */\n  inline static constexpr Level fromValue(unsigned int value, const Codeplug::Element::Limit::Range<unsigned int> range={1,10}) {\n    // If 0 is not in normal range -> always may 0 -> 0 (e.g. means off).\n    if ((0 == value) && (0 != range.min))\n      return Level::null();\n    return Level(range.mapTo({1,10},value));\n  }\n\nprotected:\n  /** The actual level value. */\n  unsigned int _level;\n};\n\n\nQ_DECLARE_METATYPE(Level)\n\n\nnamespace YAML\n{\n/** Implements the conversion to and from YAML::Node. */\ntemplate<>\nstruct convert<Level>\n{\n  /** Serializes the interval. */\n  static Node encode(const Level& rhs) {\n    return Node(rhs.format().toStdString());\n  }\n\n  /** Parses the interval. */\n  static bool decode(const Node& node, Level& rhs) {\n    // Usually means default level\n    if (node.IsNull()) {\n      rhs = Level::invalid();\n      return true;\n    }\n    // Fails on non-scalars\n    if (!node.IsScalar())\n      return false;\n    // Otherwise, parse string.\n    return rhs.parse(QString::fromStdString(node.as<std::string>()));\n  }\n};\n}\n\n\n#endif // LEVEL_HH\n"
  },
  {
    "path": "lib/libdmrconf.hh",
    "content": "/** @mainpage libdmrconf -- A Library to program DMR radios.\n * Libdmrconf is a Qt based library, providing methods to assemble, read and program codeplugs for\n * cheap Chinese DMR radios. To this end, it aims at providing a common way to describe codeplugs\n * irrespective of the actual radio being used.\n *\n * The aim of this project is, to define a minimal but sufficient generic configuration for all\n * radios, such that all relevant ham-radio features of the radios can be used. This is a challenge,\n * as there are many manufacturers, each selling several different models, each having\n * a variety of different features. In fact, manufacturers provide a separate application to program\n * each model. The resulting codeplugs are usually stored in a binary format,\n * similar to the actual codeplug being loaded onto the radio.\n *\n * This makes absolutely sense for commercial applications, like radios used by staff at large\n * events. In these cases, the organizer of the event would buy large quantities of identical radios and\n * assemble a single codeplug for all radios.\n *\n * For ham-radio purposes, however, this incompatibility between models and manufactures is an\n * issue. Assembling a decent codeplug for a region with several repeaters is cumbersome. As these\n * applications cannot share codeplugs between models let alone between manufactures, this\n * cumbersome work has to be repeated for each model, although the basic configuration of these\n * models like contact, channels & zones remains identical. In fact, many features of these radios\n * are specific to the commercial application and are not used in ham-radio context.\n *\n * A common minimalistic configuration of these radios for ham-radio use would allow for sharing\n * codeplugs between different models and even manufactures. Hence, the hard work to\n * assemble a decent codeplug for a particular region only needs to be done once.\n *\n * To this end, @c libdmrconf defines a configuration language in text format, that represents the\n * configuration of the radio and from which the actual binary code-plug for each radio is derived.\n * If a radio does not support a particular feature within the configuration, it simply gets\n * ignored. The configuration language as well as classes for reading and writing config files are\n * documented in the <a href=\"https://dm3mat.darc.de/qdmr/manual/#conf\">manual</a>.\n *\n * @section usage Using libdmrconf\n *   - Using cmake to find libdmrconf\n *   - setting paths\n *   - linking\n *   - include files\n *\n * @section interface Interfacing radios\n * In a first step, detect all USB devices that could be connected radios. Some radios use generic\n * USB CDC-AMC (USB-to-serial) chips. In these cases be careful, that you do not confuse them with\n * other hardware connected to the host. So, to detect USB interfaces use the device specific\n * interfaces, e.g. @c AnytoneInterface::detect(), @c RadioddityInterface::detect() or\n * @c TyTInterface::detect(). For example\n * @code\n *  #include <libdmrconf.hh>\n *\n *  int main(void) {\n *    // Find any supported devices connected\n *    QList<USBDeviceDescriptor> devices = AnytoneInterface::detect() +\n *                                         TyTInterface::detect() +\n *                                         RadioddityInterface::detect() +\n *                                         OpenGD77Interface::detect();\n *    if (0 == devices.size()) {\n *      std::cerr << \"No matching device found.\";\n *      return -1;\n *    }\n *\n *    // As the connected radio may use a generic USB CDC-ACM chip, check if the interface is safe.\n *    if (! devices.first().isSave()) {\n *      std::cerr << \"Sorry, the detected device is not save to talk to automatically, \"\n *                   \"check if the chosen device is the correct one.\";\n *      return -1;\n *    }\n *\n *    // Then try to connect to the first found.\n *    // The radio interface is common to all devices. So the remaining code is device independent.\n *    Radio *radio = Radio::detect(devices.first(), RadioInfo(), err);\n *    if (nullptr == radio) {\n *      std::cerr << \"Oops:\" << err.format();\n *      return -1;\n *    }\n *\n *    // Download codeplug\n *    if (! radio->startDownload(true, err)) {\n *      std::cerr << \"Error while downloading codeplug:\" << err.format();\n *      delete radio;\n *      return -1;\n *    }\n *\n *    // Decode codeplug to device independent config\n *    Config config;\n *    if (! radio->codeplug().decode(&config, err)) {\n *      std::cerr << \"Cannot decode codeplug:\" << err.format();\n *      delete radio;\n *      return -1;\n *    }\n *\n *    // Now, manipulate the generic codeplug in config.\n *\n *    // Verify codeplug with radio\n *    RadioLimitContext issues;\n *    if (! radio->limits().verifyConfig(config, issues)) {\n *      // Dump issues...\n *      delete radio;\n *      return -1;\n *    }\n *\n *    // Encode and upload codeplug back to the device\n *    if (! radio->startUpload(config, true, Codeplug::Flags(), err)) {\n *      std::err << \"Cannot write codeplug: \" << err.format();\n *      delete radio;\n *      return -1;\n *    }\n *\n *    // Done.\n *    delete radio;\n *    return 0;\n *  }\n * @endcode\n *\n * @section codeplug Reading and writing codeplug files\n *   - Using YAML for reading and writing codeplug files\n *\n * @section config Manipulating codeplugs\n *   - Some examples for basic manipulations of the @c Config object.\n *\n */\n\n#ifndef __LIBDMRCONF_HH__\n#define __LIBDMRCONF_HH__\n\n#include \"config.h\"\n#include \"config.hh\"\n#include \"csvreader.hh\"\n\n#include \"radio.hh\"\n\n#include \"anytone_interface.hh\"\n#include \"tyt_interface.hh\"\n#include \"opengd77_interface.hh\"\n#include \"radioddity_interface.hh\"\n\n#endif // __LIBDMRCONF_HH__\n"
  },
  {
    "path": "lib/logger.cc",
    "content": "#include \"logger.hh\"\n#include <QFileInfo>\n#include <QDir>\n#include <QDateTime>\n\n\n/* ********************************************************************************************* *\n * Implementation of LogMessage\n * ********************************************************************************************* */\nLogMessage::LogMessage(Level level, const QString &file, int line, const QString &message)\n  : QTextStream(), _level(level), _file(file), _line(line), _message(message)\n{\n  this->setString(&_message);\n  this->seek(_message.size());\n}\n\nLogMessage::LogMessage(const LogMessage &other)\n  : QTextStream(), _level(other._level), _file(other._file), _line(other._line), _message(other._message)\n{\n  this->setString(&_message);\n  this->seek(_message.size());\n}\n\nLogMessage::~LogMessage() {\n  Logger::get().log(*this);\n}\n\nLogMessage::Level\nLogMessage::level() const {\n  return _level;\n}\n\nconst QString &\nLogMessage::file() const {\n  return _file;\n}\n\nint\nLogMessage::line() const {\n  return _line;\n}\n\nconst QString &\nLogMessage::message() const {\n  return _message;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of LogHandler\n * ********************************************************************************************* */\nLogHandler::LogHandler(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nLogHandler::~LogHandler() {\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of Logger\n * ********************************************************************************************* */\nLogger *Logger::_instance = nullptr;\n\nLogger::Logger()\n  : QObject(nullptr), _handler(), _lock()\n{\n  // pass...\n}\n\nLogger::~Logger() {\n  _handler.clear();\n}\n\nvoid\nLogger::log(const LogMessage &msg) {\n  _lock.lock();\n  foreach (LogHandler *handler, _handler) {\n    handler->handle(msg);\n  }\n  _lock.unlock();\n}\n\nvoid\nLogger::addHandler(LogHandler *handler) {\n  if (nullptr == handler)\n    return;\n  if (_handler.contains(handler))\n    return;\n  handler->setParent(this);\n  _handler.append(handler);\n  connect(handler, SIGNAL(destroyed(QObject*)), this, SLOT(onHandlerDeleted(QObject*)));\n}\n\nvoid\nLogger::remHandler(LogHandler *handler) {\n  if (_handler.contains(handler)) {\n    handler->setParent(nullptr);\n    disconnect(handler, SIGNAL(destroyed(QObject*)), this, SLOT(onHandlerDeleted(QObject*)));\n  }\n  _handler.removeAll(handler);\n}\n\nvoid\nLogger::onHandlerDeleted(QObject *obj) {\n  _handler.removeAll(dynamic_cast<LogHandler*>(obj));\n}\n\nLogger &\nLogger::get() {\n  if (nullptr == _instance) {\n    _instance = new Logger();\n  }\n  return *_instance;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of StreamLogHandler\n * ********************************************************************************************* */\nStreamLogHandler::StreamLogHandler(QTextStream &stream, LogMessage::Level minLevel, bool color, QObject *parent)\n  : LogHandler(parent), _stream(stream), _minLevel(minLevel), _color(color)\n{\n  // pass...\n}\n\nLogMessage::Level\nStreamLogHandler::minLevel() const {\n  return _minLevel;\n}\n\nvoid\nStreamLogHandler::setMinLevel(LogMessage::Level minLevel) {\n  _minLevel = minLevel;\n}\n\nvoid\nStreamLogHandler::handle(const LogMessage &message) {\n  if (message.level() < _minLevel)\n    return;\n  switch (message.level()) {\n  case LogMessage::DEBUG:\n    if (_color)\n      _stream << \"\\033[37m\";\n    _stream << \"Debug \";\n    break;\n  case LogMessage::INFO:\n    if (_color)\n      _stream << \"\\033[39m\";\n    _stream << \"Info \";\n    break;\n  case LogMessage::WARNING:\n    if (_color)\n      _stream << \"\\033[33m\";\n    _stream << \"Warning \";\n    break;\n  case LogMessage::ERROR:\n    if (_color)\n      _stream << \"\\033[31m\";\n    _stream << \"ERROR \";\n    break;\n  case LogMessage::FATAL:\n    if (_color)\n      _stream << \"\\033[30m\\033[101m\";\n    _stream << \"FATAL \";\n    break;\n  }\n  QFileInfo finfo(message.file());\n  _stream << \"in \" << finfo.dir().dirName() << \"/\" << finfo.fileName()\n          << \"@\" << message.line() << \": \" << message.message() << \"\\n\";\n  if (_color)\n    _stream << \"\\033[39m\";\n  _stream.flush();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of FileLogHandler\n * ********************************************************************************************* */\nFileLogHandler::FileLogHandler(const QString &filename, LogMessage::Level minLevel, QObject *parent)\n  : LogHandler(parent), _file(filename), _stream(), _minLevel(minLevel)\n{\n  QFileInfo info(filename);\n  // Check if logfile exists\n  if (! info.exists()) {\n    // check and create path to logfile (if needed)\n    if (! info.absoluteDir().mkpath(info.absoluteDir().absolutePath())) {\n      logError() << \"Cannot create log-file directory '\" << info.absoluteDir().absolutePath() << \"'.\";\n      return;\n    }\n    // Create logfile\n    _file.open(QFile::WriteOnly);\n  } else {\n    // Open logfile\n    _file.open(QFile::Append);\n  }\n\n  if (_file.isOpen()) {\n    // Set stream desitnation to logfile\n    _stream.setDevice(&_file);\n  }\n}\n\nFileLogHandler::~FileLogHandler() {\n  if (_file.isOpen()) {\n    _file.flush();\n    _file.close();\n  }\n}\n\nLogMessage::Level\nFileLogHandler::minLevel() const {\n  return _minLevel;\n}\n\nvoid\nFileLogHandler::setMinLevel(LogMessage::Level minLevel) {\n  _minLevel = minLevel;\n}\n\nvoid\nFileLogHandler::handle(const LogMessage &message) {\n  if (!_file.isOpen())\n    return;\n\n  if (message.level() < _minLevel)\n    return;\n\n  _stream << QDateTime::currentDateTime().toString(Qt::ISODateWithMs) << \": \";\n  switch (message.level()) {\n  case LogMessage::DEBUG:   _stream << \"Debug \"; break;\n  case LogMessage::INFO:    _stream << \"Info \"; break;\n  case LogMessage::WARNING: _stream << \"Warning \"; break;\n  case LogMessage::ERROR:   _stream << \"ERROR \"; break;\n  case LogMessage::FATAL:   _stream << \"FATAL \"; break;\n  }\n  QFileInfo finfo(message.file());\n  _stream << \"in \" << finfo.dir().dirName() << \"/\" << finfo.fileName()\n          << \"@\" << message.line() << \": \" << message.message() << \"\\n\";\n  _stream.flush();\n}\n"
  },
  {
    "path": "lib/logger.hh",
    "content": "/** @defgroup log Log Message Handling\n * @ingroup util */\n#ifndef LOGGER_HH\n#define LOGGER_HH\n\n#include <QFile>\n#include <QTextStream>\n#include <QList>\n#include <QMutex>\n\n/** Constructs a debug message. */\n#define logDebug() LogMessage(LogMessage::DEBUG, __FILE__, __LINE__)\n/** Constructs an info message. */\n#define logInfo()  LogMessage(LogMessage::INFO, __FILE__, __LINE__)\n/** Constructs a warning message. */\n#define logWarn()  LogMessage(LogMessage::WARNING, __FILE__, __LINE__)\n/** Constructs an error message. */\n#define logError() LogMessage(LogMessage::ERROR, __FILE__, __LINE__)\n/** Constructs a fatal error message. */\n#ifdef __cpp_lib_stacktrace\n#include <stacktrace>\n#define logFatal() LogMessage(LogMessage::FATAL, __FILE__, __LINE__) << \\\n  QString::fromStdString(std::to_string(std::stacktrace::current()))\n#else\n#define logFatal() LogMessage(LogMessage::FATAL, __FILE__, __LINE__)\n#endif\n\n/** Implements a log-message.\n * Instances of this class will forward the content of this message automatically to the @c Logger\n * instance upon destruction. That means, you do not need to forward log messages explicitly.\n * @ingroup log */\nclass LogMessage: public QTextStream\n{\npublic:\n  /** Possible log-levels. */\n  typedef enum {\n    DEBUG,    ///< Level for debug messages. Will not be shown to the user unless requested.\n    INFO,     ///< Level for informative messages. Will not be shown to the user unless requested.\n    WARNING,  ///< Level for warning messages.\n    ERROR,    ///< Level for error messages.\n    FATAL     ///< Level for fatal error messages.\n  } Level;\n\npublic:\n  /** Constructor.\n   * @param level Specifies the level of the log message.\n   * @param file Specifies the source file.\n   * @param line Specifies the source line.\n   * @param message Specifies the log message content. */\n  LogMessage(Level level, const QString &file, int line, const QString &message=\"\");\n  /** Copy constructor. */\n  LogMessage(const LogMessage &other);\n  /** Destructor. */\n  virtual ~LogMessage();\n\n  /** Returns the level of the log message. */\n  Level level() const;\n  /** Returns the source file. */\n  const QString &file() const;\n  /** Returns the source line. */\n  int line() const;\n  /** Returns the log message content. */\n  const QString &message() const;\n\nprotected:\n  /** The log level. */\n  Level _level;\n  /** The source file. */\n  QString _file;\n  /** The source line. */\n  int _line;\n  /** The log message content. */\n  QString _message;\n};\n\n\n/** Interface for all log message handler.\n * @ingroup log */\nclass LogHandler: public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit LogHandler(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~LogHandler();\n  /** Callback to handle log messages. */\n  virtual void handle(const LogMessage &message) = 0;\n};\n\n\n/** Singleton class to process log messages.\n * @ingroup log */\nclass Logger: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. Use @c get method to obtain an instance. */\n  Logger();\n\npublic:\n  /** Destructor. */\n  virtual ~Logger();\n\n  /** Logs a message. */\n  void log(const LogMessage &msg);\n  /** Adds a log-handler to the logger. The ownership is transferred to the logger. */\n  void addHandler(LogHandler *handler);\n  /** Removes a log-handler from the logger. The ownership is transferred back to the caller. */\n  void remHandler(LogHandler *handler);\n\nprotected slots:\n  /** Internal callback to handle deleted handler objects. */\n  void onHandlerDeleted(QObject *obj);\n\npublic:\n  /** Factory method to get the singleton instance. */\n  static Logger &get();\n\nprotected:\n  /** The singleton instance. */\n  static Logger *_instance;\n  /** The list of registered log-handler. */\n  QList<LogHandler *> _handler;\n  /** Some mutex to prevent issues with log messages from different threads. */\n  QMutex _lock;\n};\n\n\n/** A log-handler that dumps log-messages into a @c QTextStream.\n * @ingroup log */\nclass StreamLogHandler: public LogHandler\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param stream Specifies the text stream to log into.\n   * @param minLevel Specifies the minimum log-level to log.\n   * @param color If @c true, the output will be colored.\n   * @param parent Specifies the parent object. */\n  StreamLogHandler(QTextStream &stream, LogMessage::Level minLevel=LogMessage::DEBUG, bool color=false, QObject *parent=nullptr);\n\n  /** Returns the minimum log level. */\n  LogMessage::Level minLevel() const;\n  /** Resets the minimum log level. */\n  void setMinLevel(LogMessage::Level minLevel);\n\n  void handle(const LogMessage &message);\n\nprotected:\n  /** A reference to the text stream to log into. */\n  QTextStream &_stream;\n  /** The minimum log level. */\n  LogMessage::Level _minLevel;\n  /** If true, write messages using console colors. */\n  bool _color;\n};\n\n\n/** A log-handler that dumps log-messages into files.\n * @ingroup log */\nclass FileLogHandler: public LogHandler\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param file Specifies the filename to log to.\n   * @param minLevel Specifies the minimum log-level to log.\n   * @param parent Specifies the parent object. */\n  FileLogHandler(const QString &file, LogMessage::Level minLevel=LogMessage::DEBUG, QObject *parent=nullptr);\n\n  /** Destructor, closes log file. */\n  virtual ~FileLogHandler();\n\n  /** Returns the minimum log level. */\n  LogMessage::Level minLevel() const;\n  /** Resets the minimum log level. */\n  void setMinLevel(LogMessage::Level minLevel);\n\n  void handle(const LogMessage &message);\n\nprotected:\n  /** The file to log into. */\n  QFile _file;\n  /** A reference to the text stream to log into. */\n  QTextStream _stream;\n  /** The minimum log level. */\n  LogMessage::Level _minLevel;\n};\n\n#endif // LOGGER_HH\n"
  },
  {
    "path": "lib/md2017.cc",
    "content": "#include \"md2017.hh\"\n#include \"md2017_limits.hh\"\n\n\nRadioLimits *MD2017::_limits = nullptr;\n\nMD2017::MD2017(TyTInterface *device, QObject *parent)\n  : TyTRadio(device, parent), _name(\"TyT DM-2017\")\n{\n  //_codeplug(nullptr), _callsigndb(nullptr);\n}\n\nMD2017::~MD2017() {\n  // pass...\n}\n\nconst QString &\nMD2017::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nMD2017::limits() const {\n  if (nullptr == _limits)\n    _limits = new MD2017Limits();\n  return *_limits;\n}\n\nconst Codeplug &\nMD2017::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nMD2017::codeplug() {\n  return _codeplug;\n}\n\nconst CallsignDB *\nMD2017::callsignDB() const {\n  return &_callsigndb;\n}\n\nCallsignDB *\nMD2017::callsignDB() {\n  return &_callsigndb;\n}\n\nRadioInfo\nMD2017::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::MD2017, \"md2017\", \"MD-2017\", \"TyT\", {TyTInterface::interfaceInfo()},\n        QList<RadioInfo>{\n          RadioInfo(RadioInfo::RT82, \"RT82\", \"Retevis\", {TyTInterface::interfaceInfo()})\n        });\n}\n"
  },
  {
    "path": "lib/md2017.hh",
    "content": "/** @defgroup md2017 TYT MD-2017, Retevis RT82\n * Device specific classes for TYT MD-2017 and Retevis RT82.\n *\n * \\image html md2017.jpg \"MD-2017\" width=200px\n * \\image latex md2017.jpg \"MD-2017\" width=200px\n *\n * <img src=\"sm2017.jpg\" width=\"200px\" align=\"left\"/>\n * The TYT MD-2017 and the identical Retevis RT-82 are decent VHF/UHF FM and DMR handheld radios.\n * Both radios are available with and without an GPS option. @c libdmrconf will support that\n * feature. Non-GPS variants of that radio will simply ignore any GPS system settings.\n *\n * These radios support up to 3000 channels organized in 250 zones. Each zone may hold up to 64\n * channels for each VFO (64 for VFO A and 64 for VFO B). There are also up to 250 scanlists\n * holding up to 31(?) channels each.\n *\n * The radio can hold up to 3000 contacts (DMR contacts) and 250 RX group lists as well as up to 50\n * pre-programmed messages. Depending on the firmware programmed on the radio, it may also hold a\n * callsign database of up to 100000 entries. This can be used to resolve amlost any DMR ID assigned\n * (at the time of this writing, there are about 140k IDs assigned) to name and callsign.\n *\n * @ingroup tyt */\n#ifndef MD2017_HH\n#define MD2017_HH\n\n#include \"tyt_radio.hh\"\n#include \"md2017_codeplug.hh\"\n#include \"md2017_callsigndb.hh\"\n\n/** Implements an USB interface to the TYT MD-2017 & Retevis RT82 VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * The TYT MD-2017 and Retevis RT82 radios use the TyT typical DFU-style communication protocol\n * to read and write codeplugs onto the radio (see @c TyTRadio).\n *\n * @ingroup md2017 */\nclass MD2017 : public TyTRadio\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param device Specifies the DFU device to use for communication with the device.\n   * @param parent The QObject parent. */\n  MD2017(TyTInterface *device=nullptr, QObject *parent=nullptr);\n  /** Desturctor. */\n  virtual ~MD2017();\n\n  const QString &name() const;\n  const RadioLimits &limits() const;\n\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** Holds the name of the device. */\n  QString _name;\n  /** The codeplug object. */\n  MD2017Codeplug _codeplug;\n  /** The callsign DB object. */\n  MD2017CallsignDB _callsigndb;\n\nprivate:\n  /** Singleton instance of the limits for this radio. */\n  static RadioLimits *_limits;\n};\n\n#endif // MD2017_HH\n"
  },
  {
    "path": "lib/md2017_callsigndb.cc",
    "content": "#include \"md2017_callsigndb.hh\"\n\nMD2017CallsignDB::MD2017CallsignDB(QObject *parent)\n  : TyTCallsignDB(parent)\n{\n  image(0).setName(\"TYT MD-2017 Callsign database.\");\n}\n\nMD2017CallsignDB::~MD2017CallsignDB() {\n  // pass...\n}\n\n\n"
  },
  {
    "path": "lib/md2017_callsigndb.hh",
    "content": "#ifndef MD2017_CALLSIGNDB_HH\n#define MD2017_CALLSIGNDB_HH\n\n#include \"tyt_callsigndb.hh\"\n\n/** Device specific implementation of the call-sign DB for the TyT MD-2017.\n *\n * In fact this callsign DB is identical to the generic @c TyTCallsignDB.\n *\n * @ingroup md2017 */\nclass MD2017CallsignDB : public TyTCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit MD2017CallsignDB(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~MD2017CallsignDB();\n};\n\n#endif // MD2017_CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/md2017_codeplug.cc",
    "content": "#include \"md2017_codeplug.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n\n#define NUM_CHANNELS                3000\n#define ADDR_CHANNELS           0x110000\n#define CHANNEL_SIZE            0x000040\n\n#define NUM_CONTACTS               10000\n#define ADDR_CONTACTS           0x140000\n#define CONTACT_SIZE            0x000024\n\n#define NUM_ZONES                    250\n#define ADDR_ZONES              0x0149e0\n#define ZONE_SIZE               0x000040\n#define ADDR_ZONEEXTS           0x031000\n#define ZONEEXT_SIZE            0x0000e0\n\n#define NUM_GROUPLISTS               250\n#define ADDR_GROUPLISTS         0x00ec20\n#define GROUPLIST_SIZE          0x000060\n\n#define NUM_SCANLISTS                250\n#define ADDR_SCANLISTS          0x018860\n#define SCANLIST_SIZE           0x000068\n\n#define ADDR_TIMESTAMP          0x002000\n#define ADDR_SETTINGS           0x002040\n#define ADDR_BOOTSETTINGS       0x02f000\n#define ADDR_MENUSETTINGS       0x0020f0\n#define ADDR_BUTTONSETTINGS     0x002100\n#define ADDR_PRIVACY_KEYS       0x0059c0\n\n#define NUM_GPSSYSTEMS                16\n#define ADDR_GPSSYSTEMS         0x03ec40\n#define GPSSYSTEM_SIZE          0x000010\n\n#define ADDR_EMERGENCY_SETTINGS 0x005a50\n#define NUM_EMERGENCY_SYSTEMS         32\n#define ADDR_EMERGENCY_SYSTEMS  0x005a60\n#define EMERGENCY_SYSTEM_SIZE   0x000028\n\n#define ADDR_VFO_CHANNEL_A      0x02ef00\n#define ADDR_VFO_CHANNEL_B      0x02ef40\n\n/* ********************************************************************************************* *\n * MD2017Codeplug::ContactElement\n * ********************************************************************************************* */\nMD2017Codeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::ContactElement(ptr, size)\n{\n  // pass...\n}\n\nMD2017Codeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : TyTCodeplug::ContactElement(ptr)\n{\n  // pass...\n}\n\nbool\nMD2017Codeplug::ContactElement::isValid() const {\n  return Codeplug::Element::isValid() && (! name().isEmpty());\n}\n\n\nMD2017Codeplug::MD2017Codeplug(QObject *parent)\n  : TyTCodeplug(parent)\n{\n  addImage(\"TYT MD-2017 Codeplug\");\n  image(0).addElement(0x002000, 0x3e000);\n  image(0).addElement(0x110000, 0x90000);\n\n  // Clear entire codeplug\n  clear();\n}\n\nMD2017Codeplug::~MD2017Codeplug() {\n  // pass...\n}\n\nvoid\nMD2017Codeplug::clearTimestamp() {\n  TimestampElement(data(ADDR_TIMESTAMP)).clear();\n}\n\nbool\nMD2017Codeplug::encodeTimestamp() {\n  TimestampElement ts(data(ADDR_TIMESTAMP));\n  ts.setTimestamp(QDateTime::currentDateTime());\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(ADDR_SETTINGS)).clear();\n}\n\nbool\nMD2017Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nMD2017Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(ctx.config());\n}\n\nvoid\nMD2017Codeplug::clearChannels() {\n  // Clear channels\n  for (int i=0; i<NUM_CHANNELS; i++)\n    ChannelElement(data(ADDR_CHANNELS+i*CHANNEL_SIZE)).clear();\n}\n\nbool\nMD2017Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Channels\n  for (unsigned int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (i < ctx.count<Channel>()) {\n      chan.fromChannelObj(ctx.get<Channel>(i+1), ctx);\n    } else {\n      chan.clear();\n    }\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (! chan.isValid())\n      continue;\n    ErrorStack err;\n    if (Channel *obj = chan.toChannelObj(err)) {\n      ctx.config()->channelList()->add(obj);\n      ctx.add(obj, i+1);\n    } else {\n      logWarn() << \"Cannot decode channel at index \" << i << \":\\n\"\n                << err.format(\" \");\n      continue;\n    }\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n\n    if ((! chan.isValid()) || (! ctx.has<Channel>(i+1)))\n      continue;\n\n    ErrorStack err;\n    if (! chan.linkChannelObj(ctx.get<Channel>(i+1), ctx, err)) {\n      logWarn() << \"Cannot link channel at index \" << i << \":\\n\"\n                << err.format(\" \");\n      continue;\n    }\n  }\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearContacts() {\n  // Clear contacts\n  for (int i=0; i<NUM_CONTACTS; i++)\n    ContactElement(data(ADDR_CONTACTS+i*CONTACT_SIZE)).clear();\n}\n\nbool\nMD2017Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Encode contacts\n  for (unsigned int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (i < ctx.count<DigitalContact>())\n      cont.fromContactObj(ctx.get<DMRContact>(i+1));\n    else\n      cont.clear();\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (! cont.isValid())\n      continue;\n    if (DMRContact *obj = cont.toContactObj()) {\n      ctx.config()->contacts()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid contact at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearZones() {\n  // Clear zones & zone extensions\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement(data(ADDR_ZONES+i*ZONE_SIZE)).clear();\n    ZoneExtElement(data(ADDR_ZONEEXTS+i*ZONEEXT_SIZE)).clear();\n  }\n}\n\nbool\nMD2017Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES + i*ZONE_SIZE));\n    ZoneExtElement ext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    zone.clear();\n    ext.clear();\n    if (i < ctx.config()->zones()->count()) {\n      zone.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n      if (ctx.config()->zones()->zone(i)->B()->count() || (16 < ctx.config()->zones()->zone(i)->A()->count()))\n        ext.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n    }\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (Zone *obj = zone.toZoneObj()) {\n      ctx.config()->zones()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nMD2017Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (! zone.linkZone(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n    ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    if (! zoneext.linkZoneObj(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearGroupLists() {\n  for (int i=0; i<NUM_GROUPLISTS; i++)\n    GroupListElement(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE)).clear();\n}\n\nbool\nMD2017Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (i < ctx.config()->rxGroupLists()->count())\n      glist.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx);\n    else\n      glist.clear();\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (RXGroupList *obj = glist.toGroupListObj(ctx)) {\n      ctx.config()->rxGroupLists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (! glist.linkGroupListObj(ctx.get<RXGroupList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearScanLists() {\n  // Clear scan lists\n  for (int i=0; i<NUM_SCANLISTS; i++)\n    ScanListElement(data(ADDR_SCANLISTS + i*SCANLIST_SIZE)).clear();\n}\n\nbool\nMD2017Codeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Scanlists\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (i < ctx.config()->scanlists()->count())\n      scan.fromScanListObj(ctx.config()->scanlists()->scanlist(i), ctx);\n    else\n      scan.clear();\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (ScanList *obj = scan.toScanListObj(ctx)) {\n      ctx.config()->scanlists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid scanlist at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (! scan.linkScanListObj(ctx.get<ScanList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link scan list at index\" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearPositioningSystems() {\n  // Clear GPS systems\n  for (int i=0; i<NUM_GPSSYSTEMS; i++)\n    GPSSystemElement(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE)).clear();\n}\n\nbool\nMD2017Codeplug::encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (i < ctx.count<DMRAPRSSystem>())\n      gps.fromGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx);\n    else\n      gps.clear();\n  }\n  return true;\n}\n\nbool\nMD2017Codeplug::createPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (DMRAPRSSystem *obj = gps.toGPSSystemObj()) {\n      ctx.config()->posSystems()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nMD2017Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (! gps.linkGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD2017Codeplug::clearBootSettings() {\n  BootSettingsElement(data(ADDR_BOOTSETTINGS)).clear();\n}\n\nvoid\nMD2017Codeplug::clearMenuSettings() {\n  MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear();\n}\n\nvoid\nMD2017Codeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear();\n}\n\nbool\nMD2017Codeplug::encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode settings\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nMD2017Codeplug::decodeButtonSetttings( Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(ctx.config());\n}\n\n\nvoid\nMD2017Codeplug::clearPrivacyKeys() {\n  EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear();\n}\n\nbool\nMD2017Codeplug::encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n  // First, reset keys\n  clearPrivacyKeys();\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // If there is no encryption extension -> done.\n  if (! ctx.config()->commercialExtension())\n    return true;\n  // Otherwise encode\n  return keys.fromCommercialExt(ctx.config()->commercialExtension(), ctx);\n}\n\nbool\nMD2017Codeplug::decodePrivacyKeys(Context &ctx, const ErrorStack &err) {\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // Decode element\n  if(! keys.updateCommercialExt(ctx)) {\n    errMsg(err) << \"Cannot create encryption keys.\";\n    return false;\n  }\n  return true;\n}\n\n\nvoid\nMD2017Codeplug::clearTextMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\n\nbool\nMD2017Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err);\n}\n\nbool\nMD2017Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).decode(ctx, err);\n}\n\n\nvoid\nMD2017Codeplug::clearEmergencySystems() {\n  EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear();\n  for (int i=0; i<NUM_EMERGENCY_SYSTEMS; i++)\n    EmergencySystemElement(data(ADDR_EMERGENCY_SYSTEMS + i*EMERGENCY_SYSTEM_SIZE)).clear();\n}\n\nvoid\nMD2017Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_A)).clear();\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_B)).clear();\n}\n"
  },
  {
    "path": "lib/md2017_codeplug.hh",
    "content": "#ifndef MD2017_CODEPLUG_HH\n#define MD2017_CODEPLUG_HH\n\n#include \"tyt_codeplug.hh\"\n#include \"uv390_codeplug.hh\"\n\n/** Device specific implementation of the codeplug for the TyT MD-2017.\n *\n * The codeplug consists of two segments. The first segment starts at address @c 0x002000 and ends at\n * address @c 0x040000. The second segment starts at address @c 0x110000 and ends at @c 0x1a0000. The\n * segments must align with @c 0x400 (1024 bytes).\n *\n * @section md2017cpl Codeplug structure within radio\n * The codeplug structure is reverse engineered almost completely and can be programmed from\n * scratch. That is, it is not necessary to update an existing codeplug on the radio.\n * <table>\n *  <tr><th>Start</th>    <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x002000-0x040000</th></tr>\n *  <tr><td>0x002000</td> <td>0x00200c</td> <td>0x0000c</td> <td>Timestamp see @c TyTCodeplug::TimestampElement.</td></tr>\n *  <tr><td>0x00200c</td> <td>0x002040</td> <td>0x00034</td> <td>Reserved, filled with 0xff. </td></tr>\n *  <tr><td>0x002040</td> <td>0x0020f0</td> <td>0x000b0</td> <td>General settings see @c TyTCodeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x0020f0</td> <td>0x002100</td> <td>0x00010</td> <td>Menu settings, see @c UV390Codeplug::menu_t</td></tr>\n *  <tr><td>0x002100</td> <td>0x002140</td> <td>0x00040</td> <td>Button config, see @c UV390Codeplug::buttons_t.</td></tr>\n *  <tr><td>0x002140</td> <td>0x002180</td> <td>0x00040</td> <td>Reserved, filled with 0xff.</td></tr>\n *  <tr><td>0x002180</td> <td>0x0059c0</td> <td>0x03840</td> <td>50 Text messages @ 0x120 bytes each, see @c UV390Codeplug::message_t.</td></tr>\n *  <tr><td>0x0059c0</td> <td>0x005a70</td> <td>0x000b0</td> <td>Privacy keys, see @c UV390Codeplug::privacy_t.</td></tr>\n *  <tr><td>0x005a70</td> <td>0x005a80</td> <td>0x00010</td> <td>Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement.</td></td>\n *  <tr><td>0x005a80</td> <td>0x005f80</td> <td>0x00500</td> <td>Emergency systems, see @c TyTCodeplug::EmergencySystemElement.</td></td>\n *  <tr><td>0x005f80</td> <td>0x00ec20</td> <td>0x08ca0</td> <td>Reserved, filled with 0xff.</td></td>\n *  <tr><td>0x00ec20</td> <td>0x0149e0</td> <td>0x05dc0</td> <td>250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement.</td></tr>\n *  <tr><td>0x0149e0</td> <td>0x018860</td> <td>0x03e80</td> <td>250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement.</td></tr>\n *  <tr><td>0x018860</td> <td>0x01edf0</td> <td>0x06590</td> <td>250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement.</td></tr>\n *  <tr><td>0x01edf0</td> <td>0x02ef00</td> <td>0x10110</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x02ef00</td> <td>0x02ef40</td> <td>0x00040</td> <td>VFO A channel, see @c TyTCodeplug::VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef40</td> <td>0x02ef80</td> <td>0x00040</td> <td>VFO B channel, see @c TyTCodeplug::VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef80</td> <td>0x02f000</td> <td>0x00080</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x02f000</td> <td>0x02f010</td> <td>0x00010</td> <td>Some unknown settings like current channel, see @c TyTCodeplug::BootSettingsElement.</td></tr>\n *  <tr><td>0x02f010</td> <td>0x031000</td> <td>0x01ff0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x031000</td> <td>0x03eac0</td> <td>0x0dac0</td> <td>250 Zone-extensions @ 0xe0 bytes each, see @c TyTCodeplug::ZoneExtElement.</td></tr>\n *  <tr><td>0x03eac0</td> <td>0x03ec40</td> <td>0x00180</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x03ec40</td> <td>0x03ed40</td> <td>0x00100</td> <td>16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement.</td></tr>\n *  <tr><td>0x03ed40</td> <td>0x040000</td> <td>0x012c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><th colspan=\"4\">Second segment 0x110000-0x1a0000</th></tr>\n *  <tr><td>0x110000</td> <td>0x13ee00</td> <td>0x2ee00</td> <td>3000 Channels @ 0x40 bytes each, see @c TyTCodeplug::ChannelElement.</td></tr>\n *  <tr><td>0x13ee00</td> <td>0x140000</td> <td>0x01200</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x140000</td> <td>0x197e40</td> <td>0x57e40</td> <td>10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement.</td></tr>\n *  <tr><td>0x197e40</td> <td>0x1a0000</td> <td>0x081c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n * </table>\n *\n * @ingroup md2017 */\nclass MD2017Codeplug : public TyTCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Reuse TyT MD-UV390 channel element. */\n  typedef UV390Codeplug::ChannelElement ChannelElement;\n\n  /** Reuse TyT MD-UV390 VFO channel element. */\n  typedef UV390Codeplug::VFOChannelElement VFOChannelElement;\n\n  /** Reuse TyT MD-UV390 zone extension element. */\n  typedef UV390Codeplug::ZoneExtElement ZoneExtElement;\n\n  /** Reuse TyT MD-UV390 boot settings element. */\n  typedef UV390Codeplug::BootSettingsElement BootSettingsElement;\n\n  /** Reuse TyT MD-UV390 menu settings element. */\n  typedef UV390Codeplug::MenuSettingsElement MenuSettingsElement;\n\n  /** Contact element for MD2017 codeplugs.\n   *\n   * This class implements the same memory layout as the base TyTCodeplug::ContactElement. It just\n   * overrides the @c isValid method.\n   *\n   * Memory layout of encoded contact:\n   * @verbinclude tyt_contact.txt */\n  class ContactElement: public TyTCodeplug::ContactElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n\n    bool isValid() const;\n  };\n\npublic:\n  /** Constructor. */\n  explicit MD2017Codeplug(QObject *parent = nullptr);\n  /** Destructor. */\n  virtual ~MD2017Codeplug();\n\npublic:\n  void clearTimestamp();\n  bool encodeTimestamp();\n\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPositioningSystems();\n  bool encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSetttings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPrivacyKeys();\n  bool encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool decodePrivacyKeys(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearTextMessages();\n  bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err);\n  bool decodeTextMessages(Context &ctx, const ErrorStack &err);\n\n  /** Resets/clears the boot settings. */\n  virtual void clearBootSettings();\n  void clearMenuSettings();\n  void clearEmergencySystems();\n  /** Resets VFO settings. */\n  virtual void clearVFOSettings();\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int messages() { return 0x002180; }\n    /// @endcond\n  };\n};\n\n#endif // MD2017_CODEPLUG_HH\n"
  },
  {
    "path": "lib/md2017_filereader.cc",
    "content": "#include \"md2017_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_FILE_ADDR   0x00002225\n#define SEGMENT0_TARGET_ADDR 0x00002000\n#define SEGMENT0_SIZE        0x0003e000\n#define SEGMENT1_FILE_ADDR   0x00040235\n#define SEGMENT1_TARGET_ADDR 0x00110000\n#define SEGMENT1_SIZE        0x00090000\n\nbool\nMD2017FileReader::read(const QString &filename, MD2017Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (852533 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 852533 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  if (! file.seek(SEGMENT1_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR);\n  n = SEGMENT1_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/md2017_filereader.hh",
    "content": "#ifndef MD2017_FILEREADER_HH\n#define MD2017_FILEREADER_HH\n\n#include \"md2017_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is still pretty simple. The first part of the file consists of\n * a mal-formed DFU file. This contains a single image with a single element containing the\n * first section of the memory written to the device. The second section is then added as-is\n * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ.\n *\n * <table>\n *  <tr><th>File Start</th> <th>Memory Start</th> <th>Size</th></tr>\n *  <tr><td>0x002225</td>   <td>0x002000</td>     <td>0x03e000</td></tr>\n *  <tr><td>0x040235</td>   <td>0x110000</td>     <td>0x090000</td></tr>\n * </table>\n *\n * @ingroup md2017 */\nclass MD2017FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err Error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, MD2017Codeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // MD2017_FILEREADER_HH\n"
  },
  {
    "path": "lib/md2017_limits.cc",
    "content": "#include \"md2017_limits.hh\"\n#include \"md2017_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nMD2017Limits::MD2017Limits(QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 122197;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"introLine2\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::Mid),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(MD2017Codeplug::GeneralSettingsElement::Limit::bootPasswordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, 10000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::Mid),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name\n          { \"A\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems. */\n  add(\"positioning\", new RadioLimitList({\n        { DMRAPRSSystem::staticMetaObject, 0, 16, new RadioLimitObject {\n          { \"name\", new RadioLimitStringIgnored() },\n          { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n          { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n          { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) );\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList {\n           {BasicEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::basicKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{4}\")}\n            }},\n           {AESEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{32}\")}\n            }} }\n        }\n      });\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/md2017_limits.hh",
    "content": "#ifndef MD2017LIMITS_HH\n#define MD2017LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the limits for the TyT MD-2017.\n * @ingroup md2017 */\nclass MD2017Limits: public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit MD2017Limits(QObject *parent=nullptr);\n};\n\n#endif // MD2017LIMITS_HH\n"
  },
  {
    "path": "lib/md390.cc",
    "content": "#include \"md390.hh\"\n#include \"md390_limits.hh\"\n\n#include \"logger.hh\"\n#include \"utils.hh\"\n\n#define NUM_CHANNELS                1000\n#define ADDR_CHANNELS           0x01ee00\n#define CHANNEL_SIZE            0x000040\n#define BLOCK_SIZE                  1024\n\n\nMD390::MD390(TyTInterface *device, const ErrorStack &err, QObject *parent)\n  : TyTRadio(device, parent), _name(\"TyT MD-390\"), _limits(nullptr)\n{\n  // Read channels and identify device variance based on the channel frequencies. This may block\n  // the GUI for some time.\n  unsigned addr_start = align_addr(ADDR_CHANNELS, BLOCK_SIZE);\n  unsigned channel_offset = ADDR_CHANNELS-addr_start;\n  uint total_size = align_size(addr_start+NUM_CHANNELS*CHANNEL_SIZE, BLOCK_SIZE)-addr_start;\n  QByteArray buffer(total_size, 0xff);\n\n  if (! _dev->read_start(0, addr_start, err)) {\n    errMsg(err) << \"Cannot read channels from MD-390, cannot determine variant.\";\n    return;\n  }\n  for (unsigned i=0; i<(total_size/BLOCK_SIZE); i++){\n    if (! _dev->read(0, ADDR_CHANNELS, (uint8_t *)buffer.data()+i*BLOCK_SIZE, BLOCK_SIZE, err)) {\n      errMsg(err) << \"Cannot read channels from MD-390, cannot determine variant.\";\n      return;\n    }\n  }\n  _dev->read_finish(err);\n\n  // Determine frequency range of channels\n  unsigned channelCount = 0;\n  QPair<double, double> range(std::numeric_limits<double>::max(),0);\n  for (unsigned i=0; i<NUM_CHANNELS; i++) {\n    MD390Codeplug::ChannelElement channel(\n          ((uint8_t *)buffer.data()) + channel_offset + i*CHANNEL_SIZE);\n    if (! channel.isValid())\n      continue;\n    if (channel.rxFrequency().inHz()) {\n      range.first = std::min(range.first, channel.rxFrequency().inMHz());\n      range.second = std::max(range.second, channel.rxFrequency().inMHz());\n    }\n    if (channel.txFrequency().inHz()) {\n      range.first = std::min(range.first, channel.txFrequency().inMHz());\n      range.second = std::max(range.second, channel.txFrequency().inMHz());\n    }\n    channelCount++;\n  }\n\n  logDebug() << \"Got frequency range \" << range.first << \"MHz - \" << range.second\n             << \"MHz from \" << channelCount << \" channels.\";\n\n  if ((137<=range.first) && (174>=range.second)) {\n    _limits = new MD390Limits({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)}}, this);\n    _name += \"V\";\n  } else if ((350<=range.first) && (400>=range.second)) {\n    _limits = new MD390Limits({{Frequency::fromMHz(350.), Frequency::fromMHz(400.)}}, this);\n    _name += \"U\";\n  } else if ((400<=range.first) && (450>=range.second)) {\n    _limits = new MD390Limits({{Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, this);\n    _name += \"U\";\n  } else if ((450<=range.first) && (520>=range.second)) {\n    _limits = new MD390Limits({{Frequency::fromMHz(450.), Frequency::fromMHz(520.)}}, this);\n    _name += \"U\";\n  } else {\n    // Invalid frequency range, needs \"Ignore frequency limits\" in settings to write any codeplug.\n    _limits = new MD390Limits({}, this);\n    errMsg(err) << \"Cannot determine frequency range from channel frequencies between \"\n                << range.first << \"MHz and \" << range.second\n                << \"MHz. Will not check frequency ranges.\";\n  }\n}\n\nconst QString &\nMD390::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nMD390::limits() const {\n  return *_limits;\n}\n\nconst Codeplug &\nMD390::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nMD390::codeplug() {\n  return _codeplug;\n}\n\nconst CallsignDB *\nMD390::callsignDB() const {\n  return nullptr;\n}\n\nCallsignDB *\nMD390::callsignDB() {\n  return nullptr;\n}\n\nRadioInfo\nMD390::defaultRadioInfo() {\n  return RadioInfo(RadioInfo::MD390, \"md390\", \"MD-390\", \"TyT\", {TyTInterface::interfaceInfo()},\n                   QList<RadioInfo>{\n                     RadioInfo(RadioInfo::RT8, \"rt8\", \"RT8\", \"Retevis\", {TyTInterface::interfaceInfo()})\n                   });\n}\n"
  },
  {
    "path": "lib/md390.hh",
    "content": "/** @defgroup md390 TYT MD-390 (U/V)\n * Device specific classes for TyT MD-390, both the VHF and UHF version.\n *\n * \\image html md390.jpg \"MD-390\" width=200px\n * \\image latex md390.jpg \"MD-390\" width=200px\n *\n * The TYT MD-390 is a decent VHF or UHF FM and DMR handheld radio.\n * The radio is available with and without an GPS option. @c libdmrconf will support that\n * feature. Non-GPS variants of that radio will simply ignore any GPS system settings.\n *\n * These radios support up to 1000 channels organized in 250 zones. Each zone may hold up to 16\n * channels. There are also up to 250 scanlists\n * holding up to 31(?) channels each.\n *\n * The radio can hold up to 1000 contacts (DMR contacts) and 250 RX group lists as well as up to 50\n * pre-programmed messages.\n *\n * @ingroup tyt */\n#ifndef MD390_HH\n#define MD390_HH\n\n#include \"tyt_radio.hh\"\n#include \"md390_codeplug.hh\"\n\n/** Implements an USB interface to the TYT MD-390 VHF/UHF 5W DMR (Tier I&II) radio.\n *\n * The TYT MD-390 radio use the TyT typical DFU-style communication protocol\n * to read and write codeplugs onto the radio (see @c TyTRadio).\n *\n * @ingroup md390 */\nclass MD390 : public TyTRadio\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param device Specifies the DFU device to use for communication with the device.\n   * @param err Passes an error stack to put error messages on.\n   * @param parent The QObject parent. */\n  MD390(TyTInterface *device=nullptr, const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\n  const QString &name() const;\n  const RadioLimits &limits() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** Holds the name of the device. */\n  QString _name;\n  /** The codeplug object. */\n  MD390Codeplug _codeplug;\n\nprivate:\n  /** Instance of limits for this radio. Gets instantiated for every radio as it depends on the\n   * radio variant. */\n  RadioLimits *_limits;\n};\n\n#endif // MD2017_HH\n"
  },
  {
    "path": "lib/md390_codeplug.cc",
    "content": "#include \"md390_codeplug.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"intermediaterepresentation.hh\"\n\n\n#define NUM_CHANNELS                1000\n#define ADDR_CHANNELS           0x01ee00\n#define CHANNEL_SIZE            0x000040\n\n#define NUM_CONTACTS                1000\n#define ADDR_CONTACTS           0x005f80\n#define CONTACT_SIZE            0x000024\n\n#define NUM_ZONES                    250\n#define ADDR_ZONES              0x0149e0\n#define ZONE_SIZE               0x000040\n\n#define NUM_GROUPLISTS               250\n#define ADDR_GROUPLISTS         0x00ec20\n#define GROUPLIST_SIZE          0x000060\n\n#define NUM_SCANLISTS                250\n#define ADDR_SCANLISTS          0x018860\n#define SCANLIST_SIZE           0x000068\n\n#define ADDR_TIMESTAMP          0x002000\n#define ADDR_SETTINGS           0x002040\n#define ADDR_BOOTSETTINGS       0x02f000\n#define ADDR_MENUSETTINGS       0x0020f0\n#define MENUSETTINGS_SIZE       0x000010\n#define ADDR_BUTTONSETTINGS     0x002100\n#define ADDR_PRIVACY_KEYS       0x0059c0\n\n#define NUM_GPSSYSTEMS                16\n#define ADDR_GPSSYSTEMS         0x03ec40\n#define GPSSYSTEM_SIZE          0x000010\n\n#define ADDR_EMERGENCY_SETTINGS 0x005a50\n#define NUM_EMERGENCY_SYSTEMS         32\n#define ADDR_EMERGENCY_SYSTEMS  0x005a60\n#define EMERGENCY_SYSTEM_SIZE   0x000028\n\n\n/* ********************************************************************************************* *\n * Implementation of MD390Codeplug::ChannelElement\n * ********************************************************************************************* */\nMD390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : DM1701Codeplug::ChannelElement::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nMD390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : DM1701Codeplug::ChannelElement(ptr, CHANNEL_SIZE)\n{\n  // pass...\n}\n\nvoid\nMD390Codeplug::ChannelElement::clear() {\n  DM1701Codeplug::ChannelElement::clear();\n\n  enableCompressedUDPHeader(false);\n}\n\nbool\nMD390Codeplug::ChannelElement::compressedUDPHeader() const {\n  return !getBit(0x0003, 6);\n}\nvoid\nMD390Codeplug::ChannelElement::enableCompressedUDPHeader(bool enable) {\n  setBit(0x0003, 6, !enable);\n}\n\nChannel *\nMD390Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const {\n  Channel *ch = DM1701Codeplug::ChannelElement::toChannelObj(err);\n  if (nullptr == ch)\n    return ch;\n\n  // Apply extension\n  if (ch->tytChannelExtension()) {\n    ch->tytChannelExtension()->enableCompressedUDPHeader(compressedUDPHeader());\n  }\n\n  return ch;\n}\n\nvoid\nMD390Codeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx) {\n  DM1701Codeplug::ChannelElement::fromChannelObj(c, ctx);\n\n  // apply extensions (extension will be created in TyTCodeplug::ChannelElement::fromChannelObj)\n  if (TyTChannelExtension *ex = c->tytChannelExtension()) {\n    enableCompressedUDPHeader(ex->compressedUDPHeader());\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of MD390Codeplug::MenuSettingsElement\n * ******************************************************************************************** */\nMD390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::MenuSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nMD390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr)\n  : TyTCodeplug::MenuSettingsElement(ptr, MENUSETTINGS_SIZE)\n{\n  // pass...\n}\n\nvoid\nMD390Codeplug::MenuSettingsElement::clear() {\n  TyTCodeplug::MenuSettingsElement::clear();\n\n  enableGPSInformation(true);\n}\n\nbool\nMD390Codeplug::MenuSettingsElement::gpsInformation() const {\n  return !getBit(0x04, 4);\n}\nvoid\nMD390Codeplug::MenuSettingsElement::enableGPSInformation(bool enable) {\n  setBit(0x04, 4, !enable);\n}\n\nbool\nMD390Codeplug::MenuSettingsElement::fromConfig(const Config *config) {\n  if (! TyTCodeplug::MenuSettingsElement::fromConfig(config))\n    return false;\n\n  if (TyTConfigExtension *ex = config->tytExtension()) {\n     enableGPSInformation(ex->menuSettings()->gpsInformation());\n  }\n\n  return true;\n}\n\nbool\nMD390Codeplug::MenuSettingsElement::updateConfig(Config *config) {\n  if (! TyTCodeplug::MenuSettingsElement::updateConfig(config))\n    return false;\n\n  if (TyTConfigExtension *ex = config->tytExtension()) {\n    ex->menuSettings()->enableGPSInformation(gpsInformation());\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of MD390Codeplug\n * ********************************************************************************************* */\nMD390Codeplug::MD390Codeplug(QObject *parent)\n  : TyTCodeplug(parent)\n{\n  addImage(\"TYT MD-390 Codeplug\");\n  image(0).addElement(0x002000, 0x3e000);\n  // Clear entire codeplug\n  clear();\n}\n\nConfig *\nMD390Codeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = TyTCodeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot prepare codeplug for MD-390.\";\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot split zones in codeplug for MD-390.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\nbool\nMD390Codeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! TyTCodeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process MD-390 codeplug.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot merge zones from decoded MD-390 codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nMD390Codeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  logDebug() << \"Decode MD390 codeplug, programmed with CPS version \"\n             << TimestampElement(data(ADDR_TIMESTAMP)).cpsVersion() << \".\";\n  return TyTCodeplug::decodeElements(ctx, err);\n}\n\nvoid\nMD390Codeplug::clearTimestamp() {\n  TimestampElement(data(ADDR_TIMESTAMP)).clear();\n}\n\nbool\nMD390Codeplug::encodeTimestamp() {\n  TimestampElement ts(data(ADDR_TIMESTAMP));\n  ts.setTimestamp(QDateTime::currentDateTime());\n  return true;\n}\n\nvoid\nMD390Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(ADDR_SETTINGS)).clear();\n}\n\nbool\nMD390Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nMD390Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(ctx.config());\n}\n\nvoid\nMD390Codeplug::clearChannels() {\n  // Clear channels\n  for (int i=0; i<NUM_CHANNELS; i++)\n    ChannelElement(data(ADDR_CHANNELS+i*CHANNEL_SIZE)).clear();\n}\n\nbool\nMD390Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Channels\n  for (unsigned int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (i < ctx.count<Channel>()) {\n      chan.fromChannelObj(ctx.get<Channel>(i+1), ctx);\n    } else {\n      chan.clear();\n    }\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (! chan.isValid())\n      continue;\n    if (Channel *obj = chan.toChannelObj()) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CHANNELS; i++) {\n    ChannelElement chan(data(ADDR_CHANNELS+i*CHANNEL_SIZE));\n    if (! chan.isValid())\n      continue;\n    if (! chan.linkChannelObj(ctx.get<Channel>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nMD390Codeplug::clearContacts() {\n  // Clear contacts\n  for (int i=0; i<NUM_CONTACTS; i++)\n    ContactElement(data(ADDR_CONTACTS+i*CONTACT_SIZE)).clear();\n}\n\nbool\nMD390Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode contacts\n  for (unsigned int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (i < ctx.count<DigitalContact>())\n      cont.fromContactObj(ctx.get<DMRContact>(i+1));\n    else\n      cont.clear();\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (! cont.isValid())\n      continue;\n    if (DMRContact *obj = cont.toContactObj()) {\n      ctx.config()->contacts()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid contact at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nMD390Codeplug::clearZones() {\n  // Clear zones & zone extensions\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement(data(ADDR_ZONES+i*ZONE_SIZE)).clear();\n  }\n}\n\nbool\nMD390Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES + i*ZONE_SIZE));\n    zone.clear();\n    if (! ctx.has<Zone>(i+1))\n      continue;\n    Zone *obj = ctx.get<Zone>(i+1);\n    zone.setName(obj->name());\n    // fill channels\n    for (int c=0; c<16; c++) {\n      if (c < obj->A()->count())\n        zone.setMemberIndex(c, ctx.index(obj->A()->get(c)));\n    }\n  }\n\n  return true;\n}\n\nbool\nMD390Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    Zone *obj = new Zone(zone.name());\n    obj->setName(zone.name());\n    ctx.config()->zones()->add(obj);\n    ctx.add(obj, i+1);\n  }\n\n  return true;\n}\n\nbool\nMD390Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (! ctx.has<Zone>(i+1))\n      continue;\n\n    Zone *obj = ctx.get<Zone>(i+1);\n    for (int j=0; ((j<16) && zone.memberIndex(j)); j++) {\n      if (! ctx.has<Channel>(zone.memberIndex(j))) {\n        logWarn() << \"Cannot link channel with index \" << zone.memberIndex(j)\n                  << \" channel not defined.\";\n        continue;\n      }\n      obj->A()->add(ctx.get<Channel>(zone.memberIndex(j)));\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD390Codeplug::clearGroupLists() {\n  for (int i=0; i<NUM_GROUPLISTS; i++)\n    GroupListElement(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE)).clear();\n}\n\nbool\nMD390Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (i < ctx.config()->rxGroupLists()->count())\n      glist.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx);\n    else\n      glist.clear();\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (RXGroupList *obj = glist.toGroupListObj(ctx)) {\n      ctx.config()->rxGroupLists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (! glist.linkGroupListObj(ctx.get<RXGroupList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nMD390Codeplug::clearScanLists() {\n  // Clear scan lists\n  for (int i=0; i<NUM_SCANLISTS; i++)\n    ScanListElement(data(ADDR_SCANLISTS + i*SCANLIST_SIZE)).clear();\n}\n\nbool\nMD390Codeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Scanlists\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (i < ctx.config()->scanlists()->count())\n      scan.fromScanListObj(ctx.config()->scanlists()->scanlist(i), ctx);\n    else\n      scan.clear();\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (ScanList *obj = scan.toScanListObj(ctx)) {\n      ctx.config()->scanlists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid scanlist at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (! scan.linkScanListObj(ctx.get<ScanList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD390Codeplug::clearPositioningSystems() {\n  // Clear GPS systems\n  for (int i=0; i<NUM_GPSSYSTEMS; i++)\n    GPSSystemElement(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE)).clear();\n}\n\nbool\nMD390Codeplug::encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (i < ctx.count<DMRAPRSSystem>()) {\n      logDebug() << \"Encode GPS system #\" << i << \" '\" <<\n                    ctx.get<DMRAPRSSystem>(i+1)->name() << \"'.\";\n      gps.fromGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx);\n    } else {\n      gps.clear();\n    }\n  }\n  return true;\n}\n\nbool\nMD390Codeplug::createPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (DMRAPRSSystem *obj = gps.toGPSSystemObj()) {\n      ctx.config()->posSystems()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nMD390Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (! gps.linkGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nMD390Codeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear();\n}\n\nbool\nMD390Codeplug::encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(flags); Q_UNUSED(err)\n  // Encode settings\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nMD390Codeplug::decodeButtonSetttings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(ctx.config());\n}\n\n\nvoid\nMD390Codeplug::clearPrivacyKeys() {\n  EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear();\n}\n\nbool\nMD390Codeplug::encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n  // First, reset keys\n  clearPrivacyKeys();\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // Encode keys\n  return keys.fromCommercialExt(ctx.config()->commercialExtension(), ctx);\n}\n\nbool\nMD390Codeplug::decodePrivacyKeys(Context &ctx, const ErrorStack &err) {\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // Decode element\n  if (! keys.updateCommercialExt(ctx)) {\n    errMsg(err) << \"Cannot create encryption extension.\";\n    return false;\n  }\n  return true;\n}\n\n\nvoid\nMD390Codeplug::clearTextMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\n\nbool\nMD390Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err);\n}\n\nbool\nMD390Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).decode(ctx, err);\n}\n\n\nvoid\nMD390Codeplug::clearMenuSettings() {\n  MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear();\n}\n\nvoid\nMD390Codeplug::clearEmergencySystems() {\n  EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear();\n  for (int i=0; i<NUM_EMERGENCY_SYSTEMS; i++)\n    EmergencySystemElement(data(ADDR_EMERGENCY_SYSTEMS + i*EMERGENCY_SYSTEM_SIZE)).clear();\n}\n"
  },
  {
    "path": "lib/md390_codeplug.hh",
    "content": "#ifndef MD390CODEPLUG_HH\n#define MD390CODEPLUG_HH\n\n#include \"dm1701_codeplug.hh\"\n\n/** Device specific implementation of the codeplug for the TyT MD-390(U/V).\n *\n * The codeplug consists of a single segment. It starts at address @c 0x002000 and ends at\n * address @c 0x040000.\n *\n * @section md390cpl Codeplug structure within radio\n * The codeplug structure is reverse engineered almost completely and can be programmed from\n * scratch. That is, it is not necessary to update an existing codeplug on the radio.\n * <table>\n *  <tr><th>Start</th>    <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">Segment 0x002000-0x040000</th></tr>\n *  <tr><td>0x002000</td> <td>0x00200c</td> <td>0x0000c</td> <td>Timestamp see @c TyTCodeplug::TimestampElement.</td></tr>\n *  <tr><td>0x00200c</td> <td>0x002040</td> <td>0x00034</td> <td>Reserved, filled with 0xff. </td></tr>\n *  <tr><td>0x002100</td> <td>0x002140</td> <td>0x00040</td> <td>Button config, see @c TyTCodeplug::ButtonSettingsElement.</td></tr>\n *  <tr><td>0x002140</td> <td>0x002180</td> <td>0x00040</td> <td>Reserved, filled with 0xff.</td></tr>\n *  <tr><td>0x002040</td> <td>0x0020f0</td> <td>0x000b0</td> <td>General settings see @c TyTCodeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x002180</td> <td>0x0059c0</td> <td>0x03840</td> <td>50 Text messages @ 0x120 bytes each, see @c TyTCodeplug::MessageElement.</td></tr>\n *  <tr><td>0x0059c0</td> <td>0x005a70</td> <td>0x000b0</td> <td>??? Privacy keys, see @c TyTCodeplug::EncryptionElement.</td></tr>\n *  <tr><td>0x005a70</td> <td>0x005a80</td> <td>0x00010</td> <td>Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement.</td></td>\n *  <tr><td>0x005a80</td> <td>0x005f80</td> <td>0x00500</td> <td>Emergency systems, see @c TyTCodeplug::EmergencySystemElement.</td></td>\n *  <tr><td>0x005f80</td> <td>0x00ec20</td> <td>0x008ca</td> <td>1000 contacts, see @c TyTCodeplug::ContactElement.</td></tr>\n *  <tr><td>0x00ec20</td> <td>0x0149e0</td> <td>0x05dc0</td> <td>250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement.</td></tr>\n *  <tr><td>0x0149e0</td> <td>0x018860</td> <td>0x03e80</td> <td>250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement.</td></tr>\n *  <tr><td>0x018860</td> <td>0x01edf0</td> <td>0x06590</td> <td>250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement.</td></tr>\n *  <tr><td>0x01ee00</td> <td>0x02e800</td> <td>0x0fa00</td> <td>1000 channels, see @c MD390Codeplug::ChannelElement.</td></tr>\n *  <tr><td>0x03ec40</td> <td>0x03ed40</td> <td>0x00100</td> <td>16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement.</td></tr>\n * </table>\n *\n * @ingroup md390 */\n\nclass MD390Codeplug : public TyTCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Extends the common @c TyTCodeplug::ChannelElement to implement the MD-390 specific settings.\n   *\n   * Memory layout of the channel (size 0x0040 bytes):\n   * @verbinclude md390_channel.txt */\n  class ChannelElement: public DM1701Codeplug::ChannelElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns @c true if the 'compressed UDP data header' is enabled. */\n    virtual bool compressedUDPHeader() const;\n    /** Enables/disables 'compressed UDP data header'. */\n    virtual void enableCompressedUDPHeader(bool enable);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual void fromChannelObj(const Channel *c, Context &ctx);\n  };\n\n  /** Extends the @c TyTCodeplug::MenuSettingsElement to implement the MD-390 specific menu settings.\n   *\n   * Memory layout of the settings (size 0x0010 bytes):\n   * @verbinclude md390_menusettings.txt */\n  class MenuSettingsElement: public TyTCodeplug::MenuSettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    MenuSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit MenuSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns @c true if GPS information is enabled. */\n    virtual bool gpsInformation() const;\n    /** Enables/disables GPS information menu. */\n    virtual void enableGPSInformation(bool enable);\n\n    bool fromConfig(const Config *config);\n    bool updateConfig(Config *config);\n  };\n\npublic:\n  /** Empty constructor. */\n  explicit MD390Codeplug(QObject *parent=nullptr);\n\n  Config *preprocess(Config *config, const ErrorStack &err) const;\n  bool postprocess(Config *config, const ErrorStack &err) const;\n\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearTimestamp();\n  bool encodeTimestamp();\n\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPositioningSystems();\n  bool encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSetttings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPrivacyKeys();\n  bool encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool decodePrivacyKeys(Context &ctx, const ErrorStack &err);\n\n  void clearTextMessages();\n  bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err);\n  bool decodeTextMessages(Context &ctx, const ErrorStack &err);\n\n  void clearMenuSettings();\n  void clearEmergencySystems();\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int messages() { return 0x002180; }\n    /// @endcond\n  };\n};\n\n#endif // MD390CODEPLUG_HH\n"
  },
  {
    "path": "lib/md390_filereader.cc",
    "content": "#include \"md390_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_FILE_ADDR   0x00002225\n#define SEGMENT0_TARGET_ADDR 0x00002000\n#define SEGMENT0_SIZE        0x0003e000\n\nbool\nMD390FileReader::read(const QString &filename, MD390Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (262709 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 262709 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err)  << \"Cannot read codeplug file '\" << filename\n                   << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/md390_filereader.hh",
    "content": "#ifndef MD390FILEREADER_HH\n#define MD390FILEREADER_HH\n\n#include \"md390_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is still pretty simple. The first part of the file consists of\n * a mal-formed DFU file. This contains a single image with a single element containing the\n * first section of the memory written to the device. The second section is then added as-is\n * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ.\n *\n * <table>\n *  <tr><th>File Start</th> <th>Memory Start</th> <th>Size</th></tr>\n *  <tr><td>0x002225</td>   <td>0x002000</td>     <td>0x03e000</td></tr>\n * </table>\n *\n * @ingroup md390 */\nclass MD390FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err Error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, MD390Codeplug *codeplug, const ErrorStack &err = ErrorStack());\n};\n\n#endif // MD390FILEREADER_HH\n"
  },
  {
    "path": "lib/md390_limits.cc",
    "content": "#include \"md390_limits.hh\"\n\n#include \"md390_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nMD390Limits::MD390Limits(const std::initializer_list<std::pair<Frequency,Frequency>> &freqRanges, QObject *parent)\n  : RadioLimits(true, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = false;\n  _callSignDBImplemented  = false;\n  _numCallSignDBEntries   = 0;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"introLine2\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::Mid),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(MD390Codeplug::GeneralSettingsElement::Limit::bootPasswordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, 1000,\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies(freqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(freqRanges)},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies(freqRanges, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies(freqRanges)},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::Mid),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name\n          { \"A\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems. */\n  add(\"positioning\", new RadioLimitList({\n        { DMRAPRSSystem::staticMetaObject, 0, 16, new RadioLimitObject {\n          { \"name\", new RadioLimitStringIgnored() },\n          { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n          { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n          { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) } } },\n        { FMAPRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } } ) );\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList {\n           {BasicEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::basicKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{4}\")}\n            }},\n           {AESEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{32}\")}\n            }} }\n        }\n      });\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/md390_limits.hh",
    "content": "#ifndef MD390LIMITS_HH\n#define MD390LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the radio limits for TyT MD-390 radios.\n * @ingroup md390 */\nclass MD390Limits: public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor from frequency ranges. */\n  MD390Limits(const std::initializer_list<std::pair<Frequency, Frequency> > &freqRanges, QObject *parent=nullptr);\n};\n\n#endif // MD390LIMITS_HH\n"
  },
  {
    "path": "lib/melody.cc",
    "content": "#include \"melody.hh\"\n#include \"logger.hh\"\n\n#include <QRegularExpression>\n\n\n/* ********************************************************************************************* *\n * Implementation of Melody\n * ********************************************************************************************* */\nMelody::Melody(unsigned int bpm, QObject *parent)\n  : ConfigItem(parent), _bpm(bpm), _melody()\n{\n  // pass...\n}\n\nConfigItem *\nMelody::clone() const {\n  Melody *melody = new Melody();\n  if (! melody->copy(*this)) {\n    melody->deleteLater();\n    return nullptr;\n  }\n  return melody;\n}\n\nbool\nMelody::copy(const ConfigItem &other) {\n  if (! ConfigItem::copy(other))\n    return false;\n\n  const Melody *omelody = other.as<Melody>();\n\n  _bpm = omelody->_bpm;\n  _melody = omelody->_melody;\n\n  return true;\n}\n\nbool\nMelody::isEmpty() const {\n  return 0 == _melody.count();\n}\n\nMelody::iterator\nMelody::begin() {\n  return _melody.begin();\n}\nMelody::iterator\nMelody::end() {\n  return _melody.end();\n}\n\nMelody::const_iterator\nMelody::begin() const {\n  return _melody.begin();\n}\nMelody::const_iterator\nMelody::end() const {\n  return _melody.end();\n}\n\nsize_t\nMelody::count() const {\n  return _melody.count();\n}\n\nconst Melody::Note &\nMelody::operator [](size_t index) const {\n  return _melody[index];\n}\n\nMelody::Note &\nMelody::operator [](size_t index) {\n  return _melody[index];\n}\n\nunsigned int\nMelody::bpm() const {\n  return _bpm;\n}\nvoid\nMelody::setBPM(unsigned int bpm) {\n  if (_bpm == bpm)\n    return;\n  _bpm = bpm;\n  emit modified(this);\n}\n\nbool\nMelody::fromLilypond(const QString &melody) {\n  _melody.clear();\n  Note::Duration currentDuration = Note::Duration::Whole;\n  for (QString note : melody.split(\" \", Qt::SkipEmptyParts)) {\n    Note n;\n    if (! n.fromLilypond(note, currentDuration))\n      return false;\n    currentDuration = n.duration;\n    _melody.append(n);\n  }\n\n  emit modified(this);\n  return true;\n}\n\nQString\nMelody::toLilypond() const {\n  QStringList res;\n  Note::Duration currentDuration = Note::Duration::Whole;\n  for (Note note : _melody) {\n    res.append(note.toLilypond(currentDuration));\n    currentDuration = note.duration;\n  }\n  return res.join(\" \");\n}\n\nQVector<QPair<double, unsigned int>>\nMelody::toTones() const {\n  QVector<QPair<double, unsigned int>> res;\n  for (const Note &note: _melody) {\n    res.append(note.toTone(_bpm));\n  }\n  return res;\n}\n\nbool\nMelody::infer(const QVector<QPair<double, unsigned int> > &tones) {\n  // Find minimum quantization error -> infer also BPM\n  unsigned int best_bpm = 30;\n  unsigned int best_cost = quantizationTimingError(tones, best_bpm) + std::abs(100-(int)best_bpm);\n  for (unsigned int bpm=31; bpm<200; bpm++) {\n    unsigned int cost = quantizationTimingError(tones, best_bpm);\n    if (cost < best_cost) {\n      best_bpm = bpm;\n      best_cost = cost + std::abs(100 - (int)bpm);\n    }\n  }\n  _bpm = best_bpm;\n  _melody.clear();\n  for(const QPair<double,unsigned int> &tone: tones) {\n    Note note; note.infer(tone.first, tone.second, _bpm);\n    _melody.append(note);\n  }\n\n  emit modified(this);\n  return true;\n}\n\nunsigned int\nMelody::quantizationTimingError(const QVector<QPair<double, unsigned int> > &tones, unsigned int bpm) {\n  if (0 == tones.count())\n    return 0;\n  unsigned int error=0;\n  for (const QPair<double,unsigned int> &tone: tones) {\n    error += Note::quantizationTimingError(tone.second, bpm);\n  }\n  return error / tones.count();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of Melody::Note\n * ********************************************************************************************* */\nMelody::Note::Note()\n  : tone(Tone::C), duration(Duration::Quarter), dotted(false), octave(0)\n{\n  // pass...\n}\n\nbool\nMelody::Note::fromLilypond(const QString &note, Duration currentDuration) {\n  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|)(\\\\.|)$\");\n  QRegularExpression rest_pattern(\"^r(1|2|4|8|16|)(\\\\.|)$\");\n  QRegularExpressionMatch note_match = note_pattern.match(note);\n\n  if (note_match.hasMatch()) {\n    if (\"c\" == note_match.captured(1)) tone=Tone::C;\n    else if ((\"cis\" == note_match.captured(1)) || (\"des\" == note_match.captured(1))) tone=Tone::Cis;\n    else if (\"d\" == note_match.captured(1)) tone=Tone::D;\n    else if ((\"dis\" == note_match.captured(1)) || (\"ees\" == note_match.captured(1))) tone=Tone::Dis;\n    else if (\"e\" == note_match.captured(1)) tone=Tone::E;\n    else if (\"f\" == note_match.captured(1)) tone=Tone::F;\n    else if ((\"fis\" == note_match.captured(1)) || (\"ges\" == note_match.captured(1))) tone=Tone::Fis;\n    else if (\"g\" == note_match.captured(1)) tone=Tone::G;\n    else if ((\"gis\" == note_match.captured(1)) || (\"aes\" == note_match.captured(1))) tone=Tone::Gis;\n    else if (\"a\" == note_match.captured(1)) tone=Tone::A;\n    else if ((\"ais\" == note_match.captured(1)) || (\"bes\" == note_match.captured(1))) tone=Tone::Ais;\n    else if (\"b\" == note_match.captured(1)) tone=Tone::B;\n    else return false;\n\n    if (0 == note_match.capturedLength(2)) octave = 0;\n    else if ('\\'' == note_match.captured(2).at(0)) octave = note_match.capturedLength(2);\n    else if (','  == note_match.captured(2).at(0)) octave = -note_match.capturedLength(2);\n    else return false;\n\n    if (0 == note_match.capturedLength(3)) duration = currentDuration;\n    else if (\"1\" == note_match.captured(3)) duration = Duration::Whole;\n    else if (\"2\" == note_match.captured(3)) duration = Duration::Half;\n    else if (\"4\" == note_match.captured(3)) duration = Duration::Quarter;\n    else if (\"8\" == note_match.captured(3)) duration = Duration::Eighth;\n    else if (\"16\" == note_match.captured(3)) duration = Duration::Sixteenth;\n    else return false;\n\n    dotted = (1 == note_match.capturedLength(4));\n\n    return true;\n  }\n\n  QRegularExpressionMatch rest_match = rest_pattern.match(note);\n  if (rest_match.hasMatch()) {\n    tone = Tone::Rest;\n    if (0 == rest_match.capturedLength(1)) duration = currentDuration;\n    else if (\"1\" == rest_match.captured(1)) duration = Duration::Whole;\n    else if (\"2\" == rest_match.captured(1)) duration = Duration::Half;\n    else if (\"4\" == rest_match.captured(1)) duration = Duration::Quarter;\n    else if (\"8\" == rest_match.captured(1)) duration = Duration::Eighth;\n    else if (\"16\" == rest_match.captured(1)) duration = Duration::Sixteenth;\n    else return false;\n    dotted = (1 == rest_match.capturedLength(2));\n    return true;\n  }\n\n  return false;\n}\n\nQString\nMelody::Note::toLilypond(Duration currentDuration) const {\n  QString res; res.reserve(10);\n  switch (tone) {\n  case Tone::C: res.append(\"c\"); break;\n  case Tone::Cis: res.append(\"cis\"); break;\n  case Tone::D: res.append(\"d\"); break;\n  case Tone::Dis: res.append(\"dis\"); break;\n  case Tone::E: res.append(\"e\"); break;\n  case Tone::F: res.append(\"f\"); break;\n  case Tone::Fis: res.append(\"fis\"); break;\n  case Tone::G: res.append(\"g\"); break;\n  case Tone::Gis: res.append(\"gis\"); break;\n  case Tone::A: res.append(\"a\"); break;\n  case Tone::Ais: res.append(\"ais\"); break;\n  case Tone::B: res.append(\"b\"); break;\n  case Tone::Rest: res.append(\"r\"); break;\n  }\n  if (Tone::Rest != tone) {\n    if (0 > octave) {\n      for (int i=0; i<(-octave); i++)\n        res.append(\",\");\n    } else if (0 < octave) {\n      for (int i=0; i<octave; i++)\n        res.append(\"'\");\n    }\n  }\n  if (duration != currentDuration) {\n    switch (duration) {\n    case Duration::Whole: res.append(\"1\"); break;\n    case Duration::Half: res.append(\"2\"); break;\n    case Duration::Quarter: res.append(\"4\"); break;\n    case Duration::Eighth: res.append(\"8\"); break;\n    case Duration::Sixteenth: res.append(\"16\"); break;\n    }\n  }\n  if (dotted) res.append(\".\");\n  return res;\n}\n\nQPair<double, unsigned int>\nMelody::Note::toTone(unsigned int bpm) const {\n  unsigned int bar = (4*60000)/bpm;\n  unsigned int dur = bar;\n\n  if (Duration::Whole == duration) {\n    dur = bar; if (dotted) dur += dur/2;\n  } else if (Duration::Half == duration) {\n    dur = bar/2; if (dotted) dur += dur/2;\n  } else if (Duration::Quarter == duration) {\n    dur = bar/4; if (dotted) dur += dur/2;\n  } else if (Duration::Eighth == duration) {\n    dur = bar/8; if (dotted) dur += dur/2;\n  } else if (Duration::Sixteenth == duration) {\n    dur = bar/16; if (dotted) dur += dur/2;\n  }\n\n  if (Tone::Rest == tone) {\n    return QPair<double, unsigned int>(0.0, dur);\n  }\n\n  int halfTones = 12*octave;\n  switch (tone) {\n  case Tone::C:   halfTones -=  9; break;\n  case Tone::Cis: halfTones -=  8; break;\n  case Tone::D:   halfTones -=  7; break;\n  case Tone::Dis: halfTones -=  6; break;\n  case Tone::E:   halfTones -=  5; break;\n  case Tone::F:   halfTones -=  4; break;\n  case Tone::Fis: halfTones -=  3; break;\n  case Tone::G:   halfTones -=  2; break;\n  case Tone::Gis: halfTones -=  1; break;\n  case Tone::A: break;\n  case Tone::Ais: halfTones += 1; break;\n  case Tone::B:   halfTones += 2; break;\n  case Tone::Rest: break;\n  }\n  double freq = 440.*std::pow(2.0,halfTones/12.0);\n  return QPair<double, unsigned int>(freq, dur);\n}\n\nunsigned int\nMelody::Note::infer(double frequency, unsigned int ms, unsigned int bpm) {\n  if (frequency) {\n    // Try to infer half-tones from A4\n    int halfTones = std::round(std::log2(frequency/440.)*12.0);\n    int halfTonesC4 = halfTones+9;\n    octave = halfTonesC4/12;\n    halfTonesC4 = std::abs(halfTonesC4)%12;\n    switch(halfTonesC4) {\n    case 0: tone = Tone::C; break;\n    case 1: tone = Tone::Cis; break;\n    case 2: tone = Tone::D; break;\n    case 3: tone = Tone::Dis; break;\n    case 4: tone = Tone::E; break;\n    case 5: tone = Tone::F; break;\n    case 6: tone = Tone::Fis; break;\n    case 7: tone = Tone::G; break;\n    case 8: tone = Tone::Gis; break;\n    case 9: tone = Tone::A; break;\n    case 10: tone = Tone::Ais; break;\n    case 11: tone = Tone::B; break;\n    }\n  } else if (ms) {\n    // Obviously a rest\n    octave = 0; tone = Tone::Rest;\n  } else {\n    logWarn() << \"Cannot infer a note of frequency and duration 0.\";\n    octave = 0; tone = Tone::Rest; duration = Duration::Quarter; dotted=false;\n  }\n\n  // Try to infer note duration from duration and BPM\n  static const unsigned int bar = (4*60000)/bpm;\n  int fraction = std::round(std::log2(bar)-std::log2(ms));\n  int diff = 0;\n  if (1 > fraction) {\n    duration = Duration::Whole;\n    diff = std::abs((int)ms-(int)bar);\n  } else if (2 > fraction) {\n    duration = Duration::Half;\n    diff = std::abs((int)ms-(int)bar/2);\n  } else if (3 > fraction) {\n    duration = Duration::Quarter;\n    diff = std::abs((int)ms-(int)bar/4);\n  } else if (4 > fraction) {\n    duration = Duration::Eighth;\n    diff = std::abs((int)ms-(int)bar/8);\n  } else {\n    duration = Duration::Sixteenth;\n    diff = std::abs((int)ms-(int)bar/16);\n  }\n\n  return diff;\n}\n\nunsigned int\nMelody::Note::quantizationTimingError(unsigned int ms, unsigned int bpm) {\n  static const unsigned int bar = (4*60000)/bpm;\n  int fraction = std::round(std::log2(bar)-std::log2(ms));\n  // Limit onto [2^0, 2^4] fractions == [1/1, 1/16]\n  fraction = std::max(0, std::min(4, fraction));\n  return std::abs(ms - bar*std::pow(2, fraction));\n}\n"
  },
  {
    "path": "lib/melody.hh",
    "content": "#ifndef MELODY_HH\n#define MELODY_HH\n\n#include \"configobject.hh\"\n#include <QPair>\n\n/** A config item that encodes a melody. This can be used to configure several melodies like\n * ring tones and boot-up melodies. Have fun with it. In contrast to the common manufacturer CPSs,\n * qdmr uses a proper musical notation for that.\n *\n * This however, comes with some difficulties. While the translation from musical notation to\n * tone frequencies and durations is easy, the reverse direction is not. The note duration is the\n * most difficult. In musical notation, durations are expressed in terms of fractions of a bar,\n * implicitly defined by the beat frequency. This additional information must be inferred. To achieve\n * this, this class searches for the BPM, that minimizes the quantization error in the duration\n * while keeping the BPM as close as possible to 100 BPM. Yes, this is utterly over-engineered.\n *\n * @ingroup config */\nclass Melody : public ConfigItem\n{\n  Q_OBJECT\n\n  /** The BPM of the melody. */\n  Q_PROPERTY(unsigned int bpm READ bpm WRITE setBPM)\n  /** The melody in LilyPond notation. */\n  Q_PROPERTY(QString melody READ toLilypond WRITE fromLilypond)\n\npublic:\n  /** Encodes a note, that is tone and duration. */\n  struct Note {\n    /** Possible tone values. */\n    enum class Tone {\n      Rest, C, Cis, D, Dis, E, F, Fis, G, Gis, A, Ais, B\n    };\n    /** Note durations as fractions of a bar. */\n    enum class Duration {\n      Whole, Half, Quarter, Eighth, Sixteenth\n    };\n\n    /** The note tone. */\n    Tone tone;\n    /** The note duration. */\n    Duration duration;\n    /** If @c true, the note/rest is dottet. */\n    bool dotted;\n    /** The octave of the note, 0 means middle. */\n    int octave;\n\n    /** Default constructor. A middle C quarter note. */\n    Note();\n\n    /** Reads a note in Lilypond notation. */\n    bool fromLilypond(const QString &note, Duration currentDuration);\n\n    /** Serializes the note in Lilypond notation. */\n    QString toLilypond(Duration currentDuration) const;\n\n    /** Converts the note to a frequency in Hz and duration in seconds. */\n    QPair<double, unsigned int> toTone(unsigned int bpm) const;\n\n    /** Infers the note from the given frequency and duration in ms.\n     * This is guesswork, consequently there will be some issues. The function updates the note and\n     * returns the timing error in ms. */\n    unsigned int infer(double frequency, unsigned int ms, unsigned int bpm);\n\n    /** Computes the quantization timing error for the given duration and BPM. */\n    static unsigned int quantizationTimingError(unsigned int ms, unsigned int bpm);\n  };\n\n  /** Iterator over notes. */\n  typedef QVector<Note>::iterator iterator;\n  /** Const iterator over notes. */\n  typedef QVector<Note>::const_iterator const_iterator;\n\npublic:\n  /** Empty constructor. */\n  Melody(unsigned int bpm=100, QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n  bool copy(const ConfigItem &other);\n\n  /** Returns @c true, if the melody has no notes (invalid). */\n  bool isEmpty() const;\n\n  /** Returns an iterator pointing at the first note. */\n  iterator begin();\n  /** Returns an iterator pointing right after the last note. */\n  iterator end();\n  /** Returns a const-iterator pointing at the first note. */\n  const_iterator begin() const;\n  /** Returns a const-iterator pointing right after the last note. */\n  const_iterator end() const;\n\n  /** Returns the number of notes (and rests) of this melody. */\n  size_t count() const;\n  /** Element access. */\n  const Note &operator [](size_t index) const;\n  /** Element access. */\n  Note &operator [](size_t index);\n\n  /** Returns the BPM of the melody. */\n  unsigned int bpm() const;\n  /** Sets the BPM of the melody. */\n  void setBPM(unsigned int bpm);\n\n  /** Parses the Lilypond notation of the melody.\n   * For example,\n   * @code\n   * a8 b e2 des4 d\n   * @endcode\n   */\n  bool fromLilypond(const QString &melody);\n  /** Serializes the melody into Lilypond notation. */\n  QString toLilypond() const;\n\n  /** Converts the melody to a series of tones in terms of frequency and duration in ms. */\n  QVector<QPair<double, unsigned int>> toTones() const;\n  /** Infer melody from a vector of frequeny-duration pairs. */\n  bool infer(const QVector<QPair<double, unsigned int>> &tones);\n\nprotected:\n  /** Computes the absolute quantization timing error over the given melody for the given BPM. */\n  static unsigned int quantizationTimingError(\n      const QVector<QPair<double, unsigned int>> &tones, unsigned int bpm);\n\nprotected:\n  /** Holds the beats per minute. */\n  unsigned int _bpm;\n  /** The actual melody. */\n  QVector<Note> _melody;\n};\n\n#endif // MELODY_HH\n"
  },
  {
    "path": "lib/melody_stream.cc",
    "content": "#include \"melody_stream.hh\"\n\n#include \"logger.hh\"\n#include \"melody.hh\"\n#include <qendian.h>\n\nMelodyStream::MelodyStream(QObject *parent)\n  : QBuffer(parent), _melody(), _format(), _raw()\n{\n  _format.setSampleRate(44100.0);\n  _format.setSampleFormat(QAudioFormat::Int16);\n  _format.setChannelCount(1);\n  setBuffer(&_raw);\n}\n\nconst QAudioFormat&\nMelodyStream::audioFormat() const {\n  return _format;\n}\n\nvoid\nMelodyStream::setMelody(Melody *melody) {\n  _raw.clear();\n  if (!_melody.isNull())\n    disconnect(_melody, &ConfigItem::modified, this, &MelodyStream::reload);\n  _melody = melody;\n  if (_melody.isNull())\n    return;\n  connect(_melody, &ConfigItem::modified, this, &MelodyStream::reload);\n  reload();\n}\n\nvoid\nMelodyStream::reload() {\n  if (_melody.isNull() || _melody->isEmpty())\n    return;\n  double beat = _format.sampleRate() * _melody->bpm()/60.0;\n  auto tones = _melody->toTones();\n\n  double duration = beat / 16; // two 1/32 silence at beginning and end\n\n  for (const auto &tone : tones)\n    duration += (tone.second * _format.sampleRate()) / 1000.;\n  unsigned int samples = duration;\n  // pre-allocate data\n  _raw.fill(0, samples*2);\n\n  int head = beat/64, tail=beat/32, startSample = head;\n  for (const auto &tone : tones) {\n    int toneDuration = tone.second * _format.sampleRate() / 1000.0;\n    // Handle silence\n    if (0 == tone.first) {\n      startSample += toneDuration;\n      continue;\n    }\n    // Evaluate sin and envelope\n    for (int i = -head; i < toneDuration+tail; i++) {\n      double envelope = 1.;\n      if (i <= 0)\n        envelope = double(head+i)/head;\n      else if (i >= toneDuration)\n        envelope = 1 - double(i-toneDuration)/tail;\n      int16_t value = (1<<12) * envelope * std::sin(2*M_PI * tone.first * i / _format.sampleRate());\n      *reinterpret_cast<int16_t *>(_raw.data() + 2 * (startSample + i)) += value;\n    }\n    startSample += toneDuration;\n  }\n}"
  },
  {
    "path": "lib/melody_stream.hh",
    "content": "#ifndef MELODY_STREAM_HH\n#define MELODY_STREAM_HH\n\n#include <QAudioFormat>\n#include <QBuffer>\n#include <QPointer>\nclass Melody;\n\n/** Represents a raw stream playing a melody. */\nclass MelodyStream : public QBuffer\n{\nQ_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit MelodyStream(QObject *parent = nullptr);\n  /** Returns the audio format of the stream. */\n  const QAudioFormat &audioFormat() const;\n  /** Sets the melody. */\n  void setMelody(Melody *melody);\n\nprotected slots:\n  void reload();\n\nprotected:\n  QPointer<Melody> _melody;\n  QAudioFormat _format;\n  QByteArray _raw;\n};\n\n\n\n#endif //QDMR_MELODY_STREAM_HH\n"
  },
  {
    "path": "lib/opengd77.cc",
    "content": "#include \"opengd77.hh\"\n#include \"logger.hh\"\n#include \"config.hh\"\n\n\nOpenGD77::OpenGD77(OpenGD77Interface *device, QObject *parent)\n  : OpenGD77Base(device, parent), _name(\"Open GD-77\"), _codeplug(), _callsigns()\n{\n  _satelliteConfig = new OpenGD77SatelliteConfig(this);\n}\n\n\nconst QString &\nOpenGD77::name() const {\n  return _name;\n}\n\n\n\nconst Codeplug &\nOpenGD77::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nOpenGD77::codeplug() {\n  return _codeplug;\n}\n\n\nconst CallsignDB *\nOpenGD77::callsignDB() const {\n  return &_callsigns;\n}\n\nCallsignDB *\nOpenGD77::callsignDB() {\n  return &_callsigns;\n}\n\n\nRadioInfo\nOpenGD77::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::OpenGD77, \"opengd77\", \"OpenGD77\", \"OpenGD77 Project\", {OpenGD77Interface::interfaceInfo()});\n}\n"
  },
  {
    "path": "lib/opengd77.hh",
    "content": "#ifndef OPENGD77_HH\n#define OPENGD77_HH\n\n#include \"opengd77base.hh\"\n#include \"opengd77_interface.hh\"\n#include \"opengd77_codeplug.hh\"\n#include \"opengd77_callsigndb.hh\"\n\n\n/** Implements an USB interface to Open GD-77(S) VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * @ingroup ogd77 */\nclass OpenGD77 : public OpenGD77Base\n{\n\tQ_OBJECT\n\npublic:\n\t/** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit OpenGD77(OpenGD77Interface *device=nullptr, QObject *parent=nullptr);\n\n\tconst QString &name() const;\n\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprotected:\n  /** The device identifier. */\n\tQString _name;\n  /** The actual binary codeplug representation. */\n  OpenGD77Codeplug _codeplug;\n  /** The actual binary callsign DB representation. */\n  OpenGD77CallsignDB _callsigns;\n};\n\n#endif // OPENGD77_HH\n"
  },
  {
    "path": "lib/opengd77_callsigndb.cc",
    "content": "#include \"opengd77_callsigndb.hh\"\n#include \"utils.hh\"\n#include \"userdatabase.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77CallsignDB\n * ******************************************************************************************** */\nOpenGD77CallsignDB::OpenGD77CallsignDB(QObject *parent)\n  : OpenGD77BaseCallsignDB(parent)\n{\n  addImage(\"OpenGD77 call-sign database\");\n}\n\n\nbool\nOpenGD77CallsignDB::encode(UserDatabase *calldb, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Limit entry count\n  auto n = std::min((unsigned int)calldb->count(), Limit::entries());\n  if (selection.hasCountLimit())\n    n = std::min(n, (unsigned int)selection.countLimit());\n  // If there are no entries -> done.\n  if (0 == n)\n    return true;\n  auto n0 = std::min(n, Limit::entries0()),\n      n1 = std::min(n - n0, Limit::entries1());\n\n  // Select first n entries and sort them in ascending order of their IDs\n  QVector<UserDatabase::User> users;\n  for (unsigned i=0; i<n; i++)\n    users.append(calldb->user(i));\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n\n  if (n)\n    logDebug() << \"Store \" << n << \" entries starting from \"\n               << users.front().id << \":\" << users.front().call << \", \" << users.front().name << \" in \" << users.front().city\n               << \" to \" << users.back().id << \":\" << users.back().call << \", \" << users.back().name << \" in \" << users.back().city;\n\n  // Allocate segment0 for user db if requested\n  unsigned size = align_size(DatabaseHeaderElement::size()+n0*DatabaseEntryElement::size(),\n                             Limit::blockSize());\n  this->image(0).addElement(Offset::header(), size);\n  size = align_size(n1*DatabaseEntryElement::size(), Limit::blockSize());\n  if (n1)\n    this->image(0).addElement(Offset::entries1(), size);\n\n  // Encode user DB\n  DatabaseHeaderElement header(this->data(Offset::header()));\n  header.clear(); header.setEntrySize(DatabaseEntryElement::size()); header.setEntryCount(n);\n  for (unsigned i=0; i<n0; i++) {\n    DatabaseEntryElement(this->data(Offset::entries0() + i*DatabaseEntryElement::size()))\n        .fromEntry(users[i]);\n  }\n  for (unsigned i=0; i<n1; i++) {\n    DatabaseEntryElement(this->data(Offset::entries1() + i*DatabaseEntryElement::size()))\n        .fromEntry(users[n0+i]);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/opengd77_callsigndb.hh",
    "content": "#ifndef OPENGD77CALLSIGNDB_HH\n#define OPENGD77CALLSIGNDB_HH\n\n#include \"opengd77base_callsigndb.hh\"\n#include \"userdatabase.hh\"\n\n/** Represents and encodes the binary format for the call-sign database within the radio.\n *\n * The memory layout of the call-sign DB is relatively simple. The DB starts at address\n * @c 0x00030000 with a maximum size of @c 0x00040000. The first 12bytes form the DB header\n * (see @c OpenGD77CallsignDB::userdb_t) followed by the DB entries (see\n * @c OpenGD77CallsignDB::userdb_entry_t).\n *\n * The entries can be of variable size. The size of each entry is encoded in the header. QDMR uses\n * a fixed size of 19bytes per entry. The entries must be sorted in ascending order to allow for an\n * efficient binary search. No index table is used here.\n *\n * @ingroup ogd77 */\nclass OpenGD77CallsignDB : public OpenGD77BaseCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit OpenGD77CallsignDB(QObject *parent=nullptr);\n\n  static constexpr unsigned int size0() { return 0x40000; }\n  static constexpr unsigned int size1() { return 0x48000; }\n\n  /** Encodes as many entries as possible of the given user-database. */\n  bool encode(UserDatabase *calldb, const Flags &selection=Flags(),\n              const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits of the callsign DB. */\n  struct Limit: public OpenGD77BaseCallsignDB::Limit {\n    /// Number of entries, segment 0.\n    static constexpr unsigned int entries0() {\n      return (size0()-DatabaseHeaderElement::size())/DatabaseEntryElement::size();\n    }\n    /// Number of entries, segment 1.\n    static constexpr unsigned int entries1() {\n      return size1()/DatabaseEntryElement::size();\n    }\n    static constexpr unsigned int entries() {\n      return entries0() + entries1();\n    }\n  };\n\nprotected:\n  /** Some internal offsets within the callsign db. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int header()   { return 0x030000; }\n    static constexpr unsigned int entries0() { return header() + DatabaseHeaderElement::size(); }\n    static constexpr unsigned int entries1() { return 0x0b8000; }\n    /// @endcond\n  };\n};\n\n#endif // OPENGD77CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/opengd77_codeplug.cc",
    "content": "#include \"opengd77_codeplug.hh\"\n#include \"config.hh\"\n#include \"channel.hh\"\n#include <QDateTime>\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77Codeplug\n * ******************************************************************************************** */\nOpenGD77Codeplug::OpenGD77Codeplug(QObject *parent)\n  : OpenGD77BaseCodeplug(parent)\n{\n  // Delete allocated image by GD77 codeplug\n  addImage(\"OpenGD77 Codeplug EEPROM\");\n  image(EEPROM).addElement(Offset::settings(), 0x05fe0);\n  image(EEPROM).addElement(0x07500, 0x03b00);\n\n  addImage(\"OpenGD77 Codeplug FLASH\");\n  image(FLASH).addElement(Offset::additionalSettings(), AdditionalSettingsElement::size());\n  image(FLASH).addElement(0x7b000, 0x13e60);\n}\n\nvoid\nOpenGD77Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  GeneralSettingsElement el(data(Offset::settings(), ImageIndex::settings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n\n  return el.encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).decode(ctx, err);\n}\n\n\nvoid\nOpenGD77Codeplug::clearDTMFSettings() {\n  //DTMFSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\nbool\nOpenGD77Codeplug::decodeDTMFSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\n\nvoid\nOpenGD77Codeplug::clearAPRSSettings() {\n  APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  APRSSettingsBankElement el(data(Offset::aprsSettings(), ImageIndex::aprsSettings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n\n  return el.encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::decodeAPRSSettings(Context &ctx, const ErrorStack &err) {\n  return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings()))\n      .decode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::linkAPRSSettings(Context &ctx, const ErrorStack &err) {\n  return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings()))\n      .link(ctx, err);\n}\n\n\nbool\nOpenGD77Codeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  // Encode boot melody if set\n  if (ctx.config()->settings()->tone()->bootToneEnabled() && !ctx.config()->settings()->tone()->bootMelody()->isEmpty()) {\n    AdditionalSettingsElement opt(data(Offset::additionalSettings(), ImageIndex::additionalSettings()));\n    if (! flags.updateCodeplug() && ! opt.isValid())\n      opt.clear();\n    opt.bootMelody().encode(ctx, ctx.config()->settings()->tone()->bootMelody(), err);\n  }\n  // Encode other boot settings\n  return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings()))\n      .encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) {\n  // Check if boot melody is encoded\n  AdditionalSettingsElement opt(data(Offset::additionalSettings(), ImageIndex::additionalSettings()));\n  if (opt.hasSettings(AdditionalSettingsElement::BootMelody)) {\n    ctx.config()->settings()->tone()->enableBootTone(\n      opt.bootMelody().decode(ctx, ctx.config()->settings()->tone()->bootMelody(), err));\n  }\n  return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings()))\n      .decode(ctx, err);\n}\n\n\nvoid\nOpenGD77Codeplug::clearContacts() {\n  ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).decode(ctx, err);\n}\n\n\nvoid\nOpenGD77Codeplug::clearDTMFContacts() {\n  DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts()))\n      .encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts()))\n      .decode(ctx, err);\n}\n\n\nvoid\nOpenGD77Codeplug::clearChannels() {\n  for (unsigned int b=0; b<Limit::channelBanks(); b++) {\n    if (0 == b)\n      ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0())).clear();\n    else\n      ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(),\n                              ImageIndex::channelBank1())).clear();\n  }\n}\n\nbool\nOpenGD77Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  for (unsigned int b=0, c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i<ChannelBankElement::Limit::channelCount(); i++, c++) {\n      if (ctx.has<Channel>(c)) {\n        if (! bank.channel(i).encode(ctx.get<Channel>(c), ctx, err)) {\n          errMsg(err) << \"Cannot encode channel '\" << ctx.get<Channel>(c)->name()\n                      << \"' at index \" << i << \" of bank \" << b << \".\";\n          return false;\n        }\n        bank.enable(i, true);\n      } else {\n        bank.enable(i, false);\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int b=0,c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i < ChannelBankElement::Limit::channelCount(); i++) {\n      if (! bank.isEnabled(i))\n        continue;\n\n      Channel *obj = bank.channel(i).decode(ctx, err);\n      if (nullptr == obj) {\n        errMsg(err) << \"Cannot create channel from index \" << i << \" in bank \" << b << \".\";\n        return false;\n      }\n      ctx.config()->channelList()->add(obj);\n      ctx.add(obj, c++);\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int b=0,c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i < ChannelBankElement::Limit::channelCount(); i++) {\n      if (! bank.isEnabled(i))\n        continue;\n\n      Channel *obj = ctx.get<Channel>(c);\n      if (! bank.channel(i).link(obj, ctx, err)) {\n        errMsg(err) << \"Cannot link channel '\" << obj->name()\n                    << \"' from index \" << i << \" in bank \" << b << \".\";\n        return false;\n      }\n      c++;\n    }\n  }\n\n  return true;\n}\n\nvoid\nOpenGD77Codeplug::clearBootSettings() {\n  BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())).clear();\n}\n\n\nvoid\nOpenGD77Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(Offset::vfoA(), ImageIndex::vfoA())).clear();\n  VFOChannelElement(data(Offset::vfoB(), ImageIndex::vfoB())).clear();\n}\n\nvoid\nOpenGD77Codeplug::clearZones() {\n  ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).decode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).link(ctx, err);\n}\n\nvoid\nOpenGD77Codeplug::clearGroupLists() {\n  GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).clear();\n}\n\nbool\nOpenGD77Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).encode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).decode(ctx, err);\n}\n\nbool\nOpenGD77Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).link(ctx, err);\n}\n\n"
  },
  {
    "path": "lib/opengd77_codeplug.hh",
    "content": "#ifndef OPENGD77_CODEPLUG_HH\n#define OPENGD77_CODEPLUG_HH\n\n#include \"opengd77base_codeplug.hh\"\n#include \"opengd77_extension.hh\"\n\n\n/** Represents, encodes and decodes the device specific codeplug for Open GD-77 firmware.\n * This codeplug is almost identical to the original GD77 codeplug.\n *\n * @section ogd77cpl Codeplug structure within radio\n * The memory representation of the codeplug within the radio is divided into two images\n * (EEPROM and Flash) and each image again into two sections.\n\n * The first segment of the EEPROM image starts at the address 0x000e0 and ends at 0x06000, while\n * the second EEPROM section starts at 0x07500 and ends at 0x0b000.\n *\n * The first segment of the Flash image starts at the address 0x00000 and ends at 0x011a0, while the\n * second Flash section starts at 0x7b000 and ends at 0x8ee60.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First EEPROM segment 0x000e0-0x06000</th></tr>\n *  <tr><td>0x000e0</td> <td>0x000ec</td> <td>0x000c</td> <td>General settings, see @c RadioddityCodeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x000ec</td> <td>0x00128</td> <td>0x003c</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x00128</td> <td>0x01370</td> <td>0x1248</td> <td>32 message texts, see @c RadioddityCodeplug::MessageBankElement.</td></tr>\n *  <tr><td>0x01370</td> <td>0x01790</td> <td>0x0420</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x01790</td> <td>0x02dd0</td> <td>0x1640</td> <td>64 scan lists, see @c GD77Codeplug::ScanListBankElement, GD77Codeplug::ScanListElement.</td></tr>\n *  <tr><td>0x02dd0</td> <td>0x02f88</td> <td>0x01b8</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x02f88</td> <td>0x03388</td> <td>0x0400</td> <td>DTMF contacts, see RadioddityCodeplug::DTMFContactElement.</td></tr>\n *  <tr><td>0x03388</td> <td>0x03780</td> <td>0x03f8</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x03780</td> <td>0x05390</td> <td>0x1c10</td> <td>First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement, @c OpenGD77Codeplug::ChannelElement.</td></tr>\n *  <tr><td>0x05390</td> <td>0x06000</td> <td>0x0c70</td> <td>??? Unknown ???</td></tr>\n *  <tr><th colspan=\"4\">Second EEPROM segment 0x07500-0x13000</th></tr>\n *  <tr><td>0x07500</td> <td>0x07518</td> <td>0x0018</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07518</td> <td>0x07538</td> <td>0x0020</td> <td>Boot settings, see @c RadioddityCodeplug::BootSettingsElement.</td></tr>\n *  <tr><td>0x07538</td> <td>0x07540</td> <td>0x0008</td> <td>Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.</td></tr>\n *  <tr><td>0x07540</td> <td>0x07560</td> <td>0x0020</td> <td>2 intro lines, @c RadioddityCodeplug::BootTextElement.</td></tr>\n *  <tr><td>0x07560</td> <td>0x07590</td> <td>0x0030</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07590</td> <td>0x075c8</td> <td>0x0038</td> <td>VFO A settings @c OpenGD77Codeplug::VFOChannelElement</td></tr>\n *  <tr><td>0x075c8</td> <td>0x07600</td> <td>0x0038</td> <td>VFO B settings @c OpenGD77Codeplug::VFOChannelElement</td></tr>\n *  <tr><td>0x07600</td> <td>0x08010</td> <td>0x0a10</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x08010</td> <td>0x12c10</td> <td>0xac00</td> <td>250 zones, see @c OpenGD77Codeplug::ZoneBankElement, @c OpenGD77Codeplug::ZoneElement.</td></tr>\n *  <tr><td>0x12c10</td> <td>0x13000</td> <td>0x03f0</td> <td>??? Unknown ???</td></tr>\n *\n *  <tr><th colspan=\"4\">First Flash segment 0x00000-0x011a0</th></tr>\n *  <tr><td>0x00000</td> <td>0x011a0</td> <td>0x11a0</td> <td>??? Unknown ???</td></tr>\n *  <tr><th colspan=\"4\">Second Flash segment 0x7b000-0x8ee60</th></tr>\n *  <tr><td>0x7b000</td> <td>0x7b1b0</td> <td>0x01b0</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x7b1b0</td> <td>0x87620</td> <td>0xc470</td> <td>Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement and @c OpenGD77Codeplug::ChannelElement.</td></tr>\n *  <tr><td>0x87620</td> <td>0x8d620</td> <td>0x6000</td> <td>1024 contacts, see @c OpenGD77Codeplug::ContactElement.</td></tr>\n *  <tr><td>0x8d620</td> <td>0x8e2a0</td> <td>0x0c80</td> <td>76 RX group lists, see @c GD77Codeplug::GroupListBankElement, @c GD77Codeplug::GroupListElement.</td></tr>\n *  <tr><td>0x8e2a0</td> <td>0x8ee60</td> <td>0x0bc0</td> <td>??? Unknown ???</td></tr>\n * </table>\n * @ingroup ogd77 */\nclass OpenGD77Codeplug: public OpenGD77BaseCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs an empty codeplug for the GD-77. */\n  explicit OpenGD77Codeplug(QObject *parent=nullptr);\n\npublic:\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFSettings();\n  bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearAPRSSettings();\n  bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFContacts();\n  bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearBootSettings();\n  bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearVFOSettings();\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some Limits for this codeplug. */\n  struct Limit: public Element::Limit {\n    /** Number of channel banks. */\n    static constexpr unsigned int channelBanks() { return 8; }\n  };\n\nprotected:\n  /** Internal used image indices. */\n  struct ImageIndex {\n    /// @cond DO_NOT_DOCUEMNT\n    static constexpr unsigned int settings()           { return EEPROM; }\n    static constexpr unsigned int dtmfSettings()       { return EEPROM; }\n    static constexpr unsigned int aprsSettings()       { return EEPROM; }\n    static constexpr unsigned int dtmfContacts()       { return EEPROM; }\n    static constexpr unsigned int channelBank0()       { return EEPROM; }\n    static constexpr unsigned int bootSettings()       { return EEPROM; }\n    static constexpr unsigned int vfoA()               { return EEPROM; }\n    static constexpr unsigned int vfoB()               { return EEPROM; }\n    static constexpr unsigned int zoneBank()           { return EEPROM; }\n    static constexpr unsigned int additionalSettings() { return FLASH; }\n    static constexpr unsigned int channelBank1()       { return FLASH; }\n    static constexpr unsigned int contacts()           { return FLASH; }\n    static constexpr unsigned int groupLists()         { return FLASH; }\n    /// @endcond\n  };\n\n  /** Some offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUEMNT\n    static constexpr unsigned int settings()           { return 0x000080; }\n    static constexpr unsigned int dtmfSettings()       { return 0x001470; }\n    static constexpr unsigned int aprsSettings()       { return 0x001588; }\n    static constexpr unsigned int dtmfContacts()       { return 0x002f88; }\n    static constexpr unsigned int channelBank0()       { return 0x003780; } // Channels 1-128\n    static constexpr unsigned int bootSettings()       { return 0x007518; }\n    static constexpr unsigned int vfoA()               { return 0x007590; }\n    static constexpr unsigned int vfoB()               { return 0x0075c8; }\n    static constexpr unsigned int zoneBank()           { return 0x008010; }\n    static constexpr unsigned int additionalSettings() { return 0x000000; }\n    static constexpr unsigned int channelBank1()       { return 0x07b1b0; } // Channels 129-1024\n    static constexpr unsigned int contacts()           { return 0x087620; }\n    static constexpr unsigned int groupLists()         { return 0x08d620; }\n    /// @endcond\n  };\n};\n\n#endif // OPENGD77_CODEPLUG_HH\n"
  },
  {
    "path": "lib/opengd77_extension.cc",
    "content": "#include \"opengd77_extension.hh\"\n#include \"utils.hh\"\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77ChannelExtension\n * ******************************************************************************************** */\nOpenGD77ChannelExtension::OpenGD77ChannelExtension(QObject *parent)\n  : ConfigExtension(parent), _zoneSkip(false), _allSkip(false), _beep(true), _powerSave(true),\n    _location(), _txTalkerAliasTS1(TalkerAlias::None), _txTalkerAliasTS2(TalkerAlias::None)\n{\n  // pass...\n}\n\nConfigItem *\nOpenGD77ChannelExtension::clone() const {\n  OpenGD77ChannelExtension *ex = new OpenGD77ChannelExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\n\nbool\nOpenGD77ChannelExtension::scanZoneSkip() const {\n  return _zoneSkip;\n}\n\nvoid\nOpenGD77ChannelExtension::enableScanZoneSkip(bool enable) {\n  _zoneSkip = enable;\n}\n\n\nbool\nOpenGD77ChannelExtension::scanAllSkip() const {\n  return _allSkip;\n}\n\nvoid\nOpenGD77ChannelExtension::enableScanAllSkip(bool enable) {\n  _allSkip = enable;\n}\n\n\nbool\nOpenGD77ChannelExtension::beep() const {\n  return _beep;\n}\n\nvoid\nOpenGD77ChannelExtension::enableBeep(bool enable) {\n  _beep = enable;\n}\n\n\nbool\nOpenGD77ChannelExtension::powerSave() const {\n  return _powerSave;\n}\n\nvoid\nOpenGD77ChannelExtension::enablePowerSave(bool enable) {\n  _powerSave = enable;\n}\n\n\nbool\nOpenGD77ChannelExtension::locationEnabled() const {\n  return _locationEnabled;\n}\n\nvoid\nOpenGD77ChannelExtension::enableLocation(bool enable) {\n  if (_locationEnabled == enable)\n    return;\n  _locationEnabled = enable;\n  emit modified(this);\n}\n\n\nconst QGeoCoordinate &\nOpenGD77ChannelExtension::location() const {\n  return _location;\n}\n\nvoid\nOpenGD77ChannelExtension::setLocation(const QGeoCoordinate &loc) {\n  _location = loc;\n}\n\nQString\nOpenGD77ChannelExtension::locator() const {\n  return deg2loc(location(), 8);\n}\n\nvoid\nOpenGD77ChannelExtension::setLocator(const QString &loc) {\n  _location = loc2deg(loc);\n}\n\n\nOpenGD77ChannelExtension::TalkerAlias\nOpenGD77ChannelExtension::talkerAliasTS1() const {\n  return _txTalkerAliasTS1;\n}\n\nvoid\nOpenGD77ChannelExtension::setTalkerAliasTS1(TalkerAlias ta) {\n  _txTalkerAliasTS1 = ta;\n}\n\n\nOpenGD77ChannelExtension::TalkerAlias\nOpenGD77ChannelExtension::talkerAliasTS2() const {\n  return _txTalkerAliasTS2;\n}\n\nvoid\nOpenGD77ChannelExtension::setTalkerAliasTS2(TalkerAlias ta) {\n  _txTalkerAliasTS2 = ta;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77ContactExtension\n * ******************************************************************************************** */\nOpenGD77ContactExtension::OpenGD77ContactExtension(QObject *parent)\n  : ConfigExtension(parent), _timeSlotOverride(TimeSlotOverride::None)\n{\n  // pass...\n}\n\nConfigItem *\nOpenGD77ContactExtension::clone() const {\n  OpenGD77ContactExtension *ex = new OpenGD77ContactExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\nOpenGD77ContactExtension::TimeSlotOverride\nOpenGD77ContactExtension::timeSlotOverride() const {\n  return _timeSlotOverride;\n}\nvoid\nOpenGD77ContactExtension::setTimeSlotOverride(TimeSlotOverride ts) {\n  _timeSlotOverride = ts;\n}\n\n"
  },
  {
    "path": "lib/opengd77_extension.hh",
    "content": "/** @defgroup ogd77ex OpenGD77 Extensions\n * This module collects classes that implement the firmware specific extensions to the common\n * codeplug configuration for radios running the OpenGD77 firmware.\n *\n * @ingroup ogd77 */\n#ifndef OPENGD77EXTENSION_HH\n#define OPENGD77EXTENSION_HH\n\n#include <QGeoCoordinate>\n#include \"configobject.hh\"\n#include \"melody.hh\"\n\n\n/** Implements the channel extensions for the OpenGD77 radios.\n * @since 0.9.0\n * @ingroup ogd77ex */\nclass OpenGD77ChannelExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"Channel settings for OpenGD77 radios.\")\n  Q_CLASSINFO(\"longDescription\", \"This extension implements all channel settings specific to radios \"\n              \"running the OpenGD77 firmware.\")\n\n  /** The zone skip flag. */\n  Q_PROPERTY(bool scanZoneSkip READ scanZoneSkip WRITE enableScanZoneSkip)\n  /** The all skip flag. */\n  Q_PROPERTY(bool scanAllSkip READ scanAllSkip WRITE enableScanAllSkip)\n  /** The beep enable flag. */\n  Q_PROPERTY(bool beep READ beep WRITE enableBeep)\n  /** The power save enable flag. */\n  Q_PROPERTY(bool powerSave READ powerSave WRITE enablePowerSave)\n  /** Sets a fixed location for the APRS report. */\n  Q_PROPERTY(QString location READ locator WRITE setLocator)\n  /** Sets the talker alias for timeslot 1. */\n  Q_PROPERTY(TalkerAlias talkerAliasTS1 READ talkerAliasTS1 WRITE setTalkerAliasTS1)\n  /** Sets the talker alias for timeslot 2. */\n  Q_PROPERTY(TalkerAlias talkerAliasTS2 READ talkerAliasTS2 WRITE setTalkerAliasTS2)\n\n\npublic:\n  enum class TalkerAlias {\n    None, APRS, Text, Both\n  };\n  Q_ENUM(TalkerAlias)\n\n\npublic:\n  /** Constructor. */\n  Q_INVOKABLE explicit OpenGD77ChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the zone skip flag is set. */\n  bool scanZoneSkip() const;\n  /** Enables/disables zone skip. */\n  void enableScanZoneSkip(bool enable);\n\n  /** Returns @c true if the all-skip flag is set. */\n  bool scanAllSkip() const;\n  /** Enables/disables all skip. */\n  void enableScanAllSkip(bool enable);\n\n  /** Returns @c true if the beep tone is enabled for this channel. */\n  bool beep() const;\n  /** Enable beep tone for this channel. */\n  void enableBeep(bool enable);\n\n  /** Returns @c true, if power save is enabled for this channel (default: true). */\n  bool powerSave() const;\n  /** Enables power save for this channel. */\n  void enablePowerSave(bool enable);\n\n  /** Returns @c true if the fixed location is enabled. */\n  bool locationEnabled() const;\n  /** Enables the fixed location. */\n  void enableLocation(bool enable);\n  /** Returns the fixed location for this channel. */\n  const QGeoCoordinate &location() const;\n  /** Returns the fixed location for this channel. */\n  QString locator() const;\n  /** Sets the fixed location for this channel. */\n  void setLocation(const QGeoCoordinate &loc);\n  /** Sets the fixed location for this channel. */\n  void setLocator(const QString &locator);\n\n  /** Returns the talker alias setting for timeslot 1. */\n  TalkerAlias talkerAliasTS1() const;\n  /** Sets the talker alias setting for timeslot 1. */\n  void setTalkerAliasTS1(TalkerAlias ta);\n\n  /** Returns the talker alias setting for timeslot 2. */\n  TalkerAlias talkerAliasTS2() const;\n  /** Sets the talker alias setting for timeslot 2. */\n  void setTalkerAliasTS2(TalkerAlias ta);\n\nprotected:\n  /** Holds the zone skip flag. */\n  bool _zoneSkip;\n  /** Holds the all skip flag. */\n  bool _allSkip;\n  /** Holds the beep enable flag. */\n  bool _beep;\n  /** Holds the power-save flag. */\n  bool _powerSave;\n  /** If @c true, fixed location is used. */\n  bool _locationEnabled;\n  /** Holds the fixed location. */\n  QGeoCoordinate _location;\n  /** Holds the talker alias setting for timeslot 1. */\n  TalkerAlias _txTalkerAliasTS1;\n  /** Holds the talker alias setting for timeslot 2. */\n  TalkerAlias _txTalkerAliasTS2;\n};\n\n\n/** Implements the contact extensions for the OpenGD77 radios.\n * @since 0.9.0\n * @ingroup ogd77ex */\nclass OpenGD77ContactExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"DMR contact settings for OpenGD77 radios.\")\n  Q_CLASSINFO(\"longDescription\", \"This extension implements all contact settings specific to radios \"\n              \"running the OpenGD77 firmware. As the OpenGD77 codeplug is derived from the \"\n              \"Radioddity GD77 codeplug, all Radioddity extension also apply.\")\n\n  /** If set, overrides the channel time slot if this contact is selected as the transmit contact. */\n  Q_PROPERTY(TimeSlotOverride timeSlotOverride READ timeSlotOverride WRITE setTimeSlotOverride)\n  Q_CLASSINFO(\"timeSlotOverrideDescription\", \"If set, overrides the channels timeslot.\")\n  Q_CLASSINFO(\"timeSlotOverrideLongDescription\",\n              \"The OpenGD77 firmware allows contacts to override the channel time slot if the \"\n              \"contact is selected as the current destination contact for that channel. This allows \"\n              \"to assign a specific time slot to a contact, rather than creating a particular \"\n              \"channel for that contact that only differs in the time slot.\")\n\npublic:\n  /** Possible modes of time slot override. */\n  enum class TimeSlotOverride {\n    None, ///< Do not override time slot.\n    TS1,  ///< Override with time slot 1.\n    TS2   ///< Override with time slot 2.\n  };\n  Q_ENUM(TimeSlotOverride)\n\npublic:\n  /** Constructor. */\n  Q_INVOKABLE explicit OpenGD77ContactExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the time slot override. */\n  TimeSlotOverride timeSlotOverride() const;\n  /** Sets the time slot override. */\n  void setTimeSlotOverride(TimeSlotOverride ts);\n\nprotected:\n  /** Holds the time slot override. */\n  TimeSlotOverride _timeSlotOverride;\n};\n\n\n#endif // OPENGD77EXTENSION_HH\n"
  },
  {
    "path": "lib/opengd77_interface.cc",
    "content": "#include \"opengd77_interface.hh\"\n#include \"logger.hh\"\n#include \"radioinfo.hh\"\n#include <QtEndian>\n#include <QDateTime>\n#include <QTimeZone>\n\n\n#define USB_VID 0x1fc9\n#define USB_PID 0x0094\n\n#define BLOCK_SIZE  32\n#define SECTOR_SIZE 4096\n#define ALIGN_BLOCK_SIZE(n) ((0==((n)%BLOCK_SIZE)) ? (n) : (n)+(BLOCK_SIZE-((n)%BLOCK_SIZE)))\n\n#define TIMEOUT -1 // ms\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77Interface::FirmwareInfo\n * ********************************************************************************************* */\nbool\nOpenGD77Interface::FirmwareInfo::featureInvertedDisplay() const {\n  return (qFromLittleEndian(features) & (1<<0));\n}\n\nbool\nOpenGD77Interface::FirmwareInfo::featureExtendedCallsignDB() const {\n  return (qFromLittleEndian(features) & (1<<1));\n}\n\nbool\nOpenGD77Interface::FirmwareInfo::featureVoicePromptLoaded() const {\n  return (qFromLittleEndian(features) & (1<<2));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77Interface::ReadRequest\n * ********************************************************************************************* */\nbool\nOpenGD77Interface::ReadRequest::initReadEEPROM(uint32_t addr, uint16_t length) {\n  this->type = 'R';\n  this->command = READ_EEPROM;\n  this->address = qToBigEndian(addr);\n  this->length = qToBigEndian(length);\n  return true;\n}\n\nbool\nOpenGD77Interface::ReadRequest::initReadFlash(uint32_t addr, uint16_t length) {\n  this->type = 'R';\n  this->command = READ_FLASH;\n  this->address = qToBigEndian(addr);\n  this->length = qToBigEndian(length);\n  return true;\n}\n\nbool\nOpenGD77Interface::ReadRequest::initReadFirmwareInfo() {\n  this->type = 'R';\n  this->command = READ_FIRMWARE_INFO;\n  this->address = 0;\n  this->length = qToBigEndian((uint16_t)sizeof(FirmwareInfo));\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77Interface::WriteRequest\n * ********************************************************************************************* */\nbool\nOpenGD77Interface::WriteRequest::initWriteEEPROM(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size) {\n  if (size > 32)\n    size = 32;\n  this->type = (Variant::GD77 == variant) ? 'W' : 'X';\n  this->command = WRITE_EEPROM;\n  this->payload.address = qToBigEndian(addr);\n  this->payload.length = qToBigEndian(size);\n  memcpy(this->payload.data, data, size);\n  return true;\n}\n\nbool\nOpenGD77Interface::WriteRequest::initSetFlashSector(Variant variant, uint32_t addr) {\n  uint32_t sec = addr/SECTOR_SIZE;\n  this->type = (Variant::GD77 == variant) ? 'W' : 'X';\n  logDebug() << \"Send SET_FLASH_SECTOR (\" << this->type << \"): \" << Qt::hex << sec << \"h\";\n  this->command = SET_FLASH_SECTOR;\n  this->sector[0] = ((sec>>16) & 0xff);\n  this->sector[1] = ((sec>>8) & 0xff);\n  this->sector[2] = (sec & 0xff);\n  return true;\n}\n\nbool\nOpenGD77Interface::WriteRequest::initWriteFlash(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size) {\n  if (size > 32)\n    size = 32;\n  this->type = (Variant::GD77 == variant) ? 'W' : 'X';\n  this->command = WRITE_SECTOR_BUFFER;\n  logDebug() << \"Send WRITE_FLASH_BUFFER (\" << this->type << \") @ \" << Qt::hex << addr << \".\";\n  this->payload.address = qToBigEndian(addr);\n  this->payload.length = qToBigEndian(size);\n  memcpy(this->payload.data, data, size);\n  return true;\n}\n\nbool\nOpenGD77Interface::WriteRequest::initFinishWriteFlash(Variant variant) {\n  this->type = (Variant::GD77 == variant) ? 'W' : 'X';\n  this->command = WRITE_FLASH_SECTOR;\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77Interface::CommandRequest\n * ********************************************************************************************* */\nvoid\nOpenGD77Interface::CommandRequest::initShowCPSScreen() {\n  this->type = 'C';\n  this->command = SHOW_CPS_SCREEN;\n  this->x = 0;\n  this->y = 0;\n  this->font = 0;\n  this->alignment = 0;\n  this->inverted = 0;\n  memset(this->message, 0, sizeof(this->message));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initClearScreen() {\n  this->type = 'C';\n  this->command = CLEAR_SCREEN;\n  this->x = 0;\n  this->y = 0;\n  this->font = 0;\n  this->alignment = 0;\n  this->inverted = 0;\n  memset(this->message, 0, sizeof(this->message));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initDisplay(uint8_t x, uint8_t y,\n                                               const char *message, unsigned int iSize,\n                                               uint8_t font, uint8_t alignment, uint8_t inverted) {\n  this->type = 'C';\n  this->command = DISPLAY;\n  this->x = x;\n  this->y = y;\n  this->font = font;\n  this->alignment = alignment;\n  this->inverted = inverted;\n  memset(this->message, 0, 16);\n  strncpy(this->message, message, std::min(16u, iSize));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initRenderCPS() {\n  this->type = 'C';\n  this->command = RENDER_CPS;\n  this->x = 0;\n  this->y = 0;\n  this->font = 0;\n  this->alignment = 0;\n  this->inverted = 0;\n  memset(this->message, 0, sizeof(this->message));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initCloseScreen() {\n  this->type = 'C';\n  this->command = CLOSE_CPS_SCREEN;\n  this->x = 0;\n  this->y = 0;\n  this->font = 0;\n  this->alignment = 0;\n  this->inverted = 0;\n  memset(this->message, 0, sizeof(this->message));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initCommand(Option option) {\n  this->type = 'C';\n  this->command = COMMAND;\n  this->option = option;\n  this->y = 0;\n  this->font = 0;\n  this->alignment = 0;\n  this->inverted = 0;\n  memset(this->message, 0, sizeof(this->message));\n}\n\nvoid\nOpenGD77Interface::CommandRequest::initSetDateTime(const QDateTime &dt) {\n  initCommand(SET_DATETIME);\n  this->timestamp = qToLittleEndian(dt.toUTC().toSecsSinceEpoch());\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77Interface\n * ********************************************************************************************* */\nOpenGD77Interface::OpenGD77Interface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : USBSerial(descr, QSerialPort::Baud115200, err, parent), _extendedCallsignDB(false),\n  _sector(-1)\n{\n  // pass...\n}\n\nOpenGD77Interface::~OpenGD77Interface() {\n  // pass...\n}\n\nUSBDeviceInfo\nOpenGD77Interface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID, USB_PID);\n}\n\nQList<USBDeviceDescriptor>\nOpenGD77Interface::detect(bool saveOnly) {\n  Q_UNUSED(saveOnly)\n  return USBSerial::detect(USB_VID, USB_PID, true);\n}\n\nvoid\nOpenGD77Interface::close() {\n  if (isOpen())\n    USBSerial::close();\n}\n\nRadioInfo\nOpenGD77Interface::identifier(const ErrorStack &err) {\n  Q_UNUSED(err);\n  if (! isOpen())\n    return RadioInfo();\n\n  FirmwareInfo info;\n  if (! readFirmwareInfo(info, err)) {\n    errMsg(err) << \"Cannot identify OpenGD77 variant.\";\n    return RadioInfo();\n  }\n\n  logDebug() << \"Got type=\" << info.radioType << \".\";\n  switch ((FirmwareInfo::RadioType)info.radioType) {\n  case FirmwareInfo::RadioType::GD77:\n  case FirmwareInfo::RadioType::GD77S:\n  case FirmwareInfo::RadioType::RD5R:\n  case FirmwareInfo::RadioType::DM1801:\n  case FirmwareInfo::RadioType::DM1801A:\n    _protocolVariant = Variant::GD77;\n  return RadioInfo::byID(RadioInfo::OpenGD77);\n\n  case FirmwareInfo::RadioType::MDUV380:\n  case FirmwareInfo::RadioType::MD380:\n  case FirmwareInfo::RadioType::DM1701:\n  case FirmwareInfo::RadioType::DM1701RGB:\n  case FirmwareInfo::RadioType::MD9600:\n  case FirmwareInfo::RadioType::MD2017:\n    _protocolVariant = Variant::UV380;\n    _extendedCallsignDB = info.featureExtendedCallsignDB() &&\n                          !info.featureVoicePromptLoaded();\n  return RadioInfo::byID(RadioInfo::OpenUV380);\n  }\n\n  errMsg(err) << \"Unknown OpenGD77 variant \" << info.radioType << \".\";\n  return RadioInfo();\n}\n\n\nbool\nOpenGD77Interface::extendedCallsignDB() const {\n  return _extendedCallsignDB;\n}\n\n\nbool\nOpenGD77Interface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err)\n{\n  logDebug() << \"Send enter prog mode ...\";\n  if (! sendShowCPSScreen(err))\n    return false;\n  //logDebug() << \"Send clear screen ...\";\n  if (! sendClearScreen(err))\n    return false;\n  //logDebug() << \"Send display text ...\";\n  if (! sendDisplay(0, 0, \"qDMR\", 4, 1, 0, err))\n    return false;\n  if (! sendDisplay(0, 16, \"Writing\", 7, 1, 0, err))\n    return false;\n  if (! sendDisplay(0, 32, \"Codeplug\", 8, 1, 0, err))\n    return false;\n  //logDebug() << \"Send 'render CPS' ...\";\n  if (! sendRenderCPS(err))\n    return false;\n  //logDebug() << \"Send 'flash red LED' ...\";\n  if (! sendCommand(CommandRequest::FLASH_RED_LED, err))\n    return false;\n  //logDebug() << \"Send save settings and VFOs ...\";\n  if (! sendCommand(CommandRequest::SAVE_SETTINGS_AND_VFOS, err))\n    return false;\n\n  if (EEPROM == bank) {\n    if (_sector >= 0) {\n      if (! finishWriteFlash(err))\n        return false;\n    }\n    _sector = -1;\n  } else if (FLASH == bank) {\n    int32_t sector = addr/SECTOR_SIZE;\n    if ((-1 != _sector) && (_sector != sector)) {\n      if (! finishWriteFlash(err))\n        return false;\n    }\n  }\n  return true;\n}\n\nbool\nOpenGD77Interface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err)\n{\n  if (EEPROM == bank) {\n    if ((0 <= _sector) && (! finishWriteFlash(err)))\n      return false;\n    for (int i=0; i<nbytes; i+=BLOCK_SIZE) {\n      if (! writeEEPROM(addr+i, data+i, BLOCK_SIZE, err)) {\n        _sector = -1;\n        return false;\n      }\n    }\n    return true;\n  }\n\nstart:\n  int32_t sector = addr/SECTOR_SIZE;\n\n  if (0 > _sector) {\n    if (! setFlashSector(addr))\n      return false;\n    _sector = sector;\n  }\n\n  if (sector == _sector) {\n    for (int i=0; i<nbytes; i+=BLOCK_SIZE) {\n      if (! writeFlash(addr+i, data+i, BLOCK_SIZE)) {\n        _sector = -1;\n        return false;\n      }\n    }\n  } else {\n    _sector = -1;\n    if (! finishWriteFlash()) {\n      return false;\n    }\n    goto start;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::write_finish(const ErrorStack &err) {\n  if (0 > _sector)\n    return true;\n  _sector = -1;\n  if (! finishWriteFlash(err))\n    return false;\n  if (! sendCloseScreen(err))\n    return false;\n  return true;\n}\n\nbool\nOpenGD77Interface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr)\n\n  if (! sendShowCPSScreen(err))\n    return false;\n  if (! sendClearScreen(err))\n    return false;\n  if (! sendDisplay(0, 0, \"qDMR\", 4, 1, 0, err))\n    return false;\n  if (! sendDisplay(0, 16, \"Reading\", 7, 1, 0, err))\n    return false;\n  if (! sendDisplay(0, 32, \"Codeplug\", 8, 1, 0, err))\n    return false;\n  if (! sendRenderCPS(err))\n    return false;\n  if (! sendCommand(CommandRequest::FLASH_GREEN_LED, err))\n    return false;\n  if (! sendCommand(CommandRequest::SAVE_SETTINGS_AND_VFOS, err))\n    return false;\n\n  return true;\n}\n\nbool\nOpenGD77Interface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  if (! isOpen()) {\n    errMsg(err) << \"Cannot read block: Device not open!\";\n    return false;\n  }\n\n  for (int i=0; i<nbytes; i+=BLOCK_SIZE) {\n    bool ok;\n    if (EEPROM == bank)\n      ok = readEEPROM(addr+i, data+i, BLOCK_SIZE, err);\n    else if (FLASH == bank)\n      ok = readFlash(addr+i, data+i, BLOCK_SIZE, err);\n    else {\n      errMsg(err) << \"Cannot read from bank \" << bank << \": Unknown memory bank.\";\n      return false;\n    }\n\n    if (! ok) {\n      errMsg(err) << \"Cannot read from bank \" << bank << \", addr \" << Qt::hex << addr+i << \"h.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::read_finish(const ErrorStack &err) {\n  if (! sendCloseScreen(err))\n    return false;\n\n  return true;\n}\n\nbool\nOpenGD77Interface::setDateTime(const QDateTime &datetime, const ErrorStack &err) {\n  return sendSetDateTime(datetime, err);\n}\n\nbool\nOpenGD77Interface::saveSettingsNotVFOs(const ErrorStack &err) {\n  logDebug() << \"Send cmd SAVE_SETTINGS_NOT_VFOS.\";\n  return sendCommand(CommandRequest::SAVE_SETTINGS_NOT_VFOS, err);\n}\n\nbool\nOpenGD77Interface::saveSettingsAndVFOs(const ErrorStack &err) {\n  logDebug() << \"Send cmd SAVE_SETTINGS_AND_VFOS.\";\n  return sendCommand(CommandRequest::SAVE_SETTINGS_AND_VFOS, err);\n}\n\nbool\nOpenGD77Interface::reboot(const ErrorStack &err) {\n  logDebug() << \"Send cmd REBOOT.\";\n  return sendCommand(CommandRequest::REBOOT, err);\n}\n\n\nbool\nOpenGD77Interface::readEEPROM(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err) {\n  Q_UNUSED(len)\n\n  if (! isOpen()) {\n    errMsg(err) << \"Cannot read block: Device not open!\";\n    return false;\n  }\n\n  ReadRequest req; req.initReadEEPROM(addr, BLOCK_SIZE);\n  if (sizeof(ReadRequest) != QSerialPort::write((const char *)&req, sizeof(ReadRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  ReadResponse resp;\n  int retlen = QSerialPort::read((char *)&resp, sizeof(ReadResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot read from serial port: Device returned empty message.\";\n    return false;\n  }\n\n  if ('R' != resp.type) {\n    errMsg(err) << \"Cannot read from device: Device returned error '\" << resp.type << \"'.\";\n    return false;\n  }\n\n  if (qFromBigEndian(req.length) != qFromBigEndian(resp.length)) {\n    errMsg(err) << \"Cannot read from device: Device returned invalid length \" <<\n                qFromBigEndian(resp.length) << \".\";\n    return false;\n  }\n\n  memcpy(data, resp.data, qFromBigEndian(resp.length));\n  return true;\n}\n\n\nbool\nOpenGD77Interface::writeEEPROM(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err) {\n  WriteRequest req; req.initWriteEEPROM(_protocolVariant, addr, data, len);\n  WriteResponse resp;\n\n  if ((8+len) != QSerialPort::write((const char *)&req, 8+len)) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot write EEPROM: Device returned empty message.\";\n    return false;\n  }\n\n  if ((req.type != resp.type) || (req.command != resp.command)) {\n    errMsg(err) << \"Cannot write EEPROM at \" << Qt::hex << addr\n             << \": Device returned error \" << resp.type << \".\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77Interface::readFlash(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err) {\n  Q_UNUSED(len)\n\n  if (! isOpen()) {\n    errMsg(err) << \"Cannot read block: Device not open!\";\n    return false;\n  }\n\n  ReadRequest req;\n  req.initReadFlash(addr, BLOCK_SIZE);\n  if (sizeof(ReadRequest) != QSerialPort::write((const char *)&req, sizeof(ReadRequest))) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  ReadResponse resp;\n  int retlen = QSerialPort::read((char *)&resp, sizeof(ReadResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot read from serial port: Device returned empty message.\";\n    return false;\n  }\n\n  if ('R' != resp.type) {\n    errMsg(err) << \"Cannot read from device: Device returned error \" << resp.type << \".\";\n    return false;\n  }\n\n  if (qFromBigEndian(req.length) != qFromBigEndian(resp.length)) {\n    errMsg(err) << \"Cannot read from device: Device returned invalid length \"\n                << qFromBigEndian(resp.length) << \".\";\n    return false;\n  }\n\n  memcpy(data, resp.data, qFromBigEndian(resp.length));\n  return true;\n}\n\nbool\nOpenGD77Interface::setFlashSector(uint32_t addr, const ErrorStack &err) {\n  WriteRequest req; req.initSetFlashSector(_protocolVariant, addr);\n  WriteResponse resp;\n\n  if (5 != QSerialPort::write((const char *)&req, 5)) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot set flash sector: Device returned empty message.\";\n    return false;\n  }\n\n  if ((req.type != resp.type) || (req.command != resp.command)) {\n    errMsg(err) << \"Cannot set flash sector: Device returned error \" << resp.type << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::writeFlash(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err) {\n  WriteRequest req; req.initWriteFlash(_protocolVariant, addr, data, len);\n  WriteResponse resp;\n\n  if ((8+len) != QSerialPort::write((const char *)&req, 8+len)) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot write to buffer: Device returned empty message.\";\n    return false;\n  }\n\n  if ((req.type != resp.type) || (req.command != resp.command)) {\n    errMsg(err) << \"Cannot write to buffer at \" << QString::number(addr,16)\n             << \": Device returned error \" << resp.type << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::finishWriteFlash(const ErrorStack &err) {\n  //logDebug() << \"Send finish write flash command ...\";\n  WriteRequest req;\n  req.initFinishWriteFlash(_protocolVariant);\n  logDebug() << \"Send cmd WRITE_FLASH_SECTOR.\";\n  WriteResponse resp;\n\n  if ((2) != QSerialPort::write((const char *)&req, 2)) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, sizeof(WriteResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot write to flash: Device returned empty message.\";\n    return false;\n  }\n\n  if ((req.type != resp.type) || (req.command != resp.command)) {\n    errMsg(err) << \"Cannot write to flash: Device returned error \" << resp.type << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::readFirmwareInfo(OpenGD77Interface::FirmwareInfo &radioInfo, const ErrorStack &err) {\n  logDebug() << \"Request radio info.\";\n  ReadRequest req; req.initReadFirmwareInfo();\n\n  if (sizeof(ReadRequest) != QSerialPort::write((const char *)&req, sizeof(ReadRequest))) {\n    errMsg(err) << \"Serial port error: \" << QSerialPort::errorString();\n    errMsg(err) << \"Cannot send read request.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  ReadResponse resp;\n  int retlen = QSerialPort::read((char *)&resp, sizeof(ReadResponse));\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot read radio info: Device returned empty message.\";\n    return false;\n  }\n\n  if (req.type != resp.type) {\n    errMsg(err) << \"Cannot read radio info: Device returned error \" << resp.type << \", expected 'R'.\";\n    return false;\n  }\n\n  memcpy(&radioInfo, &(resp.info), sizeof(FirmwareInfo));\n\n  return true;\n}\n\nbool\nOpenGD77Interface::sendShowCPSScreen(const ErrorStack &err) {\n  CommandRequest req;\n  uint8_t resp;\n  req.initShowCPSScreen();\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::sendClearScreen(const ErrorStack &err) {\n  CommandRequest req;\n  req.initClearScreen();\n  uint8_t resp;\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << QSerialPort::errorString();\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::sendDisplay(uint8_t x, uint8_t y, const char *message, uint8_t iSize, uint8_t alignment, uint8_t inverted, const ErrorStack &err) {\n  CommandRequest req;\n  req.initDisplay(x,y, message, iSize, 3, alignment, inverted);\n  uint8_t resp;\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::sendRenderCPS(const ErrorStack &err) {\n  CommandRequest req;\n  req.initRenderCPS();\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  uint8_t resp;\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77Interface::sendCloseScreen(const ErrorStack &err) {\n  CommandRequest req; req.initCloseScreen();\n  uint8_t resp;\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77Interface::sendSetDateTime(const QDateTime &dt, const ErrorStack &err) {\n  CommandRequest req; req.initSetDateTime(dt);\n  uint8_t resp;\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77Interface::sendCommand(CommandRequest::Option option, const ErrorStack &err) {\n  CommandRequest req; req.initCommand(option);\n  uint8_t resp;\n\n  if (sizeof(CommandRequest) != QSerialPort::write((const char *) &req, sizeof(CommandRequest))) {\n    errMsg(err) << \"Cannot write to serial port.\";\n    return false;\n  }\n\n  if (! waitForReadyRead(TIMEOUT)) {\n    errMsg(err) << \"Cannot read from serial port: Timeout!\";\n    return false;\n  }\n\n  int retlen = QSerialPort::read((char *)&resp, 1);\n\n  if (0 > retlen) {\n    errMsg(err) << \"Cannot read from serial port.\";\n    return false;\n  } else if (0 == retlen) {\n    errMsg(err) << \"Cannot send command: Device returned empty message.\";\n    return false;\n  } else if ('-' != resp) {\n    errMsg(err) << \"Cannot send command: Device returned unexpected response '\" << (char)resp << \"'.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n\n\n\n\n"
  },
  {
    "path": "lib/opengd77_interface.hh",
    "content": "#ifndef OPENGD77INTERFACE_HH\n#define OPENGD77INTERFACE_HH\n\n#include \"usbserial.hh\"\n#include \"errorstack.hh\"\n\n/** Implements the interface to a radio running the Open GD77 firmware.\n *\n * This interface uses a USB serial-port to communicate with the device. To find the corresponding\n * port, the device-specific VID @c 0x1fc9 and PID @c 0x0094 are used. Hence no udev rules are\n * needed to access these devices. The user, however, should be a member of the @c dialout group\n * to get access to the serial interfaces.\n *\n *\n * @section ogd77cmd Command requests\n * The overall command requests structure is\n *\n * @verbinclude opengd77_protocol_command_request.txt\n *\n * where the optional and variable length payload field is determined by the command flag. The\n * request starts with the command prefix 'C' (43h) followed by the command flag. Following\n * command flags are known.\n *\n * <table>\n *  <tr><th>Flag</th><th>Command</th></tr>\n *  <tr><td>00h</td> <td>Show CPS screen.</td></tr>\n *  <tr><td>01h</td> <td>Clear screen.</td></tr>\n *  <tr><td>02h</td> <td>Display text.</td></tr>\n *  <tr><td>03h</td> <td>Render screen.</td></tr>\n *  <tr><td>05h</td> <td>Close CPS screen.</td></tr>\n *  <tr><td>06h</td> <td>Control radio.</td></tr>\n *  <tr><td>07h</td> <td>Start GPS logging.</td></tr>\n *  <tr><td>feh</td> <td>Ping request.</td></tr>\n * </table>\n *\n * @subsection ogd77cmd_cps_screen Show CPS Screen (00h)\n * Reserves the screen for the CPS. The content is not cleared.\n *\n * The command is quiet simple\n * @verbinclude opengd77_protocol_command_show_cps_screen_request.txt\n * as is the response\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77cmd_clear_screen Clear Screen (01h)\n * Once the screen has been reserved, this command clears it.\n *\n * Also this command is quiet simple\n * @verbinclude opengd77_protocol_command_clear_screen_request.txt\n * as is the response\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77cmd_set_text Set text (02h)\n * This command has a variable payload size.\n * @verbinclude opengd77_protocol_command_display_text_request.txt\n * <table>\n *  <tr><th>Field</th><th>Meaning</th></tr>\n *  <tr><td>Column Address</td> <td>Specifies the column index.</td></tr>\n *  <tr><td>Row Address</td> <td>Specifies the row index as multiple of 10h.</td></tr>\n *  <tr><td>Size</td> <td>Text size (?). Actually, always observed 3.</td></tr>\n *  <tr><td>Alignment</td> <td>Specifies text alignment on row. 0=left, 1=center, 2=right(?)</td></tr>\n *  <tr><td>Inverted</td> <td>Inverts text, 0=off, 1=inverted (?).</td></tr>\n *  <tr><td>Payload</td> <td>Variable size, up to 16bytes ASCII text.</td></tr>\n * </table>\n *\n * If there are no errors, the radio responds with\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77cmd_render_screen Render Screen (03h)\n * Randers the transmitted screen. This command is quiet simple again, with no payload.\n * @verbinclude opengd77_protocol_command_render_screen_request.txt\n *\n * as is the response:\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77cmd_ctrl Control Radio (06h)\n * This command request is used to control the radio. The specific action is transmitted as a\n * single payload byte.\n * @verbinclude opengd77_protocol_command_control_request.txt\n * <table>\n *  <tr><th>Action Code</th><th>Meaning</th></tr>\n *  <tr><td>00h</td> <td>Save settings and rebbot.</td></tr>\n *  <tr><td>01h</td> <td>Reboot</td></tr>\n *  <tr><td>02h</td> <td>Save settings and VFOs, no reboot.</td></tr>\n *  <tr><td>03h</td> <td>Flash LED green.</td></tr>\n *  <tr><td>04h</td> <td>Flash LED red.</td></tr>\n *  <tr><td>05h</td> <td>Re-init internal buffers.</td></tr>\n *  <tr><td>06h</td> <td>Re-init sound buffers.</td></tr>\n *  <tr><td>07h</td> <td>Update date-time from GPS.</td></tr>\n * </table>\n *\n * If there are no errors, the radio responds with\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77cmd_gps Start GPS Logging (07h)\n * A simple request without any payload.\n * @verbinclude opengd77_protocol_command_start_gps_request.txt\n *\n * If there is no error, the response should be.\n * @verbinclude opengd77_protocol_command_okay_response.txt\n * After that, the radio will send GPS information via the serial port until any other command\n * is send to the radio.\n *\n * @subsection ogd77cmd_ping Ping Request (06h)\n * A simple request without any payload\n * @verbinclude opengd77_protocol_command_ping_request.txt\n * and the radio should respond with\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n *\n * @section ogd77read Read requests\n * The read command is used to obtain different stuff. Not only the code plug. In general, all read\n * share the same form\n * @verbinclude opengd77_protocol_read_request.txt\n *\n * <table>\n *  <tr><th>Code</th> <th>Memory region</th></tr>\n *  <tr><td>01h</td>  <td>Flash</td></tr>\n *  <tr><td>02h</td>  <td>EEPROM</td></tr>\n *  <tr><td>05h</td>  <td>MCU ROM</td></tr>\n *  <tr><td>06h</td>  <td>Display buffer</td></tr>\n *  <tr><td>07h</td>  <td>WAV buffer</td></tr>\n *  <tr><td>08h</td>  <td>AMBE buffer</td></tr>\n *  <tr><td>09h</td>  <td>Radio info</td></tr>\n *  <tr><td>0ah</td>  <td>FLASH security registers</td></tr>\n * </table>\n *\n * Whenever the read request returns some data, it is transmitted with the read response\n * @verbinclude opengd77_protocol_read_response.txt\n *\n * If not, a simple ACK response is send\n * @verbinclude opengd77_protocol_command_okay_response.txt\n *\n * @subsection ogd77info Radio info struct\n * When reading the radio information, the information is returned in a binary struct:\n * @verbinclude opengd77_radio_info.txt\n * <table>\n *  <tr><th>Code</th> <th>Radio Variant</th></tr>\n *  <tr><td>00h</td>  <td>Radioddity GD-77</td></tr>\n *  <tr><td>01h</td>  <td>Radioddity GD-77S</td></tr>\n *  <tr><td>02h</td>  <td>Baofeng DM-1801</td></tr>\n *  <tr><td>03h</td>  <td>Radioddity RD-5R</td></tr>\n *  <tr><td>04h</td>  <td>Baofeng DM-1801A</td></tr>\n *  <tr><td>05h</td>  <td>TyT MD-9600</td></tr>\n *  <tr><td>06h</td>  <td>TyT MD-UV390</td></tr>\n *  <tr><td>07h</td>  <td>TyT MD-380</td></tr>\n *  <tr><td>08h</td>  <td>Baofeng DM-1701</td></tr>\n *  <tr><td>09h</td>  <td>TyT MD-2017</td></tr>\n *  <tr><td>0ah</td>  <td>Baofeng DM-1701 RGB</td></tr>\n * </table>\n *\n * @section ogd77write Write requests\n *\n * @ingroup ogd77 */\nclass OpenGD77Interface : public USBSerial\n{\n  Q_OBJECT\n\npublic:\n  /** The EEPROM memory bank. */\n  static const uint32_t EEPROM = 0;\n  /** The Flash memory bank. */\n  static const uint32_t FLASH  = 1;\n\n  /** Specifies the detected model variant. */\n  enum class Variant {\n    GD77, UV380\n  };\n\npublic:\n  /** Constructs a new interface to a specific OpenGD77 device.  */\n  explicit OpenGD77Interface(const USBDeviceDescriptor &descr,\n                             const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~OpenGD77Interface();\n\n  /** Closes the interface to the device. */\n  void close();\n\n  /** Returns an identifier of the radio. */\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\n  /** Returns @c true if the device allows for storing part of the call-sign DB inside the voice\n   * prompt memory. This property is valid after identifying the device, i.e., a call to\n   * @c identifier. */\n  bool extendedCallsignDB() const;\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\n  bool setDateTime(const QDateTime &datetime, const ErrorStack &err);\n\n  bool saveSettingsNotVFOs(const ErrorStack &err=ErrorStack());\n  bool saveSettingsAndVFOs(const ErrorStack &err=ErrorStack());\n  bool reboot(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Represents a read message. */\n  struct __attribute__((packed)) ReadRequest {\n    /** Possible read sources. */\n    enum Command {\n      READ_FLASH = 1,\n      READ_EEPROM = 2,\n      READ_MCU_ROM = 5,\n      READ_DISPLAY_BUFFER = 6,\n      READ_WAV_BUFFER = 7,\n      READ_AMBE_BUFFER = 8,\n      READ_FIRMWARE_INFO = 9\n    };\n\n    /// 'R' read block, 'W' write block, 'C' command.\n    char type;\n    /// Memory to read from, FLASH, EEPROM, ROM, etc. @see OpenGD77Internface::ReadReqest::Command.\n    uint8_t command;\n    /// Memory address to read from in big endian.\n    uint32_t address;\n    /// Amount of data to read in big endian.\n    uint16_t length;\n\n    /** Constructs a FLASH read message. */\n    bool initReadFlash(uint32_t address, uint16_t length);\n    /** Constructs an EEPROM read message. */\n    bool initReadEEPROM(uint32_t address, uint16_t length);\n    /** Constructs a firmware-info read message. */\n    bool initReadFirmwareInfo();\n  };\n\n  /** Radio information struct. */\n  struct __attribute__((packed)) FirmwareInfo {\n    /** Possible radio types, returned by the radio_info struct.*/\n    enum class RadioType {\n      GD77=0, GD77S=1, DM1801=2, RD5R=3, DM1801A=4, MD9600=5,\n      MDUV380=6, MD380=7, DM1701=8, MD2017=9, DM1701RGB=10\n    };\n\n    uint32_t structVersion;   ///< Struct version number (currently 3).\n    uint32_t radioType;       ///< Device variant (see @c RadioType).\n    char fw_revision[16];     ///< Firmware revision ASCII, 0-padded.\n    char build_date[16];      ///< Firmware build time, YYYYMMDDhhmmss, 0-padded.\n    uint32_t flashChipSerial; ///< Serial number of the flash chip.\n    uint16_t features;        ///< Some flags, signaling the presence of some features.\n\n    /** Returns @c true if the devices display is inverted. */\n    bool featureInvertedDisplay() const;\n    /** Returns @c true if the vocie prompt memory is used for storing callsign db. */\n    bool featureExtendedCallsignDB() const;\n    /** Returns @c true if the voice prompt data is loaded. */\n    bool featureVoicePromptLoaded() const;\n  };\n\n  /** Represents a read response message. */\n  struct __attribute__((packed)) ReadResponse {\n    /// Same code as request. That is 'R' read block, 'W' write block, 'C' command.\n    char type;\n    /// Length of paylod.\n    uint16_t length;\n    /// Payload\n    union {\n      uint8_t data[32];    ///< Data payload.\n      FirmwareInfo info;   ///< Firmware information struct.\n    };\n  };\n\n  /** Represents a write message. */\n  struct __attribute__((packed)) WriteRequest {\n    /** Possible write destinations. */\n    enum Command {\n      SET_FLASH_SECTOR = 1,\n      WRITE_SECTOR_BUFFER = 2,\n      WRITE_FLASH_SECTOR = 3,\n      WRITE_EEPROM = 4,\n      WRITE_WAV_BUFFER = 7\n    };\n\n    /// 'R' read block, 'W'/'X' write block or 'C' command.\n    char type;\n    /// Command, @see OpenGD77Internface::WriteReqest::Command.\n    uint8_t command;\n\n    union {\n      /** 24 bit sector number. */\n      uint8_t sector[3];\n      /** Payload data. */\n      struct __attribute__((packed)) {\n        /** Target address. */\n        uint32_t address;\n        /** Payload length. */\n        uint16_t length;\n        /** Payload data. */\n        uint8_t data[32];\n      } payload;\n    };\n\n    /** Constructs a write-to-eeprom message. */\n    bool initWriteEEPROM(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size);\n    /** Constructs a set-flash-sector message. */\n    bool initSetFlashSector(Variant variant, uint32_t addr);\n    /** Constructs a write-to-flash message. */\n    bool initWriteFlash(Variant variant, uint32_t addr, const uint8_t *data, uint16_t size);\n    /** Constructs a finish-write-to-flash message. */\n    bool initFinishWriteFlash(Variant variant);\n  };\n\n  /** Represents a write-response message. */\n  struct __attribute__((packed)) WriteResponse {\n    /// Same code as request. That is 'R' read block, 'W' write block, 'C' command or '-' on Error.\n    char type;\n    /// Same code as request if OK.\n    uint8_t command;\n  };\n\n  /** Represents a command message. */\n  struct __attribute__((packed)) CommandRequest {\n    /** Possible commands. */\n    enum Command {\n      SHOW_CPS_SCREEN  = 0,\n      CLEAR_SCREEN     = 1,\n      DISPLAY          = 2,\n      RENDER_CPS       = 3,\n      CLOSE_CPS_SCREEN = 5,\n      COMMAND          = 6\n    };\n\n    /** Possible options. */\n    enum Option {\n      SAVE_SETTINGS_NOT_VFOS = 0,\n      REBOOT = 1,\n      SAVE_SETTINGS_AND_VFOS = 2,\n      FLASH_GREEN_LED = 3,\n      FLASH_RED_LED = 4,\n      INIT_CODEC = 5,\n      INIT_SOUND = 6,\n      SET_DATETIME = 7,\n      DELAY_10ms = 10\n    };\n\n    /** Message type, here 'C' for command. */\n    char type;\n    /** The command. */\n    uint8_t command;\n    /** Either a command option or the x position on screen. */\n    union {\n      /** The x-position on the screen. */\n      uint8_t x;\n      /** The command option. */\n      uint8_t option;\n    };\n    // Either some text options or a timestamp.\n    union {\n      struct __attribute__((packed)) {\n        /** The y-position on the screen. */\n        uint8_t y;\n        /** The font size. */\n        uint8_t font;\n        /** The text alignment. */\n        uint8_t alignment;\n        /** Is text inverted? */\n        uint8_t inverted;\n      };\n      uint32_t timestamp;\n    };\n\n    /** Some text message. */\n    char message[16];\n\n    /** Construct \"show CPS screen\" command message. */\n    void initShowCPSScreen();\n    /** Construct a clear-screen command message. */\n    void initClearScreen();\n    /** Construct a \"show text on screen\" message. */\n    void initDisplay(uint8_t x, uint8_t y, const char *message, unsigned int iSize,\n                     uint8_t font, uint8_t alignment, uint8_t inverted);\n    /** Construct a \"render CPS\" message. */\n    void initRenderCPS();\n    /** Construct a \"close screen\" command message. */\n    void initCloseScreen();\n    /** Construct a command message with the given option. */\n    void initCommand(Option option);\n    /** Construct a SET_DATETIME message with the given date time. */\n    void initSetDateTime(const QDateTime &dt);\n  };\n\nprotected:\n  /** Read some data from EEPROM at the given address. */\n  bool readEEPROM(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack());\n  /** Write some data to EEPROM at the given address. */\n  bool writeEEPROM(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack());\n\n  /** Read some data from Flash at the given address. */\n  bool readFlash(uint32_t addr, uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack());\n  /** Select the correct Flash sector for the given address.\n   * This command must be sent before writing to the flash memory. */\n  bool setFlashSector(uint32_t addr, const ErrorStack &err=ErrorStack());\n  /** Write some data to the given Flash memory. */\n  bool writeFlash(uint32_t addr, const uint8_t *data, uint16_t len, const ErrorStack &err=ErrorStack());\n  /** Finalize writing to the Flash memory. If not send after writing to a sector,\n   * the changes are lost. */\n  bool finishWriteFlash(const ErrorStack &err=ErrorStack());\n\n  /** Read radio info struct. */\n  bool readFirmwareInfo(FirmwareInfo &radioInfo, const ErrorStack &err=ErrorStack());\n\n  /** Send a \"show CPS screen\" message. */\n  bool sendShowCPSScreen(const ErrorStack &err=ErrorStack());\n  /** Send a \"clear screen\" message. */\n  bool sendClearScreen(const ErrorStack &err=ErrorStack());\n  /** Send a \"display some text\" message. */\n  bool sendDisplay(uint8_t x, uint8_t y, const char *message, uint8_t iSize, uint8_t alignment, uint8_t inverted, const ErrorStack &err=ErrorStack());\n  /** Send a \"render CPS screen\" message. */\n  bool sendRenderCPS(const ErrorStack &err=ErrorStack());\n  /** Send a \"close screen\" message. */\n  bool sendCloseScreen(const ErrorStack &err=ErrorStack());\n  /** Send a \"set date time\" message. */\n  bool sendSetDateTime(const QDateTime &dt, const ErrorStack &err=ErrorStack());\n  /** Sends some command message with the given options. */\n  bool sendCommand(CommandRequest::Option option, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** The protocol variant determined by the device type obtained by the firmware info. */\n  Variant _protocolVariant;\n  /** If @c true, the device allows for storing parts of the call-sign DB inside the voice prompt\n   *  memory. */\n  bool _extendedCallsignDB;\n  /** The current Flash sector, set to -1 if none is currently selected. */\n  int32_t _sector;\n};\n\n#endif // OPENGD77INTERFACE_HH\n"
  },
  {
    "path": "lib/opengd77_limits.cc",
    "content": "#include \"opengd77_limits.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"scanlist.hh\"\n#include \"roamingzone.hh\"\n#include \"opengd77_satelliteconfig.hh\"\n\n\n\nOpenGD77Limits::OpenGD77Limits(QObject *parent)\n  : RadioLimits(false, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 15796;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = true;\n  _satelliteConfigImplemented  = true;\n  _numSatellites               = OpenGD77SatelliteConfig::Limit::satellites();\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitIgnored(RadioLimitIssue::Silent) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n        /// @todo check default radio ID.\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 1024, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                (unsigned)DMRContact::GroupCall,\n                (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 32, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"number\", new RadioLimitStringRegEx(\"^[0-9A-Fa-f]+$\") }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 0, 76, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n              // allow for any digital contact reference\n              { \"contacts\", new RadioLimitRefList(1, 32, DMRContact::staticMetaObject) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 1024, // < up to 1024 channels\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              /// @todo handle OpenGD77 extension\n              {\"openGD77\", new RadioLimitIgnored()},\n              {\"tyt\", new RadioLimitIgnored()}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject)},\n              {\"aprs\", new RadioLimitObjRef(FMAPRSSystem::staticMetaObject)},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              /// @todo handle OpenGD77 extension\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 0, 68, new RadioLimitSingleZone(\n          80, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Ignore scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n\n  /* Ignore positioning systems. */\n  add(\"positioning\", new RadioLimitList(\n        ConfigObject::staticMetaObject, 0, 8,\n        new RadioLimitObjects {\n          { FMAPRSSystem::staticMetaObject, new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          { DMRAPRSSystem::staticMetaObject, new RadioLimitIgnored(RadioLimitIssue::Hint) }\n        }) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored()) );\n}\n"
  },
  {
    "path": "lib/opengd77_limits.hh",
    "content": "#ifndef OPENGD77LIMITS_HH\n#define OPENGD77LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the limits for the OpenGD77 firmware.\n * @ingroup @ogd77 */\nclass OpenGD77Limits: public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit OpenGD77Limits(QObject *parent=nullptr);\n};\n\n#endif // OPENGD77LIMITS_HH\n"
  },
  {
    "path": "lib/opengd77_satelliteconfig.cc",
    "content": "#include \"opengd77_satelliteconfig.hh\"\n#include \"errorstack.hh\"\n\n\nOpenGD77SatelliteConfig::OpenGD77SatelliteConfig(QObject *parent)\n  : OpenGD77BaseSatelliteConfig(parent)\n{\n  image(FLASH).addElement(Offset::satellites(), size()); // of 0x11a0 bytes\n}\n\n\nbool\nOpenGD77SatelliteConfig::isValid() const {\n  return OpenGD77BaseCodeplug::AdditionalSettingsElement((uint8_t *)data(Offset::satellites(), FLASH))\n      .isValid();\n}\n\n\nvoid\nOpenGD77SatelliteConfig::initialize() {\n  OpenGD77BaseCodeplug::AdditionalSettingsElement(data(Offset::satellites(), FLASH)).clear();\n}\n\n\nbool\nOpenGD77SatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) {\n  OpenGD77BaseCodeplug::AdditionalSettingsElement settings(data(Offset::satellites(), FLASH));\n  OpenGD77BaseCodeplug::SatelliteBankElement bank = settings.satellites();\n  bank.clear();\n\n  if (! bank.encode(db, err)) {\n    errMsg(err) << \"Cannot encode satellite config for OpenUV380.\";\n    return false;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/opengd77_satelliteconfig.hh",
    "content": "#ifndef OPENGD77_SATELLITECONFIG_HH\n#define OPENGD77_SATELLITECONFIG_HH\n\n#include \"opengd77base_codeplug.hh\"\n#include \"opengd77base_satelliteconfig.hh\"\n\n\nclass OpenGD77SatelliteConfig : public OpenGD77BaseSatelliteConfig\n{\n  Q_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit OpenGD77SatelliteConfig(QObject *parent = nullptr);\n\n  bool isValid() const;\n  void initialize();\n\n  /** Encodes the given satellite database. */\n  virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits for the satellite config. */\n  struct Limit {\n    /** The maximum number of satellites. */\n    static constexpr unsigned int satellites() {\n      return OpenGD77BaseCodeplug::SatelliteBankElement::Limit::satellites();\n    }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int satellites()        { return 0x000000; }\n    /// @endcond\n  };\n};\n\n#endif // OPENGD77_SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/opengd77base.cc",
    "content": "#include \"opengd77base.hh\"\n#include \"opengd77base_codeplug.hh\"\n#include \"opengd77_limits.hh\"\n#include \"logger.hh\"\n#include \"config.hh\"\n\n\n#define BSIZE 32\n#define SLOWDOWN 100 // us\n\n\nRadioLimits *OpenGD77Base::_limits = nullptr;\n\nOpenGD77Base::OpenGD77Base(OpenGD77Interface *device, QObject *parent)\n  : Radio(parent), _dev(device), _flags(), _config(nullptr), _satelliteConfig(nullptr)\n{\n  // pass...\n}\n\nOpenGD77Base::~OpenGD77Base() {\n  if (_dev && _dev->isOpen())  {\n    logDebug() << \"Closing device.\";\n    _dev->reboot();\n    _dev->close();\n  }\n  if (_dev) {\n    logDebug() << \"Deleting device.\";\n    _dev->deleteLater();\n    _dev = nullptr;\n  }\n}\n\n\nconst RadioLimits &\nOpenGD77Base::limits() const {\n  if (nullptr == _limits)\n    _limits = new OpenGD77Limits();\n  return *_limits;\n}\n\n\nbool\nOpenGD77Base::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task) {\n    logError() << \"Cannot download from radio, radio is not idle.\";\n    return false;\n  }\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for download\n  start();\n  return true;\n}\n\n\nbool\nOpenGD77Base::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags)\n\n  logDebug() << \"Start upload to \" << name() << \"...\";\n\n  if (StatusIdle != _task) {\n    logError() << \"Cannot upload to radio, radio is not idle.\";\n    return false;\n  }\n\n  if (_config)\n    delete _config;\n\n  if (! (_config = config)) {\n    logError() << \"Cannot upload to radio, no config given.\";\n    return false;\n  }\n  _config->setParent(this);\n  _flags = flags;\n\n  _task = StatusUpload;\n  _errorStack = err;\n\n  if (_flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for upload\n  start();\n\n  return true;\n}\n\nbool\nOpenGD77Base::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  logDebug() << \"Start upload to \" << name() << \"...\";\n\n  if (StatusIdle != _task) {\n    logError() << \"Cannot upload to radio, radio is not idle.\";\n    return false;\n  }\n\n  // Assemble call-sign db from user DB\n  logDebug() << \"Encode call-signs into db.\";\n  callsignDB()->encode(db, selection);\n\n  _task = StatusUploadCallsigns;\n  _flags = selection;\n  _errorStack = err;\n  if (_flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for upload\n  start();\n\n  return true;\n}\n\n\nbool\nOpenGD77Base::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  logDebug() << \"Start upload to \" << name() << \"...\";\n\n  if (StatusIdle != _task) {\n    logError() << \"Cannot upload to radio, radio is not idle.\";\n    return false;\n  }\n\n  _satelliteDatabase = db;\n\n  _task = StatusUploadSatellites;\n  _flags = flags;\n  _errorStack = err;\n  if (_flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for upload\n  start();\n\n  return true;\n}\n\n\nvoid\nOpenGD77Base::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    if (! download()) {\n      _task = StatusError;\n      _dev->read_finish();\n      _dev->reboot();\n      _dev->close();\n      emit downloadError(this);\n      return;\n    }\n\n    _dev->read_finish();\n    _dev->close();\n    _task = StatusIdle;\n    emit downloadFinished(this, &codeplug());\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! upload()) {\n      _task = StatusError;\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->write_finish();\n    _dev->saveSettingsNotVFOs();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! uploadCallsigns()) {\n      _task = StatusError;\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->saveSettingsAndVFOs();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadSatellites == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! uploadSatellites()) {\n      _task = StatusError;\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->write_finish();\n    _dev->saveSettingsNotVFOs();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  }\n}\n\n\nbool\nOpenGD77Base::download()\n{\n  emit downloadStarted();\n\n  if (codeplug().numImages() != 2) {\n    errMsg(_errorStack) << \"Cannot download codeplug: Codeplug does not contain two images.\";\n    return false;\n  }\n\n  // Check every segment in the codeplug\n  if (! codeplug().isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot download codeplug: Codeplug is not aligned with blocksize \" << BSIZE << \".\";\n    return false;\n  }\n\n  size_t totb = codeplug().memSize();\n\n  if (! _dev->read_start(0, 0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start codeplug download.\";\n    _dev->close();\n    return false;\n  }\n\n  // Then download codeplug\n  size_t bcount = 0;\n  for (int image=0; image<codeplug().numImages(); image++) {\n    uint32_t bank = (0 == image) ? OpenGD77BaseCodeplug::EEPROM : OpenGD77BaseCodeplug::FLASH;\n\n    for (int n=0; n<codeplug().image(image).numElements(); n++) {\n      unsigned addr = codeplug().image(image).element(n).address();\n      unsigned size = codeplug().image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      logDebug() << \"Read \" << size << \"h bytes from bank \" << bank\n                 << \" @ addr \" << Qt::hex << addr << \"h.\";\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot read block \" << (b0+b) << \".\";\n          return false;\n        }\n        QThread::usleep(SLOWDOWN);\n        emit downloadProgress(float(bcount*100)/totb);\n      }\n    }\n    // The is needed to prevent a bug in the firmware that causes the FW to crash during read.\n    if (! _dev->read_finish(_errorStack))\n      return false;\n  }\n\n\n  return true;\n}\n\n\nbool\nOpenGD77Base::upload()\n{\n  emit uploadStarted();\n\n  if (codeplug().numImages() != 2) {\n    errMsg(_errorStack) << \"Cannot download codeplug: Codeplug does not contain two images.\";\n    return false;\n  }\n\n  // Check every segment in the codeplug\n  if (! codeplug().isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot upload code-plug: Codeplug is not aligned with blocksize \" << BSIZE << \".\";\n    return false;\n  }\n\n  size_t totb = codeplug().memSize();\n\n  if (_flags.updateDeviceClock() &&\n      (! _dev->setDateTime(QDateTime::currentDateTimeUtc(), _errorStack))) {\n    errMsg(_errorStack) << \"Cannot set device clock.\";\n    return false;\n  }\n\n  if (! _dev->read_start(0, 0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start codeplug download.\";\n    return false;\n  }\n\n  // Then download codeplug\n  size_t bcount = 0;\n  for (int image=0; image<codeplug().numImages(); image++) {\n    uint32_t bank = ( (0 == image) ? OpenGD77BaseCodeplug::EEPROM : OpenGD77BaseCodeplug::FLASH );\n\n    for (int n=0; n<codeplug().image(image).numElements(); n++) {\n      unsigned addr = codeplug().image(image).element(n).address();\n      unsigned size = codeplug().image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      logDebug() << \"Read \" << size << \"h bytes from bank \" << bank\n                 << \" @ addr \" << Qt::hex << addr << \"h.\";\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot read block \" << Qt::hex << (b0+b) << \"h.\";\n          return false;\n        }\n        QThread::usleep(SLOWDOWN);\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n    }\n    // The is needed to prevent a bug in the firmware that causes the FW to crash during read.\n    if (! _dev->read_finish(_errorStack))\n      return false;\n  }\n\n  // Encode config into codeplug\n  codeplug().encode(_config);\n\n  if (! _dev->write_start(0,0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start codeplug upload.\";\n    return false;\n  }\n\n  // Then upload codeplug\n  for (int image=0; image<codeplug().numImages(); image++) {\n    uint32_t bank = (0 == image) ? OpenGD77BaseCodeplug::EEPROM : OpenGD77BaseCodeplug::FLASH;\n\n    for (int n=0; n<codeplug().image(image).numElements(); n++) {\n      unsigned addr = codeplug().image(image).element(n).address();\n      unsigned size = codeplug().image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      logDebug() << \"Write \" << size << \"h bytes from bank \" << bank\n                 << \" @ addr \" << Qt::hex << addr << \"h.\";\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->write(bank, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE, image), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot write block \" << (b0+b) << \".\";\n          return false;\n        }\n        QThread::usleep(SLOWDOWN);\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n    }\n    _dev->write_finish();\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77Base::uploadCallsigns()\n{\n  emit uploadStarted();\n\n  // Check every segment in the codeplug\n  if (! callsignDB()->isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot upload call-sign DB: Not aligned with block-size \" << BSIZE << \"!\";\n    return false;\n  }\n\n  size_t totb = callsignDB()->memSize();\n\n  if (_flags.updateDeviceClock() &&\n      (! _dev->setDateTime(QDateTime::currentDateTimeUtc(), _errorStack))) {\n    errMsg(_errorStack) << \"Cannot set device clock.\";\n    return false;\n  }\n\n  if (! _dev->write_start(OpenGD77BaseCodeplug::FLASH, 0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start callsign DB upload.\";\n    return false;\n  }\n\n  unsigned bcount = 0;\n  // Then upload callsign DB\n  for (int n=0; n<callsignDB()->image(0).numElements(); n++) {\n    unsigned addr = callsignDB()->image(0).element(n).address();\n    unsigned size = callsignDB()->image(0).element(n).data().size();\n    unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n    for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n      if (! _dev->write(OpenGD77BaseCodeplug::FLASH, (b0+b)*BSIZE,\n                        callsignDB()->data((b0+b)*BSIZE, 0), BSIZE, _errorStack))\n      {\n        errMsg(_errorStack) << \"Cannot write block \" << (b0+b) << \".\";\n        return false;\n      }\n      emit uploadProgress(float(bcount*100)/totb);\n    }\n  }\n\n  _dev->write_finish();\n  return true;\n}\n\n\nbool\nOpenGD77Base::uploadSatellites()\n{\n  if (! _satelliteDatabase) {\n    errMsg(_errorStack) << \"Cannot write satellite config. No config present.\";\n    return false;\n  }\n\n  emit uploadStarted();\n\n  // Check every segment in the codeplug\n  if (! _satelliteConfig->isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot upload satellite config: Not aligned with block-size \" << BSIZE << \"!\";\n    return false;\n  }\n\n  size_t totb = _satelliteConfig->memSize();\n  if (! _dev->read_start(OpenGD77BaseSatelliteConfig::FLASH, 0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start satellite config download.\";\n    return false;\n  }\n\n  // Then download satellite config\n  size_t bcount = 0;\n  for (int n=0; n<_satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).numElements(); n++) {\n    unsigned addr = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).address();\n      unsigned size = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(OpenGD77BaseSatelliteConfig::FLASH, (b0+b)*BSIZE, _satelliteConfig->data((b0+b)*BSIZE, OpenGD77BaseSatelliteConfig::FLASH), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot read block \" << (b0+b) << \".\";\n          return false;\n        }\n        QThread::usleep(SLOWDOWN);\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n  }\n  logDebug() << \"Read \" << Qt::hex << bcount << \"b of additional settings from device.\";\n  _dev->read_finish();\n\n  if (_flags.updateDeviceClock() &&\n      (! _dev->setDateTime(QDateTime::currentDateTimeUtc(), _errorStack))) {\n    errMsg(_errorStack) << \"Cannot set device clock.\";\n    return false;\n  }\n\n  // Encode config into codeplug\n  if (! _satelliteConfig->encode(_satelliteDatabase, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot encode satellite config.\";\n    return false;\n  }\n\n  if (! _dev->write_start(OpenGD77BaseSatelliteConfig::FLASH, 0, _errorStack)) {\n    errMsg(_errorStack) << \"Cannot start satellite config upload.\";\n    return false;\n  }\n\n  // Then upload satellite config\n  for (int n=0; n<_satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).numElements(); n++) {\n    unsigned addr = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).address();\n    unsigned size = _satelliteConfig->image(OpenGD77BaseSatelliteConfig::FLASH).element(n).data().size();\n    unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n\n    for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n      if (! _dev->write(OpenGD77BaseSatelliteConfig::FLASH, (b0+b)*BSIZE, _satelliteConfig->data((b0+b)*BSIZE, OpenGD77BaseSatelliteConfig::FLASH), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot write block \" << (b0+b) << \".\";\n        return false;\n      }\n      QThread::usleep(SLOWDOWN);\n      emit uploadProgress(float(bcount*50)/totb);\n    }\n  }\n  _dev->write_finish();\n\n  return true;\n}\n\n\n"
  },
  {
    "path": "lib/opengd77base.hh",
    "content": "/** @defgroup ogd77 Open GD-77 Firmware\n * Implements a radio running the Open GD77 firmware.\n * @ingroup dsc */\n\n#ifndef OPENGD77BASE_HH\n#define OPENGD77BASE_HH\n\n#include <QPointer>\n\n#include \"radio.hh\"\n#include \"opengd77_interface.hh\"\n#include \"opengd77_satelliteconfig.hh\"\n#include \"satellitedatabase.hh\"\n\n\n/** Implements an common USB interface to Open GD-77(S) type devices.\n *\n * @ingroup ogd77 */\nclass OpenGD77Base : public Radio\n{\n\tQ_OBJECT\n\npublic:\n\t/** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit OpenGD77Base(OpenGD77Interface *device=nullptr, QObject *parent=nullptr);\n  virtual ~OpenGD77Base();\n\n  const RadioLimits &limits() const;\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack());\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(), const ErrorStack &err=ErrorStack());\n\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err);\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n\tvoid run();\n\n  /** Implements the actual download process. */\n  bool download();\n  /** Implements the actual codeplug upload process. */\n  bool upload();\n  /** Implements the actual callsign DB upload process. */\n  bool uploadCallsigns();\n  /** Implements the actual satellite config upload process. */\n  bool uploadSatellites();\n\nprotected:\n  /** The interface to the radio. */\n  OpenGD77Interface *_dev;\n  /** Transfer flags. */\n  TransferFlags _flags;\n  /** The generic configuration. */\n\tConfig *_config;\n  /** The satellite configuration. */\n  QPointer<SatelliteDatabase> _satelliteDatabase;\n  OpenGD77BaseSatelliteConfig *_satelliteConfig;\n\nprivate:\n  /** Holds the singleton instance. */\n  static RadioLimits *_limits;\n};\n\n#endif // OPENGD77BASE_HH\n"
  },
  {
    "path": "lib/opengd77base_callsigndb.cc",
    "content": "#include \"opengd77base_callsigndb.hh\"\n#include \"utils.hh\"\n#include \"userdatabase.hh\"\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCallsignDB::DatabaseEntryElement\n * ******************************************************************************************** */\nQVector<QChar>\nOpenGD77BaseCallsignDB::DatabaseEntryElement::_lut =\n{\n  ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',\n  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',\n  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',\n  'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',\n  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',\n  's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '.'\n};\n\nOpenGD77BaseCallsignDB::DatabaseEntryElement::DatabaseEntryElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCallsignDB::DatabaseEntryElement::DatabaseEntryElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseEntryElement::clear() {\n  memset(_data, 0x00, size());\n}\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseEntryElement::setId(unsigned int id) {\n  setUInt24_le(Offset::dmrID(), id);\n}\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseEntryElement::setText(const QString &text) {\n  QByteArray data = pack(text);\n  auto n = std::min(3*Limit::textLength()/4, (unsigned int)data.size());\n  memcpy(_data+Offset::text(), data.constData(), n);\n}\n\nbool\nOpenGD77BaseCallsignDB::DatabaseEntryElement::fromEntry(const UserDatabase::User &user) {\n  clear();\n  setId(user.id);\n\n  QString txt;\n  QTextStream stream(&txt);\n  stream << user.call << \" \" << user.name;\n  if (! user.city.isEmpty())\n    stream << \" \" << user.city;\n  if (! user.state.isEmpty())\n    stream << \" \" << user.state;\n  if (! user.country.isEmpty())\n    stream << \" \" << user.country;\n  setText(txt);\n\n  return true;\n}\n\nQByteArray\nOpenGD77BaseCallsignDB::DatabaseEntryElement::pack(const QString &text) {\n  // Encode chars\n  QVector<uint> codes;\n  foreach (QChar c, text) {\n    if (! _lut.contains(c))\n      continue;\n    codes.append(_lut.indexOf(c));\n  }\n\n  // Fix size to multiples of 4\n  if (codes.size() % 4)\n    codes.resize(codes.size() + (4-(codes.size() % 4)));\n\n  // Pack\n  QByteArray data(3*codes.size()/4, '\\x00');\n  for (int i=0, o=0; i<codes.size(); i += 4, o += 3) {\n    uint32_t encoded\n        = ((uint32_t)(codes[i+0] & 0x3f) << 18) |\n          ((uint32_t)(codes[i+1] & 0x3f) << 12) |\n          ((uint32_t)(codes[i+2] & 0x3f) << 06) |\n          ((uint32_t)(codes[i+3] & 0x3f) << 00);\n    data[o+0] = (encoded>>16) & 0xff;\n    data[o+1] = (encoded>> 8) & 0xff;\n    data[o+2] = (encoded>> 0) & 0xff;\n  }\n\n  return data;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCallsignDB::DatabaseHeaderElement\n * ******************************************************************************************** */\nOpenGD77BaseCallsignDB::DatabaseHeaderElement::DatabaseHeaderElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCallsignDB::DatabaseHeaderElement::DatabaseHeaderElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseHeaderElement::clear() {\n  memset(_data, 0, size());\n  writeASCII(Offset::magic(), \"Id\", 2);\n  setUInt8(Offset::format(), (unsigned int)Format::Compressed);\n  setEntrySize(16); // Default text length = 16\n  writeASCII(Offset::version(),\"001\", 3);\n  setUInt32_le(Offset::entryCount(), 0);\n}\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseHeaderElement::setEntrySize(unsigned int size) {\n  setUInt8(Offset::entrySize(), 0x4a+size);\n}\n\nvoid\nOpenGD77BaseCallsignDB::DatabaseHeaderElement::setEntryCount(unsigned int count) {\n  setUInt32_le(Offset::entryCount(), count);\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCallsignDB\n * ******************************************************************************************** */\nOpenGD77BaseCallsignDB::OpenGD77BaseCallsignDB(QObject *parent)\n  : CallsignDB(parent)\n{\n  // pass...\n}\n\nOpenGD77BaseCallsignDB::~OpenGD77BaseCallsignDB() {\n  // pass...\n}\n"
  },
  {
    "path": "lib/opengd77base_callsigndb.hh",
    "content": "#ifndef OPENGD77BASECALLSIGNDB_HH\n#define OPENGD77BASECALLSIGNDB_HH\n\n#include \"codeplug.hh\"\n#include \"callsigndb.hh\"\n#include \"userdatabase.hh\"\n\n/** Represents and encodes the binary format for all call-sign databases within OpenGD77 radios.\n * @ingroup ogd77 */\nclass OpenGD77BaseCallsignDB : public CallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Represents a user-db entry within the binary codeplug. */\n  struct __attribute__((packed)) userdb_entry_t {\n    uint32_t number;                    ///< DMR ID stored in BCD little-endian.\n    char name[15];                      ///< Call or name, up to 15 ASCII chars, 0x00 padded.\n\n    /** Constructor. */\n    userdb_entry_t();\n    /** Resets the entry. */\n    void clear();\n\n    /** Returns the DMR ID number. */\n    uint32_t getNumber() const;\n    /** Sets the DMR ID number. */\n    void setNumber(uint32_t number);\n\n    /** Returns the name of the entry. */\n    QString getName() const;\n    /** Sets the name of the entry, 15b max.\n     * The name gets truncated if longer than 15b. */\n    void setName(const QString &name);\n\n    /** Encodes the given user. */\n    void fromEntry(const UserDatabase::User &user);\n  };\n\n\n  /** Represents a single encoded database entry.\n   * Consists of a DMR ID and compressed text. The text size is fixed to 16 chars. Each\n   * char is encoded using a 6bit table. Thus 16 chars are stored in 12 bytes. */\n  class DatabaseEntryElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DatabaseEntryElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DatabaseEntryElement(uint8_t *ptr);\n\n    /** The size of the entry. */\n    static constexpr unsigned int size() { return 0x000f; }\n\n    void clear() override;\n\n    /** Encodes the DMR ID. */\n    virtual void setId(unsigned int id);\n    /** Encodes the text. */\n    virtual void setText(const QString &text);\n    /** Encodes the given user. */\n    virtual bool fromEntry(const UserDatabase::User &user);\n\n  protected:\n    /** Encodes and packs the given string. That is, chars are encoded into 6bit codes using _lut\n     *  and 4 encoded chars are then packed into 3bytes. */\n    QByteArray pack(const QString &text);\n\n  public:\n    /** Some limits. */\n    struct Limit: public Element::Limit {\n      // The length of the text.\n      static constexpr unsigned int textLength() { return 16; }\n    };\n\n  protected:\n    /** Internal offsets within entry. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int dmrID() { return 0x0000; }\n      static constexpr unsigned int text()  { return 0x0003; }\n      /// @endcond\n    };\n\n    static QVector<QChar> _lut;\n  };\n\n\n  /** Represents the header of the callsign database. */\n  class DatabaseHeaderElement: public Codeplug::Element\n  {\n  public:\n    /// Possible formats.\n    enum class Format {\n      Uncompressed = 45,\n      Compressed   = 78\n    };\n\n  protected:\n    /** Hidden constructor. */\n    DatabaseHeaderElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DatabaseHeaderElement(uint8_t *ptr);\n\n    /** The size of the header. */\n    static constexpr unsigned int size() { return 0x000c; }\n\n    void clear();\n\n    void setEntrySize(unsigned int size);\n    void setEntryCount(unsigned int count);\n\n  public:\n    /** Some limits for the header. */\n    struct Limit: public Element::Limit {\n      /// None..\n    };\n\n  protected:\n    /** Internal offsets within the header. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int magic()      { return 0x0000; }\n      static constexpr unsigned int format()     { return 0x0002; }\n      static constexpr unsigned int entrySize()  { return 0x0003; }\n      static constexpr unsigned int version()    { return 0x0004; }\n      static constexpr unsigned int entryCount() { return 0x0008; }\n      /// @endcond\n    };\n  };\n\n\npublic:\n  /** Constructor. */\n  explicit OpenGD77BaseCallsignDB(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~OpenGD77BaseCallsignDB();\n\n  /** Encodes as many entries as possible of the given user-database. */\n  virtual bool encode(UserDatabase *calldb, const Flags &selection=Flags(),\n                      const ErrorStack &err=ErrorStack()) = 0;\n\npublic:\n  /** Some limits for this callsign DB. */\n  struct Limit {\n    /** Maximum block size. */\n    static constexpr unsigned int blockSize() { return 32; }\n  };\n};\n\n#endif // OPENGD77BASECALLSIGNDB_HH\n"
  },
  {
    "path": "lib/opengd77base_codeplug.cc",
    "content": "#include \"opengd77base_codeplug.hh\"\n#include \"opengd77_extension.hh\"\n#include \"radioid.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"satellitedatabase.hh\"\n#include <QRegularExpression>\n\n\n/* ********************************************************************************************* *\n * Implementation of some helper functions\n * ********************************************************************************************* */\nuint32_t\nOpenGD77BaseCodeplug::encodeAngle(double angle) {\n  uint32_t sign = (angle < 0) ? 1 : 0;\n  uint32_t decimals = std::abs(int(angle * 10000));\n  uint32_t deg  = decimals/10000; decimals = decimals % 10000;\n  return (sign << 23) | (deg << 15) | decimals;\n}\n\ndouble\nOpenGD77BaseCodeplug::decodeAngle(uint32_t code) {\n  return (((code >> 23) & 1) ? -1 : 1) * (\n        ((code >> 15) & 0xff) + double(code & 0x7fff)/10000\n        );\n}\n\n\nuint16_t\nOpenGD77BaseCodeplug::encodeSelectiveCall(const SelectiveCall &call) {\n  if (call.isInvalid())\n    return 0xffff;\n\n  uint16_t dcs = 0, inverted = 0, toneCode = 0;\n  if (call.isDCS()) {\n    dcs = 1;\n    inverted = call.isInverted() ? 1 : 0;\n    toneCode = call.octalCode();\n  } else {\n    dcs = inverted = 0;\n    toneCode = call.mHz()/100;\n  }\n\n  uint16_t bcd = (((toneCode/1000) % 10 ) << 12) |\n      (((toneCode/100) % 10 ) << 8) |\n      (((toneCode/10) % 10 ) << 4) |\n      (((toneCode/1) % 10 )  << 0);\n  return (dcs<<15) | (inverted << 14) | (bcd & 0x3fff);\n}\n\nSelectiveCall\nOpenGD77BaseCodeplug::decodeSelectiveCall(uint16_t code) {\n  if (0xffff == code)\n    return SelectiveCall();\n\n  bool dcs = ((code >> 15) & 1),\n      inverted = ((code >> 14) & 1);\n\n  uint16_t bcd = (code & 0x3fff);\n  code = 1000 * ((bcd >> 12) & 0xf)\n      + 100 * ((bcd >> 8) & 0xf)\n      + 10 * ((bcd >> 4) & 0xf)\n      + 1 * ((bcd >> 0) & 0xf);\n\n  if (! dcs)\n    return SelectiveCall(double(code)/10);\n\n  return SelectiveCall(code, inverted);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ChannelElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ChannelElement::~ChannelElement() {\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clear() {\n  setName(\"\");\n  setRXFrequency(Frequency());\n  setTXFrequency(Frequency());\n  setMode(MODE_ANALOG);\n  setPower(Channel::Power::High);\n  enableFixedPosition(false);\n  setRXTone(SelectiveCall());\n  setTXTone(SelectiveCall());\n  enableSimplex(false);\n  enablePowerSave(false);\n  enableBeep(false);\n  clearDMRId();\n  clearGroupListIndex();\n  setColorCode(0);\n  clearAPRSIndex();\n  clearTXContact();\n  setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias::None);\n  setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias::None);\n  setTimeSlot(DMRChannel::TimeSlot::TS1);\n  setBandwidth(FMChannel::Bandwidth::Narrow);\n  enableRXOnly(false);\n  enableSkipScan(false);\n  enableSkipZoneScan(false);\n  enableVOX(false);\n  setSquelch(SquelchMode::Global, 0);\n}\n\n\nQString\nOpenGD77BaseCodeplug::ChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setName(const QString &n) {\n  writeASCII(Offset::name(), n, Limit::nameLength(), 0xff);\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getBCD8_le(Offset::rxFrequency()))*10);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setRXFrequency(const Frequency &freq) {\n  setBCD8_le(Offset::rxFrequency(), freq.inHz()/10);\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::ChannelElement::txFrequency() const {\n  return Frequency::fromHz(((unsigned long long)getBCD8_le(Offset::txFrequency()))*10);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setTXFrequency(const Frequency &freq) {\n  setBCD8_le(Offset::txFrequency(), freq.inHz()/10);\n}\n\n\nOpenGD77BaseCodeplug::ChannelElement::Mode\nOpenGD77BaseCodeplug::ChannelElement::mode() const {\n  return (Mode)getUInt8(Offset::mode());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setMode(Mode mode) {\n  setUInt8(Offset::mode(), (unsigned)mode);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::globalPower() const {\n  return 0 == getUInt8(Offset::power());\n}\n\nChannel::Power\nOpenGD77BaseCodeplug::ChannelElement::power() const {\n  switch (getUInt8(Offset::power())) {\n  case 0:\n  case 1: return Channel::Power::Min;\n  case 2:\n  case 3: return Channel::Power::Low;\n  case 4:\n  case 5:\n  case 6: return Channel::Power::Mid;\n  case 7:\n  case 8:\n  case 9: return Channel::Power::High;\n  case 10: return Channel::Power::Max;\n  default:\n  break;\n  }\n\n  return Channel::Power::Min;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setPower(Channel::Power pwr) {\n  switch (pwr) {\n  case Channel::Power::Min:  setUInt8(Offset::power(),  1); break;\n  case Channel::Power::Low:  setUInt8(Offset::power(),  3); break;\n  case Channel::Power::Mid:  setUInt8(Offset::power(),  6); break;\n  case Channel::Power::High: setUInt8(Offset::power(),  9); break;\n  case Channel::Power::Max:  setUInt8(Offset::power(), 10); break;\n  }\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clearPower() {\n  setUInt8(Offset::power(), 0);\n}\n\n\nInterval\nOpenGD77BaseCodeplug::ChannelElement::transmitTimeout() const {\n  if (0 == getUInt8(Offset::txTimeout()))\n    return Interval::infinity();\n  return Interval::fromSeconds((unsigned)getUInt8(Offset::txTimeout())*15);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setTransmitTimeout(const Interval &interval) {\n  if (! interval.isFinite())\n    setUInt8(Offset::txTimeout(), 0);\n  else {\n    unsigned s = interval.seconds()/15;\n    s = std::max(1u, std::min(33u, s));\n    setUInt8(Offset::txTimeout(), s);\n  }\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::fixedPositionEnabled() const {\n  return getBit(Offset::useFixedLocation());\n}\n\nQGeoCoordinate\nOpenGD77BaseCodeplug::ChannelElement::fixedPosition() const {\n  uint32_t latCode = (((uint32_t)getUInt8(Offset::latitude2())) << 16) +\n      (((uint32_t)getUInt8(Offset::latitude1())) << 8) +\n      ((uint32_t)getUInt8(Offset::latitude0()));\n  uint32_t lonCode = (((uint32_t)getUInt8(Offset::longitude2())) << 16) +\n      (((uint32_t)getUInt8(Offset::longitude1())) << 8) +\n      ((uint32_t)getUInt8(Offset::longitude0()));\n\n  return QGeoCoordinate(decodeAngle(latCode), decodeAngle(lonCode));\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setFixedPosition(const QGeoCoordinate &coordinate) {\n  if (! coordinate.isValid()) {\n    enableFixedPosition(false);\n    return;\n  }\n\n  uint32_t latCode = encodeAngle(coordinate.latitude());\n  uint32_t lonCode = encodeAngle(coordinate.longitude());\n\n  setUInt8(Offset::latitude0(), ((latCode >>  0) & 0xff));\n  setUInt8(Offset::latitude1(), ((latCode >>  8) & 0xff));\n  setUInt8(Offset::latitude2(), ((latCode >> 16) & 0xff));\n  setUInt8(Offset::longitude0(), ((lonCode >>  0) & 0xff));\n  setUInt8(Offset::longitude1(), ((lonCode >>  8) & 0xff));\n  setUInt8(Offset::longitude2(), ((lonCode >> 16) & 0xff));\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableFixedPosition(bool enable) {\n  setBit(Offset::useFixedLocation(), enable);\n}\n\n\nSelectiveCall\nOpenGD77BaseCodeplug::ChannelElement::rxTone() const {\n  return decodeSelectiveCall(getUInt16_le(Offset::rxTone()));\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) {\n  setUInt16_le(Offset::rxTone(), encodeSelectiveCall(code));\n}\n\nSelectiveCall\nOpenGD77BaseCodeplug::ChannelElement::txTone() const {\n  return decodeSelectiveCall(getUInt16_le(Offset::txTone()));\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) {\n  setUInt16_le(Offset::txTone(), encodeSelectiveCall(code));\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::isSimplex() const {\n  return getBit(Offset::simplex());\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableSimplex(bool enable) {\n  setBit(Offset::simplex(), enable);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::powerSave() const {\n  return ! getBit(Offset::disablePowerSave());\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enablePowerSave(bool enable) {\n  setBit(Offset::disablePowerSave(), !enable);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::beep() const {\n  return ! getBit(Offset::disableBeep());\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableBeep(bool enable) {\n  setBit(Offset::disableBeep(), !enable);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::hasDMRId() const {\n  return getBit(Offset::overrideDMRID());\n}\n\nunsigned int\nOpenGD77BaseCodeplug::ChannelElement::dmrId() const {\n  return getUInt24_be(Offset::dmrId());\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setDMRId(unsigned int dmrId) {\n  setBit(Offset::overrideDMRID());\n  setUInt24_be(Offset::dmrId(), dmrId);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clearDMRId() {\n  setUInt24_be(Offset::dmrId(), 0x001600);\n  clearBit(Offset::overrideDMRID());\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::hasGroupList() const {\n  return 0 != getUInt8(Offset::groupList());\n}\n\nunsigned\nOpenGD77BaseCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupList())-1;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setGroupListIndex(unsigned index) {\n  setUInt8(Offset::groupList(), index+1);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clearGroupListIndex() {\n  setUInt8(Offset::groupList(), 0);\n}\n\n\nunsigned\nOpenGD77BaseCodeplug::ChannelElement::colorCode() const {\n  return getUInt8(Offset::colorCode());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setColorCode(unsigned cc) {\n  setUInt8(Offset::colorCode(), cc);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::hasAPRSIndex() const {\n  return 0 != getUInt8(Offset::aprsIndex());\n}\n\nunsigned int\nOpenGD77BaseCodeplug::ChannelElement::aprsIndex() const {\n  return getUInt8(Offset::aprsIndex())-1;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setAPRSIndex(unsigned int index) {\n  setUInt8(Offset::aprsIndex(), index+1);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clearAPRSIndex() {\n  setUInt8(Offset::aprsIndex(), 0);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::hasTXContact() const {\n  return 0 != getUInt16_le(Offset::txContact());\n}\n\nunsigned int\nOpenGD77BaseCodeplug::ChannelElement::txContactIndex() const {\n  return getUInt16_le(Offset::txContact()) - 1;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setTXContactIndex(unsigned int index) {\n  setUInt16_le(Offset::txContact(), index+1);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::clearTXContact() {\n  setUInt16_le(Offset::txContact(), 0);\n}\n\n\nOpenGD77ChannelExtension::TalkerAlias\nOpenGD77BaseCodeplug::ChannelElement::aliasTimeSlot1() const {\n  switch ((Alias) getUInt2(Offset::aliasTimeSlot1())) {\n  case Alias::None: return OpenGD77ChannelExtension::TalkerAlias::None;\n  case Alias::APRS: return OpenGD77ChannelExtension::TalkerAlias::APRS;\n  case Alias::Text: return OpenGD77ChannelExtension::TalkerAlias::Text;\n  case Alias::Both: return OpenGD77ChannelExtension::TalkerAlias::Both;\n  }\n\n  return OpenGD77ChannelExtension::TalkerAlias::None;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias alias) {\n  switch (alias) {\n  case OpenGD77ChannelExtension::TalkerAlias::None: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::None); break;\n  case OpenGD77ChannelExtension::TalkerAlias::APRS: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::APRS); break;\n  case OpenGD77ChannelExtension::TalkerAlias::Text: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::Text); break;\n  case OpenGD77ChannelExtension::TalkerAlias::Both: setUInt2(Offset::aliasTimeSlot1(), (unsigned int)Alias::Both); break;\n  }\n}\n\n\nOpenGD77ChannelExtension::TalkerAlias\nOpenGD77BaseCodeplug::ChannelElement::aliasTimeSlot2() const {\n  switch ((Alias) getUInt2(Offset::aliasTimeSlot2())) {\n  case Alias::None: return OpenGD77ChannelExtension::TalkerAlias::None;\n  case Alias::APRS: return OpenGD77ChannelExtension::TalkerAlias::APRS;\n  case Alias::Text: return OpenGD77ChannelExtension::TalkerAlias::Text;\n  case Alias::Both: return OpenGD77ChannelExtension::TalkerAlias::Both;\n  }\n\n  return OpenGD77ChannelExtension::TalkerAlias::None;\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias alias) {\n  switch (alias) {\n  case OpenGD77ChannelExtension::TalkerAlias::None: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::None); break;\n  case OpenGD77ChannelExtension::TalkerAlias::APRS: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::APRS); break;\n  case OpenGD77ChannelExtension::TalkerAlias::Text: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::Text); break;\n  case OpenGD77ChannelExtension::TalkerAlias::Both: setUInt2(Offset::aliasTimeSlot2(), (unsigned int)Alias::Both); break;\n  }\n}\n\n\nDMRChannel::TimeSlot\nOpenGD77BaseCodeplug::ChannelElement::timeSlot() const {\n  return (getBit(Offset::timeSlot()) ? DMRChannel::TimeSlot::TS2 : DMRChannel::TimeSlot::TS1);\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  setBit(Offset::timeSlot(), DMRChannel::TimeSlot::TS2 == ts);\n}\n\n\nFMChannel::Bandwidth\nOpenGD77BaseCodeplug::ChannelElement::bandwidth() const {\n  return (getBit(Offset::bandwidth()) ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow);\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  setBit(Offset::bandwidth(), FMChannel::Bandwidth::Wide == bw);\n}\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::rxOnly() const {\n  return getBit(Offset::rxOnly());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableRXOnly(bool enable) {\n  setBit(Offset::rxOnly(), enable);\n}\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::skipScan() const {\n  return getBit(Offset::skipScan());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableSkipScan(bool enable) {\n  setBit(Offset::skipScan(), enable);\n}\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::skipZoneScan() const {\n  return getBit(Offset::skipZoneScan());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableSkipZoneScan(bool enable) {\n  setBit(Offset::skipZoneScan(), enable);\n}\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::vox() const {\n  return getBit(Offset::vox());\n}\nvoid\nOpenGD77BaseCodeplug::ChannelElement::enableVOX(bool enable) {\n  setBit(Offset::vox(), enable);\n}\n\n\nOpenGD77BaseCodeplug::ChannelElement::SquelchMode\nOpenGD77BaseCodeplug::ChannelElement::squelchMode() const {\n  switch (getUInt8(Offset::squelch())) {\n  case 0: return SquelchMode::Global;\n  case 1: return SquelchMode::Open;\n  case 15: return SquelchMode::Closed;\n  default: return SquelchMode::Normal;\n  }\n}\n\nunsigned int\nOpenGD77BaseCodeplug::ChannelElement::squelchLevel() const {\n  int level = (10*(getUInt8(Offset::squelch())-1))/14;\n  return std::min(level, 10);\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelElement::setSquelch(SquelchMode mode, unsigned int level) {\n  level = std::min(level, 10U);\n  switch (mode) {\n  case SquelchMode::Global: level = 0; break;\n  case SquelchMode::Open:   level = 1; break;\n  case SquelchMode::Closed: level = 15; break;\n  case SquelchMode::Normal: level = 1 + (14*level)/10; break;\n  }\n  setUInt8(Offset::squelch(), level);\n}\n\n\nChannel *\nOpenGD77BaseCodeplug::ChannelElement::decode(Codeplug::Context &ctx, const ErrorStack& err) const {\n  Q_UNUSED(err); Q_UNUSED(ctx);\n  Channel *ch = nullptr;\n  if (MODE_ANALOG == mode()) {\n    FMChannel *ach = new FMChannel(); ch = ach;\n    ach->setBandwidth(bandwidth());\n    ach->setRXTone(rxTone());\n    ach->setTXTone(txTone());\n    ach->setSquelchDefault(); // There is no per-channel squelch setting\n  } else {\n    DMRChannel *dch = new DMRChannel(); ch = dch;\n    dch->setTimeSlot(timeSlot());\n    dch->setColorCode(colorCode());\n  }\n\n  // Apply common settings\n  ch->setName(name());\n  ch->setRXFrequency(rxFrequency());\n\n  if (isSimplex())\n    ch->setTXFrequency(rxFrequency());\n  else\n    ch->setTXFrequency(txFrequency());\n\n  if (globalPower())\n    ch->setDefaultPower();\n  else\n    ch->setPower(power());\n\n  ch->setRXOnly(rxOnly());\n\n  if (vox())\n    ch->setVOXDefault();\n  else\n    ch->disableVOX();\n\n  if (transmitTimeout().isInfinite())\n    ch->disableTimeout();\n  else\n    ch->setTimeout(transmitTimeout());\n\n  ch->setOpenGD77ChannelExtension(new OpenGD77ChannelExtension());\n  ch->openGD77ChannelExtension()->enableScanZoneSkip(skipZoneScan());\n  ch->openGD77ChannelExtension()->enableScanAllSkip(skipScan());\n  ch->openGD77ChannelExtension()->enableBeep(beep());\n  ch->openGD77ChannelExtension()->enablePowerSave(powerSave());\n  ch->openGD77ChannelExtension()->enableLocation(fixedPositionEnabled());\n  ch->openGD77ChannelExtension()->setLocation(fixedPosition());\n  ch->openGD77ChannelExtension()->setTalkerAliasTS1(aliasTimeSlot1());\n  ch->openGD77ChannelExtension()->setTalkerAliasTS2(aliasTimeSlot2());\n\n  // done.\n  return ch;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::link(Channel *c, Context &ctx, const ErrorStack& err) const {\n  Q_UNUSED(err)\n\n  // Link common\n\n  if (c->is<DMRChannel>()) {\n    // Link DMR channel\n    DMRChannel *dc = c->as<DMRChannel>();\n    if (hasGroupList() && ctx.has<RXGroupList>(groupListIndex()))\n      dc->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n    if (hasTXContact() && ctx.has<DMRContact>(txContactIndex()))\n      dc->setContact(ctx.get<DMRContact>(txContactIndex()));\n    // Testing dmrId() == 0 fixes a bug in the OpenGD77 firmware. May change in future.\n    if (hasDMRId() && (0 != dmrId())) {\n      logDebug() << \"Channel '\" << c->name() << \"' overrides default DMR id with \"\n                 << dmrId() << \".\";\n      auto id = ctx.config()->radioIDs()->find(dmrId());\n      if (nullptr == id) {\n        logDebug() << \"DMR Id \" << dmrId() << \" is not defined yet, create one as 'Unknown ID'.\";\n        id = new DMRRadioID(QString(\"Unknown ID\"), dmrId());\n        ctx.config()->radioIDs()->add(id);\n      }\n      dc->setRadioId(id);\n    }\n  } else if (c->is<FMChannel>()) {\n    // Link FM channel\n    auto fm = c->as<FMChannel>();\n    if (hasAPRSIndex()) {\n      if (! ctx.has<FMAPRSSystem>(aprsIndex())) {\n        logWarn() << \"Cannot link APRS system index \" << aprsIndex() << \": Unknown index. (ignored)\";\n      } else {\n        fm->setAPRS(ctx.get<FMAPRSSystem>(aprsIndex()));\n      }\n    }\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ChannelElement::encode(const Channel *c, Context &ctx, const ErrorStack& err) {\n  clear();\n\n  setName(c->name());\n\n  setRXFrequency(c->rxFrequency());\n  setTXFrequency(c->txFrequency());\n  enableSimplex(false);\n\n  clearPower();\n  if (! c->defaultPower())\n    setPower(c->power());\n\n  enableRXOnly(c->rxOnly());\n  setTransmitTimeout(c->timeout());\n\n  // Enable vox\n  bool defaultVOXEnabled = (c->defaultVOX() && ctx.config()->settings()->audio()->voxEnabled());\n  bool channelVOXEnabled = (! (c->voxDisabled()||c->defaultVOX()));\n  enableVOX(defaultVOXEnabled || channelVOXEnabled);\n\n  if (c->is<FMChannel>()) {\n    const FMChannel *ac = c->as<const FMChannel>();\n    setMode(MODE_ANALOG);\n    setBandwidth(ac->bandwidth());\n    setRXTone(ac->rxTone());\n    setTXTone(ac->txTone());\n    // no per channel squelch setting\n    if (ac->aprs() && (0<=ctx.index(ac->aprs())))\n      setAPRSIndex(ctx.index(ac->aprs()));\n  } else if (c->is<DMRChannel>()) {\n    const DMRChannel *dc = c->as<const DMRChannel>();\n    setMode(MODE_DIGITAL);\n    setTimeSlot(dc->timeSlot());\n    setColorCode(dc->colorCode());\n    // OpenGD77 does not allow for both TX contact and group list, select one, prefer group list\n    if (dc->groupList())\n      setGroupListIndex(ctx.index(dc->groupList()));\n    else if (dc->contact())\n      setTXContactIndex(ctx.index(dc->contact()));\n    if (dc->radioId() != ctx.config()->settings()->defaultId())\n      setDMRId(dc->radioId()->number());\n  } else {\n    errMsg(err) << \"Cannot encode channel of type '\" << c->metaObject()->className()\n                << \"': Not supported by the radio.\";\n    return false;\n  }\n\n  if (nullptr == c->openGD77ChannelExtension())\n    return true;\n\n  // apply extension\n  enableSkipZoneScan(c->openGD77ChannelExtension()->scanZoneSkip());\n  enableSkipScan(c->openGD77ChannelExtension()->scanAllSkip());\n  enableBeep(c->openGD77ChannelExtension()->beep());\n  enablePowerSave(c->openGD77ChannelExtension()->powerSave());\n  setFixedPosition(c->openGD77ChannelExtension()->location());\n  enableFixedPosition(c->openGD77ChannelExtension()->locationEnabled());\n\n  setAliasTimeSlot1(c->openGD77ChannelExtension()->talkerAliasTS1());\n  setAliasTimeSlot2(c->openGD77ChannelExtension()->talkerAliasTS2());\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ChannelBankElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ChannelBankElement::~ChannelBankElement() {\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::ChannelBankElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nOpenGD77BaseCodeplug::ChannelBankElement::isEnabled(unsigned idx) const {\n  unsigned byte = Offset::bitmask() + idx/8, bit = idx%8;\n  return getBit(byte, bit);\n}\nvoid\nOpenGD77BaseCodeplug::ChannelBankElement::enable(unsigned idx, bool enabled) {\n  unsigned byte = Offset::bitmask() + idx/8, bit = idx%8;\n  return setBit(byte, bit, enabled);\n}\n\nuint8_t *\nOpenGD77BaseCodeplug::ChannelBankElement::get(unsigned idx) const {\n  return (_data+Offset::channels())+idx*ChannelElement::size();\n}\n\nOpenGD77BaseCodeplug::ChannelElement\nOpenGD77BaseCodeplug::ChannelBankElement::channel(unsigned int n) {\n  return ChannelElement((_data+Offset::channels())+n*ChannelElement::size());\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::VFOChannelElement\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, unsigned size)\n  : ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr)\n  : ChannelElement(ptr)\n{\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::VFOChannelElement::clear() {\n  ChannelElement::clear();\n  setStepSize(12.5);\n  setOffsetMode(OffsetMode::Off);\n  setTXOffset(10.0);\n}\n\nQString\nOpenGD77BaseCodeplug::VFOChannelElement::name() const {\n  return QString();\n}\nvoid\nOpenGD77BaseCodeplug::VFOChannelElement::setName(const QString &name) {\n  Q_UNUSED(name);\n  ChannelElement::setName(\"\");\n}\n\ndouble\nOpenGD77BaseCodeplug::VFOChannelElement::stepSize() const {\n  switch (StepSize(getUInt4(Offset::stepSize()))) {\n  case StepSize::SS2_5kHz: return 2.5;\n  case StepSize::SS5kHz: return 5;\n  case StepSize::SS6_25kHz: return 6.25;\n  case StepSize::SS10kHz: return 10.0;\n  case StepSize::SS12_5kHz: return 12.5;\n  case StepSize::SS20kHz: return 20;\n  case StepSize::SS30kHz: return 30;\n  case StepSize::SS50kHz: return 50;\n  }\n  return 12.5;\n}\n\nvoid\nOpenGD77BaseCodeplug::VFOChannelElement::setStepSize(double kHz) {\n  if (2.5 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS2_5kHz);\n  else if (5.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS5kHz);\n  else if (6.25 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS6_25kHz);\n  else if (10.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS10kHz);\n  else if (12.5 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS12_5kHz);\n  else if (20.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS20kHz);\n  else if (30.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS30kHz);\n  else\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS50kHz);\n}\n\nOpenGD77BaseCodeplug::VFOChannelElement::OffsetMode\nOpenGD77BaseCodeplug::VFOChannelElement::offsetMode() const {\n  return (OffsetMode)getUInt2(Offset::offsetMode());\n}\nvoid\nOpenGD77BaseCodeplug::VFOChannelElement::setOffsetMode(OffsetMode mode) {\n  setUInt2(Offset::offsetMode(), (unsigned)mode);\n}\ndouble\nOpenGD77BaseCodeplug::VFOChannelElement::txOffset() const {\n  return ((double)getBCD4_le(Offset::txOffset()))/100;\n}\nvoid\nOpenGD77BaseCodeplug::VFOChannelElement::setTXOffset(double f) {\n  setBCD4_le(Offset::txOffset(), (f*100));\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::clear() {\n  setCall(\"\");\n  setRadioId(0);\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::GeneralSettingsElement::uhfMinFrequency() const {\n  return Frequency::fromMHz(getBCD4_le(Offset::uhfMinFrequency()));\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setUHFMinFrequency(const Frequency &f) {\n  setBCD4_le(Offset::uhfMinFrequency(), f.inMHz());\n}\n\nFrequency\nOpenGD77BaseCodeplug::GeneralSettingsElement::uhfMaxFrequency() const {\n  return Frequency::fromMHz(getBCD4_le(Offset::uhfMaxFrequency()));\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setUHFMaxFrequency(const Frequency &f) {\n  setBCD4_le(Offset::uhfMaxFrequency(), f.inMHz());\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::GeneralSettingsElement::vhfMinFrequency() const {\n  return Frequency::fromMHz(getBCD4_le(Offset::vhfMinFrequency()));\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setVHFMinFrequency(const Frequency &f) {\n  setBCD4_le(Offset::vhfMinFrequency(), f.inMHz());\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::GeneralSettingsElement::vhfMaxFrequency() const {\n  return Frequency::fromMHz(getBCD4_le(Offset::vhfMaxFrequency()));\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setVHFMaxFrequency(const Frequency &f) {\n  setBCD4_le(Offset::vhfMaxFrequency(), f.inMHz());\n}\n\n\nQString\nOpenGD77BaseCodeplug::GeneralSettingsElement::call() const {\n  return readASCII(Offset::call(), Limit::callLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setCall(const QString &call) {\n  writeASCII(Offset::call(), call, Limit::callLength(), 0xff);\n}\n\n\nunsigned int\nOpenGD77BaseCodeplug::GeneralSettingsElement::radioId() const {\n  return getBCD8_be(Offset::dmrId());\n}\n\nvoid\nOpenGD77BaseCodeplug::GeneralSettingsElement::setRadioId(unsigned int id) {\n  setBCD8_be(Offset::dmrId(), id);\n}\n\n\nbool\nOpenGD77BaseCodeplug::GeneralSettingsElement::encode(const Context &ctx, const ErrorStack &err) {\n  DMRRadioID *id = ctx.config()->settings()->defaultId();\n  if (nullptr == id) {\n    errMsg(err) << \"Cannot encode DMR ID. No default ID defined.\";\n    return false;\n  }\n\n  setCall(id->name());\n  setRadioId(id->number());\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::GeneralSettingsElement::decode(const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  DMRRadioID *id = new DMRRadioID(call(), radioId());\n  ctx.config()->radioIDs()->add(id);\n  ctx.config()->settings()->setDefaultId(id);\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::APRSSettingsElement\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::APRSSettingsElement::APRSSettingsElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::clear() {\n  Element::clear();\n\n  setName(\"\");\n  enableFixedPosition(false);\n  setUInt32_le(Offset::fmFrequency(), 0);\n\n  // Some random data, appears to be important\n  writeASCII(Offset::unknownBytes(), \"RA\", 2);\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::isValid() const {\n  return ! name().isEmpty();\n}\n\n\nQString\nOpenGD77BaseCodeplug::APRSSettingsElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nunsigned int\nOpenGD77BaseCodeplug::APRSSettingsElement::sourceSSID() const {\n  return getUInt8(Offset::sourceSSID());\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setSourceSSID(unsigned int ssid) {\n  setUInt8(Offset::sourceSSID(), ssid);\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::fixedPositionEnabled() const {\n  return getBit(Offset::useFixedPosition());\n}\n\nQGeoCoordinate\nOpenGD77BaseCodeplug::APRSSettingsElement::fixedPosition() const {\n  uint32_t latCode = getUInt24_le(Offset::latitude());\n  uint32_t lonCode = getUInt24_le(Offset::longitude());\n  return QGeoCoordinate(decodeAngle(latCode), decodeAngle(lonCode));\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setFixedPosition(const QGeoCoordinate &coor) {\n  setUInt24_le(Offset::latitude(), encodeAngle(coor.latitude()));\n  setUInt24_le(Offset::longitude(), encodeAngle(coor.longitude()));\n  setBit(Offset::useFixedPosition());\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::enableFixedPosition(bool enable) {\n  setBit(Offset::useFixedPosition(), enable);\n}\n\nOpenGD77BaseCodeplug::APRSSettingsElement::PositionPrecision\nOpenGD77BaseCodeplug::APRSSettingsElement::positionPrecision() const {\n  return (PositionPrecision)getUInt4(Offset::positionPrecision());\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setPositionPrecision(PositionPrecision prec) {\n  setUInt4(Offset::positionPrecision(), (unsigned int) prec);\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::hasVia1() const {\n  return ! via1Call().isEmpty();\n}\n\nQString\nOpenGD77BaseCodeplug::APRSSettingsElement::via1Call() const {\n  return readASCII(Offset::via1Call(), 6, 0x00);\n}\n\nunsigned int\nOpenGD77BaseCodeplug::APRSSettingsElement::via1SSID() const {\n  return getUInt8(Offset::via1SSID());\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setVia1(const QString &call, unsigned int ssid) {\n  writeASCII(Offset::via1Call(), call, 6, 0x00);\n  setUInt8(Offset::via1SSID(), ssid);\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::clearVia1() {\n  setVia1(\"\", 0);\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::hasVia2() const {\n  return ! via2Call().isEmpty();\n}\n\nQString\nOpenGD77BaseCodeplug::APRSSettingsElement::via2Call() const {\n  return readASCII(Offset::via2Call(), 6, 0x00);\n}\n\nunsigned int\nOpenGD77BaseCodeplug::APRSSettingsElement::via2SSID() const {\n  return getUInt8(Offset::via2SSID());\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setVia2(const QString &call, unsigned int ssid) {\n  writeASCII(Offset::via2Call(), call, 6, 0x00);\n  setUInt8(Offset::via2SSID(), ssid);\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::clearVia2() {\n  setVia2(\"\", 0);\n}\n\n\nFMAPRSSystem::Icon\nOpenGD77BaseCodeplug::APRSSettingsElement::icon() const {\n  return (FMAPRSSystem::Icon)getUInt8(Offset::iconIndex());\n}\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setIcon(FMAPRSSystem::Icon icon) {\n  setUInt8(Offset::iconTable(), (FMAPRSSystem::SECONDARY_TABLE & (unsigned int)icon) ? 1 : 0);\n  setUInt8(Offset::iconIndex(), FMAPRSSystem::ICON_MASK & (unsigned int)icon);\n}\n\nQString\nOpenGD77BaseCodeplug::APRSSettingsElement::comment() const {\n  return readASCII(Offset::comment(), Limit::commentLength(), 0x00);\n}\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setComment(const QString &comment) {\n  writeASCII(Offset::comment(), comment, Limit::commentLength(), 0x00);\n}\n\nOpenGD77BaseCodeplug::APRSSettingsElement::BaudRate\nOpenGD77BaseCodeplug::APRSSettingsElement::baudRate() const {\n  return getBit(Offset::baudRate()) ? BaudRate::Baud300 : BaudRate::Baud1200;\n}\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setBaudRate(BaudRate rate) {\n  setBit(Offset::baudRate(), BaudRate::Baud300 == rate);\n}\n\n\nFrequency\nOpenGD77BaseCodeplug::APRSSettingsElement::fmFrequency() const {\n  return Frequency::fromHz(getUInt32_le(Offset::fmFrequency())*10);\n}\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsElement::setFMFrequency(Frequency f) {\n  setUInt32_le(Offset::fmFrequency(), f.inHz()/10);\n}\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::encode(const FMAPRSSystem *sys, const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  clear();\n\n  setName(sys->name());\n  setSourceSSID(sys->srcSSID());\n\n  QStringList vias = sys->path().split(\",\");\n  unsigned int viaCount = 0;\n  for (auto via: vias) {\n    QRegularExpression pattern(\"^([A-Z0-9]+)-(1?[0-9])$\");\n    auto match = pattern.match(via);\n    if (! match.hasMatch())\n      continue;\n    if (0 == viaCount)\n      setVia1(match.captured(1), match.captured(2).toUInt());\n    else if (1 == viaCount)\n      setVia2(match.captured(1), match.captured(2).toUInt());\n    else\n      break;\n    viaCount++;\n  }\n\n  setIcon(sys->icon());\n  setComment(sys->message());\n\n  enableFixedPosition(false);\n  setBaudRate(BaudRate::Baud1200);\n  setPositionPrecision(PositionPrecision::Max);\n\n  if(sys->hasRevertChannel()) {\n    setFMFrequency(sys->revertChannel()->txFrequency());\n  }\n\n  return true;\n}\n\n\nFMAPRSSystem *\nOpenGD77BaseCodeplug::APRSSettingsElement::decode(const Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid APRS settings.\";\n    return nullptr;\n  }\n\n  FMAPRSSystem *sys = new FMAPRSSystem();\n  sys->setName(name());\n  sys->setDestination(\"APN000\", 0);\n  sys->setSrcSSID(sourceSSID());\n  QStringList path;\n  if (hasVia1()) path.append(QString(\"%1-%2\").arg(via1Call()).arg(via1SSID()));\n  if (hasVia2()) path.append(QString(\"%1-%2\").arg(via2Call()).arg(via2SSID()));\n  sys->setPath(path.join(\",\"));\n\n  sys->setIcon(icon());\n  sys->setMessage(comment());\n\n  return sys;\n}\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsElement::link(FMAPRSSystem *sys, const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  if(fmFrequency().inHz() == 0) {\n    sys->resetRevertChannel();\n  } else {\n    // First, try to find a matching analog channel in list\n    FMChannel *ch = ctx.config()->channelList()->findFMChannelByTxFreq(fmFrequency());\n    if (! ch) {\n      // If no channel is found, create one with the settings from APRS channel:\n      ch = new FMChannel();\n      ch->setName(\"APRS Channel\");\n      ch->setRXFrequency(fmFrequency());\n      ch->setTXFrequency(fmFrequency());\n      ch->setBandwidth(FMChannel::Bandwidth::Narrow);\n      logInfo() << \"No matching APRS channel found for TX frequency \" << double(fmFrequency().inHz())/1e6\n                << \"MHz, create one as 'APRS Channel'\";\n      ctx.config()->channelList()->add(ch);\n    }\n    sys->setRevertChannel(ch);\n  }\n\n  if (ctx.config()->settings()->defaultId())\n    sys->setSource(ctx.config()->settings()->defaultId()->name());\n\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::APRSSettingsBank\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::APRSSettingsBankElement::APRSSettingsBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::APRSSettingsBankElement::APRSSettingsBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::APRSSettingsBankElement::clear() {\n  for (unsigned int i=0; i<Limit::systems(); i++)\n    system(i).clear();\n}\n\n\nOpenGD77BaseCodeplug::APRSSettingsElement\nOpenGD77BaseCodeplug::APRSSettingsBankElement::system(unsigned int idx) const {\n  return APRSSettingsElement(_data + Offset::systems() + idx*Offset::betweenSystems());\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::systems(); i++) {\n    if (ctx.has<FMAPRSSystem>(i)) {\n      if (! system(i).encode(ctx.get<FMAPRSSystem>(i), ctx, err)) {\n        errMsg(err) << \"Cannot encode APRS system '\" << ctx.get<FMAPRSSystem>(i)->name()\n                    << \" at index \" << i << \".\";\n        return false;\n      }\n    } else {\n      system(i).clear();\n    }\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::systems(); i++) {\n    if (system(i).isValid()) {\n      FMAPRSSystem *sys = system(i).decode(ctx, err);\n      if (nullptr == sys) {\n        errMsg(err) << \"Cannot decode APRS system at index \" << i << \".\";\n        return false;\n      }\n      ctx.config()->posSystems()->add(sys);\n      ctx.add(sys, i);\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenGD77BaseCodeplug::APRSSettingsBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::systems(); i++) {\n    if (system(i).isValid()) {\n      if (! ctx.has<FMAPRSSystem>(i)) {\n        errMsg(err) << \"Cannot link APRS system at index \" << i << \": Not found in context.\";\n        return false;\n      }\n      if (! system(i).link(ctx.get<FMAPRSSystem>(i), ctx, err)) {\n        errMsg(err) << \"Cannot link APRS system '\" << ctx.get<FMAPRSSystem>(i)->name()\n                    << \"' at index \" << i << \".\";\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::DTMFContactElement\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nbool\nOpenGD77BaseCodeplug::DTMFContactElement::isValid() const {\n  return !name().isEmpty();\n}\n\n\nvoid\nOpenGD77BaseCodeplug::DTMFContactElement::clear() {\n  setName(\"\");\n  setNumber(\"\");\n}\n\n\nQString\nOpenGD77BaseCodeplug::DTMFContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::DTMFContactElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nQString\nOpenGD77BaseCodeplug::DTMFContactElement::number() const {\n  QString number;\n  uint8_t *ptr = _data + Offset::number();\n  const QVector<char> lut = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'};\n  for (unsigned int i=0; (i<Limit::numberLength()) && (lut.size() > *ptr); i++, ptr++)\n    number.append(lut[*ptr]);\n  return number;\n}\n\nvoid\nOpenGD77BaseCodeplug::DTMFContactElement::setNumber(const QString &number) {\n  uint8_t *ptr = _data + Offset::number();\n  memset(ptr, 0xff, Limit::numberLength());\n  unsigned int n = std::min(Limit::numberLength(), (unsigned int)number.length());\n  const QVector<QChar> lut = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'};\n  for (unsigned int i=0; (i<n) && lut.contains(number.at(i)); i++, ptr++)\n    *ptr = lut.indexOf(number.at(i));\n}\n\n\nbool\nOpenGD77BaseCodeplug::DTMFContactElement::encode(const DTMFContact *contact, const Context &ctx, const ErrorStack &err)\n{\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  setName(contact->name());\n  setNumber(contact->number());\n\n  return true;\n}\n\n\nDTMFContact *\nOpenGD77BaseCodeplug::DTMFContactElement::decode(const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx);\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid DTMF contact.\";\n    return nullptr;\n  }\n\n  return new DTMFContact(name(), number());\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenGD77BaseCodeplug::DTMFContactBankElement\n * ******************************************************************************************** */\nOpenGD77BaseCodeplug::DTMFContactBankElement::DTMFContactBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::DTMFContactBankElement::DTMFContactBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::DTMFContactBankElement::clear() {\n  for (unsigned int i=0; i<Limit::contacts(); i++)\n    contact(i).clear();\n}\n\n\nOpenGD77BaseCodeplug::DTMFContactElement\nOpenGD77BaseCodeplug::DTMFContactBankElement::contact(unsigned int n) const {\n  return DTMFContactElement(_data + Offset::contacts() + n*Offset::betweenContacts());\n}\n\n\nbool\nOpenGD77BaseCodeplug::DTMFContactBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::contacts(); i++) {\n    if (ctx.has<DTMFContact>(i)) {\n      if (! contact(i).encode(ctx.get<DTMFContact>(i), ctx, err)) {\n        errMsg(err) << \"Cannot encode DTMF contact \" << ctx.get<DTMFContact>(i)->name()\n                    << \" at index \" << i << \".\";\n        return false;\n      }\n    } else {\n      contact(i).clear();\n    }\n  }\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::DTMFContactBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::contacts(); i++) {\n    if (! contact(i).isValid())\n      continue;\n    DTMFContact *cnt = contact(i).decode(ctx, err);\n    if (nullptr == cnt) {\n      errMsg(err) << \"Cannot decode DTMF contact at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->contacts()->add(cnt);\n    ctx.add(cnt, i);\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::BootSettingsElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x20)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::BootSettingsElement::~BootSettingsElement() {\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::clear() {\n  enableBootText(true);\n  clearBootPassword();\n  setLine1(\"\");\n  setLine2(\"\");\n}\n\n\nbool\nOpenGD77BaseCodeplug::BootSettingsElement::bootText() const {\n  return (1 == getUInt8(Offset::bootText()));\n}\n\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::enableBootText(bool enable) {\n  setUInt8(Offset::bootText(), (enable ? 1 :0));\n}\n\n\nbool\nOpenGD77BaseCodeplug::BootSettingsElement::bootPasswordEnabled() const {\n  return (1 == getUInt8(Offset::bootPasswdEnable()));\n}\n\nunsigned\nOpenGD77BaseCodeplug::BootSettingsElement::bootPassword() const {\n  return getBCD8_be(Offset::bootPasswd());\n}\n\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::setBootPassword(unsigned passwd) {\n  setBCD8_be(Offset::bootPasswd(), passwd);\n  setUInt8(Offset::bootPasswdEnable(), 1);\n}\n\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::clearBootPassword() {\n  setUInt8(Offset::bootPasswdEnable(), 0);\n}\n\n\nQString\nOpenGD77BaseCodeplug::BootSettingsElement::line1() const {\n  return readASCII(Offset::line1(), Limit::lineLength(), 0xff);\n}\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::setLine1(const QString &text) {\n  writeASCII(Offset::line1(), text, Limit::lineLength(), 0xff);\n}\n\nQString\nOpenGD77BaseCodeplug::BootSettingsElement::line2() const {\n  return readASCII(Offset::line2(), Limit::lineLength(), 0xff);\n}\nvoid\nOpenGD77BaseCodeplug::BootSettingsElement::setLine2(const QString &text) {\n  writeASCII(Offset::line2(), text, Limit::lineLength(), 0xff);\n}\n\n\nbool\nOpenGD77BaseCodeplug::BootSettingsElement::encode(const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  setLine1(ctx.config()->settings()->boot()->message1());\n  setLine2(ctx.config()->settings()->boot()->message2());\n  return true;\n}\n\nbool\nOpenGD77BaseCodeplug::BootSettingsElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ctx.config()->settings()->boot()->setMessage1(line1());\n  ctx.config()->settings()->boot()->setMessage2(line2());\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ZoneElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ZoneElement::~ZoneElement() {\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::ZoneElement::clear() {\n  memset(_data+Offset::name(), 0xff, Limit::nameLength());\n  memset(_data+Offset::channels(), 0x00, Offset::betweenChannels()*Limit::memberCount());\n}\nbool\nOpenGD77BaseCodeplug::ZoneElement::isValid() const {\n  return (! name().isEmpty());\n}\n\nQString\nOpenGD77BaseCodeplug::ZoneElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nOpenGD77BaseCodeplug::ZoneElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneElement::hasMember(unsigned n) const {\n  if (n >= Limit::memberCount())\n    return false;\n  return 0 != getUInt16_le(Offset::channels() + Offset::betweenChannels()*n);\n}\n\nunsigned\nOpenGD77BaseCodeplug::ZoneElement::member(unsigned n) const {\n  if (n >= Limit::memberCount())\n    return 0;\n  return getUInt16_le(Offset::channels() + Offset::betweenChannels()*n)-1;\n}\n\nvoid\nOpenGD77BaseCodeplug::ZoneElement::setMember(unsigned n, unsigned idx) {\n  if (n >= Limit::memberCount())\n    return;\n  setUInt16_le(Offset::channels() + Offset::betweenChannels()*n, idx+1);\n}\n\nvoid\nOpenGD77BaseCodeplug::ZoneElement::clearMember(unsigned n) {\n  setUInt16_le(Offset::channels() + Offset::betweenChannels()*n, 0);\n}\n\n\nZone *\nOpenGD77BaseCodeplug::ZoneElement::decode(const Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode an invalid zone.\";\n    return nullptr;\n  }\n  return new Zone(name());\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneElement::link(Zone *zone, Context &ctx, const ErrorStack &err) const {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link invalid zone.\";\n    return false;\n  }\n\n  for (unsigned int i=0; (i<Limit::memberCount()) && hasMember(i); i++) {\n    if (ctx.has<Channel>(member(i))) {\n      zone->A()->add(ctx.get<Channel>(member(i)));\n    } else {\n      logWarn() << \"While linking zone '\" << zone->name() << \"': \" << i <<\"-th channel index \"\n                << member(i) << \" out of bounds.\";\n    }\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneElement::encode(const Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (zone->A()->count() && zone->B()->count())\n    setName(zone->name() + \" A\");\n  else\n    setName(zone->name());\n\n  for (unsigned int i=0; i<Limit::memberCount(); i++) {\n    if (i < (unsigned int)zone->A()->count())\n      setMember(i, ctx.index(zone->A()->get(i)));\n    else\n      clearMember(i);\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ZoneBankElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ZoneBankElement::~ZoneBankElement() {\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::ZoneBankElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nOpenGD77BaseCodeplug::ZoneBankElement::isEnabled(unsigned idx) const {\n  unsigned byte= Offset::bitmap() + idx/8, bit = idx%8;\n  return getBit(byte, bit);\n}\nvoid\nOpenGD77BaseCodeplug::ZoneBankElement::enable(unsigned idx, bool enabled) {\n  unsigned byte=Offset::bitmap() + idx/8, bit = idx%8;\n  setBit(byte, bit, enabled);\n}\n\n\nOpenGD77BaseCodeplug::ZoneElement\nOpenGD77BaseCodeplug::ZoneBankElement::zone(unsigned int idx) {\n  return ZoneElement(_data + Offset::zones() + idx*Offset::betweenZones());\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::zoneCount(); i++) {\n    if (ctx.has<Zone>(i)) {\n      if (! zone(i).encode(ctx.get<Zone>(i), ctx, err)) {\n        errMsg(err) << \"Cannot encode zone '\" << ctx.get<Zone>(i)->name()\n                    << \"' at index \" << i << \".\";\n        return false;\n      }\n      enable(i, true);\n    } else {\n      zone(i).clear();\n      enable(i, false);\n    }\n  }\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::zoneCount(); i++ ) {\n    if (! isEnabled(i))\n      continue;\n\n    Zone *obj = zone(i).decode(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode zone at index \" << i << \".\";\n      return false;\n    }\n\n    ctx.config()->zones()->add(obj);\n    ctx.add(obj, i);\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ZoneBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::zoneCount(); i++ ) {\n    if (! isEnabled(i))\n      continue;\n\n    Zone *obj = ctx.get<Zone>(i);\n    if (! zone(i).link(obj, ctx, err)) {\n      errMsg(err) << \"Cannot link zone '\" << obj->name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ContactElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ContactElement::~ContactElement() {\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::ContactElement::clear() {\n  setName(\"\");\n  setNumber(0);\n  setType(DMRContact::GroupCall);\n  setTimeSlotOverride(TimeSlotOverride::None);\n}\n\n\nbool\nOpenGD77BaseCodeplug::ContactElement::isValid() const {\n  return (! name().isEmpty());\n}\n\n\nQString\nOpenGD77BaseCodeplug::ContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::ContactElement::setName(const QString name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nunsigned\nOpenGD77BaseCodeplug::ContactElement::number() const {\n  return getBCD8_be(Offset::number());\n}\n\nvoid\nOpenGD77BaseCodeplug::ContactElement::setNumber(unsigned id) {\n  setBCD8_be(Offset::number(), id);\n}\n\n\nDMRContact::Type\nOpenGD77BaseCodeplug::ContactElement::type() const {\n  switch (getUInt8(Offset::type())) {\n  case 0: return DMRContact::GroupCall;\n  case 1: return DMRContact::PrivateCall;\n  case 2: return DMRContact::AllCall;\n  default: break;\n  }\n  return DMRContact::PrivateCall;\n}\n\nvoid\nOpenGD77BaseCodeplug::ContactElement::setType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break;\n  case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break;\n  case DMRContact::AllCall: setUInt8(Offset::type(), 2); break;\n  }\n}\n\n\nOpenGD77BaseCodeplug::ContactElement::TimeSlotOverride\nOpenGD77BaseCodeplug::ContactElement::timeSlotOverride() const {\n  return (TimeSlotOverride)getUInt8(Offset::timeSlotOverride());\n}\nvoid\nOpenGD77BaseCodeplug::ContactElement::setTimeSlotOverride(TimeSlotOverride ts) {\n  setUInt8(Offset::timeSlotOverride(), (unsigned int) ts);\n}\n\n\nDMRContact *\nOpenGD77BaseCodeplug::ContactElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n  if (! isValid()) {\n    errMsg(err) << \"Cannot create contact from an invalid element.\";\n    return nullptr;\n  }\n\n  auto contact = new DMRContact(type(), name(), number(), false);\n  contact->setOpenGD77ContactExtension(new OpenGD77ContactExtension());\n\n  switch (timeSlotOverride()) {\n  case TimeSlotOverride::None:\n    contact->openGD77ContactExtension()->setTimeSlotOverride(\n          OpenGD77ContactExtension::TimeSlotOverride::None);\n    break;\n  case TimeSlotOverride::TS1:\n    contact->openGD77ContactExtension()->setTimeSlotOverride(\n          OpenGD77ContactExtension::TimeSlotOverride::TS1);\n    break;\n  case TimeSlotOverride::TS2:\n    contact->openGD77ContactExtension()->setTimeSlotOverride(\n          OpenGD77ContactExtension::TimeSlotOverride::TS2);\n    break;\n  }\n\n  return contact;\n}\n\nbool\nOpenGD77BaseCodeplug::ContactElement::encode(const DMRContact *cont, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  setName(cont->name());\n  setNumber(cont->number());\n  setType(cont->type());\n\n  if (nullptr == cont->openGD77ContactExtension())\n    return true;\n\n  switch (cont->openGD77ContactExtension()->timeSlotOverride()) {\n  case OpenGD77ContactExtension::TimeSlotOverride::None:\n    setTimeSlotOverride(TimeSlotOverride::None);\n    break;\n  case OpenGD77ContactExtension::TimeSlotOverride::TS1:\n    setTimeSlotOverride(TimeSlotOverride::TS1);\n    break;\n  case OpenGD77ContactExtension::TimeSlotOverride::TS2:\n    setTimeSlotOverride(TimeSlotOverride::TS2);\n    break;\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::ContactBankElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::ContactBankElement::ContactBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::ContactBankElement::clear() {\n  for (unsigned int i=0; i<Limit::contactCount(); i++)\n    contact(i).clear();\n}\n\n\nOpenGD77BaseCodeplug::ContactElement\nOpenGD77BaseCodeplug::ContactBankElement::contact(unsigned int idx) const {\n  return ContactElement(_data + Offset::contacts() + idx*Offset::betweenContacts());\n}\n\n\nbool\nOpenGD77BaseCodeplug::ContactBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    if (ctx.has<DMRContact>(i)) {\n      if (! contact(i).encode(ctx.get<DMRContact>(i), ctx, err)) {\n        errMsg(err) << \"Cannot encode DMR contact \" << ctx.get<DMRContact>(i)->name()\n                    << \" at index \" << i << \".\";\n        return false;\n      }\n    } else {\n      contact(i).clear();\n    }\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::ContactBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    if (! contact(i).isValid())\n      continue;\n    DMRContact *cnt = contact(i).decode(ctx, err);\n    if (nullptr == cnt) {\n      errMsg(err) << \"Cannot decode contact at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->contacts()->add(cnt);\n    ctx.add(cnt, i);\n  }\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::GroupListElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::GroupListElement::clear() {\n  memset(_data + Offset::name(), 0xff, Limit::nameLength()+1);\n  memset(_data + Offset::contacts(), 0, Limit::contactCount()*2);\n}\n\n\nQString\nOpenGD77BaseCodeplug::GroupListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\n\nvoid\nOpenGD77BaseCodeplug::GroupListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListElement::hasContactIndex(unsigned int i) const {\n  return 0 != getUInt16_le(Offset::contacts() + i*Offset::betweenContacts());\n}\n\nunsigned int\nOpenGD77BaseCodeplug::GroupListElement::contactIndex(unsigned int i) const {\n  return getUInt16_le(Offset::contacts() + i*Offset::betweenContacts()) - 1;\n}\n\nvoid\nOpenGD77BaseCodeplug::GroupListElement::setContactIndex(unsigned int i, unsigned int contactIdx) {\n  setUInt16_le(Offset::contacts() + i*Offset::betweenContacts(), contactIdx + 1);\n}\n\nvoid\nOpenGD77BaseCodeplug::GroupListElement::clearContactIndex(unsigned int i) {\n  setUInt16_le(Offset::contacts() + i*Offset::betweenContacts(), 0);\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListElement::encode(RXGroupList *lst, Context &ctx, const ErrorStack &err) {\n  setName(lst->name());\n\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    if (i < (unsigned int)lst->count()) {\n      int idx = ctx.index(lst->contact(i));\n      if (0 > idx) {\n        errMsg(err) << \"Cannot encode group list '\" << lst->name()\n                    << \"', contact '\" << lst->contact(i)->name() << \"' not indexed.\";\n        return false;\n      }\n      setContactIndex(i, idx);\n    } else {\n      clearContactIndex(i);\n    }\n  }\n  return true;\n}\n\n\nRXGroupList *\nOpenGD77BaseCodeplug::GroupListElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return new RXGroupList(name());\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListElement::link(RXGroupList *lst, Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    if (! hasContactIndex(i))\n      continue;\n\n    if (! ctx.has<DMRContact>(contactIndex(i))) {\n      errMsg(err) << \"Cannot resolve contact index \" << contactIndex(i) << \".\";\n      return false;\n    }\n\n    lst->addContact(ctx.get<DMRContact>(contactIndex(i)));\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::GroupListBankElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::GroupListBankElement::clear() {\n  for (unsigned int i=0; i<Limit::groupListCount(); i++)\n    clearGroupList(i);\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListBankElement::hasGroupList(unsigned int i) const {\n  return 0 != getUInt8(Offset::length() + i);\n}\n\nunsigned int\nOpenGD77BaseCodeplug::GroupListBankElement::groupListContactCount(unsigned int i) const {\n  return getUInt8(Offset::length() + i) - 1;\n}\n\nvoid\nOpenGD77BaseCodeplug::GroupListBankElement::setGroupListContactCount(unsigned int i, unsigned int count) {\n  setUInt8(Offset::length() + i, count+1);\n}\n\nOpenGD77BaseCodeplug::GroupListElement\nOpenGD77BaseCodeplug::GroupListBankElement::groupList(unsigned int i) const {\n  return GroupListElement(_data + Offset::groupLists()  + i*Offset::betweenGroupLists());\n}\n\nvoid\nOpenGD77BaseCodeplug::GroupListBankElement::clearGroupList(unsigned int i) {\n  setUInt8(Offset::length() + i, 0);\n  groupList(i).clear();\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListBankElement::encode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::groupListCount(); i++) {\n    if (ctx.has<RXGroupList>(i)) {\n      auto obj = ctx.get<RXGroupList>(i);\n      setGroupListContactCount(i, obj->count());\n      if (! groupList(i).encode(obj, ctx, err)) {\n        clearGroupList(i);\n        errMsg(err) << \"Cannot encode group list '\" << obj->name() << \"' at index \" << i << \".\";\n        return false;\n      }\n    } else {\n      clearGroupList(i);\n    }\n  }\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::groupListCount(); i++) {\n    if (! hasGroupList(i))\n      continue;\n    auto obj = groupList(i).decode(ctx, err);\n    if (nullptr == obj) {\n      errMsg(err) << \"Cannot decode group list at index \" << i << \".\";\n      return false;\n    }\n    ctx.config()->rxGroupLists()->add(obj);\n    ctx.add(obj, i);\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::GroupListBankElement::link(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::groupListCount(); i++) {\n    if (! hasGroupList(i))\n      continue;\n    auto obj = ctx.get<RXGroupList>(i);\n    if (! groupList(i).link(obj, ctx, err)) {\n      errMsg(err) << \"Cannot link group list '\" << obj->name() << \"' at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::SatelliteElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::SatelliteElement::SatelliteElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::SatelliteElement::SatelliteElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::clear() {\n  memset(_data, 0, size());\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::writeDigit(const Offset::Bit &offset, uint8_t digit) {\n  // Must be bit 0 or 3 (BCD)\n  if (offset.bit % 4)\n    return;\n\n  uint8_t val = getUInt8(offset.byte);\n  val &= ~(0xf << offset.bit);\n  val |= ((digit & 0xf) << offset.bit);\n  setUInt8(offset.byte, val);\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::writeInteger(const Offset::Bit &offset, int value, bool sign, unsigned int dec) {\n  unsigned int o = 0;\n  // Must be bit 0 or 4 (BCD)\n  if (offset.bit % 4)\n    return;\n  if (0 == dec)\n    return;\n\n  if (sign && 0 > value)\n    writeDigit(offset + o, 0xc); // '-' ?!?\n  else\n    writeDigit(offset + o, 0xb); // blank\n\n  o += 4*(dec-1);\n  for (int i=dec; i>0; i--, o = o - 4) {\n    writeDigit(offset + o, value % 10);\n    value /= 10;\n  }\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::writeFractional(const Offset::Bit &offset, double value, bool sign, unsigned int frac) {\n  unsigned int o = 0;\n  if (offset.bit % 4)\n    return;\n\n  if (0 == frac)\n    return;\n\n  if (sign) {\n    if (0 > value)\n      writeDigit(offset + o, 0xc);\n    else\n      writeDigit(offset + o, 0xb);\n    o += 4;\n  }\n\n  value -= int(value);\n  for (unsigned int i=0; i<frac; i++, o += 4) {\n    value *= 10;\n    writeDigit(offset + o, int(value));\n    value -= int(value);\n  }\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::writeFixedPoint(const Offset::Bit &offset, double value, bool sign, unsigned int dec, unsigned int frac) {\n  unsigned int o = 0;\n  writeInteger(offset + o, value, sign, dec);\n  o += 4*dec + (sign ? 4 : 0);\n\n  writeDigit(offset + o, 0xa);\n  o += 4;\n\n  value = std::abs(value);\n  value -= int(value);\n  writeFractional(offset + o, value, false, frac);\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0x00);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setEpoch(const OrbitalElement::Epoch &epoch) {\n  writeInteger(Offset::epochYear(), epoch.year%100, false, 2);\n  writeFixedPoint(Offset::epochJulienDay(), epoch.toEpoch(), false, 3, 8);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setMeanMotionDerivative(double dmm) {\n  writeFixedPoint(Offset::meanMotionDerivative(), dmm, true, 0, 8);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setInclination(double incl) {\n  writeFixedPoint(Offset::inclination(), incl, false, 3, 4);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setAscension(double asc) {\n  writeFixedPoint(Offset::ascension(), asc, false, 3, 4);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setEccentricity(double ecc) {\n  writeFractional(Offset::eccentricity(), ecc, false, 7);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setPerigee(double arg) {\n  writeFixedPoint(Offset::perigee(), arg, false, 3, 4);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setMeanAnomaly(double ma) {\n  writeFixedPoint(Offset::meanAnomaly(), ma, false, 3, 4);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setMeanMotion(double mm) {\n  writeFixedPoint(Offset::meanMotion(), mm, false, 2, 8);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setRevolutionNumber(unsigned int num) {\n  writeInteger(Offset::revolutionNumber(), num, false, 5);\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setFMDownlink(const Frequency &f) {\n  setUInt32_le(Offset::fmDownlink(), f.inHz());\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setFMUplink(const Frequency &f) {\n  setUInt32_le(Offset::fmUplink(), f.inHz());\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setCTCSS(const SelectiveCall &call) {\n  if (! call.isCTCSS())\n    return;\n  setUInt32_le(Offset::ctcss(), call.mHz()/100);\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setAPRSDownlink(const Frequency &f) {\n  setUInt32_le(Offset::aprsDownlink(), f.inHz());\n}\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setAPRSUplink(const Frequency &f) {\n  setUInt32_le(Offset::aprsUplink(), f.inHz());\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setBeacon(const Frequency &f) {\n  setUInt32_le(Offset::beacon(), f.inHz());\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteElement::setAPRSPath(const QString &path) {\n  writeASCII(Offset::aprsPath(), path, Limit::pathLength(), 0x00);\n}\n\n\nbool\nOpenGD77BaseCodeplug::SatelliteElement::encode(const Satellite &sat, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  // meta\n  setName(sat.name());\n\n  // orbital elements\n  setEpoch(sat.epoch());\n  setMeanMotion(sat.meanMotion());\n  setMeanMotionDerivative(sat.meanMotionDerivative());\n  setInclination(sat.inclination());\n  setAscension(sat.ascension());\n  setEccentricity(sat.eccentricity());\n  setPerigee(sat.perigee());\n  setMeanAnomaly(sat.meanAnomaly());\n  setRevolutionNumber(sat.revolutionNumber());\n\n  // transponder\n  setFMDownlink(sat.fmDownlink());\n  setFMUplink(sat.fmUplink());\n  setCTCSS(sat.fmUplinkTone());\n  setAPRSDownlink(sat.aprsDownlink());\n  setAPRSUplink(sat.aprsUplink());\n  setBeacon(sat.beacon());\n\n  /// @bug set APRS path\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::SatelliteBankElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::SatelliteBankElement::SatelliteBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::SatelliteBankElement::SatelliteBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::SatelliteBankElement::clear() {\n  memset(_data, 0, size());\n  setUInt32_le(Offset::blockId(), magic());\n  setUInt32_le(Offset::segmentSize(), size()-8);\n}\n\n\nOpenGD77BaseCodeplug::SatelliteElement\nOpenGD77BaseCodeplug::SatelliteBankElement::satellite(unsigned int idx) {\n  return SatelliteElement(_data + Offset::satellites() + idx*Offset::betweenSatellites());\n}\n\nbool\nOpenGD77BaseCodeplug::SatelliteBankElement::encode(SatelliteDatabase *db, const ErrorStack &err) {\n  clear();\n\n  for (unsigned int i=0; i<Limit::satellites(); i++) {\n    SatelliteElement el = satellite(i);\n    el.clear();\n    if (db->count() <= i)\n      continue;\n    if (! el.encode(db->getAt(i), err)) {\n      errMsg(err) << \"Cannot encode satellite '\" << db->getAt(i).name()\n                  << \"' at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::NoteElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::NoteElement::NoteElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nvoid\nOpenGD77BaseCodeplug::NoteElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nOpenGD77BaseCodeplug::NoteElement::isValid() const {\n  return (0 != getUInt8(Offset::pitch()) || 0 != getUInt8(Offset::duration())) &&\n         0xff != getUInt8(Offset::pitch()) && 0xff != getUInt8(Offset::duration());\n}\n\nbool\nOpenGD77BaseCodeplug::NoteElement::isPause() const {\n  return 0 == getUInt8(Offset::pitch());\n}\n\ndouble\nOpenGD77BaseCodeplug::NoteElement::frequency() const {\n  if (isPause())\n    return 0;\n  return _lut[std::min(44, getUInt8(Offset::pitch())-1)];\n}\n\nvoid\nOpenGD77BaseCodeplug::NoteElement::setFrequency(double pitch) {\n  auto it = std::lower_bound(std::begin(_lut), std::end(_lut), pitch);\n  if (it != std::begin(_lut)) {\n    // check if it-1 is closer\n    if ((pitch-*(it-1)) < (*it-pitch))\n      --it;\n  }\n  setUInt8(Offset::pitch(), (it == std::end(_lut)) ? 44 : (1+it-std::begin(_lut)));\n}\n\nvoid\nOpenGD77BaseCodeplug::NoteElement::setPause() {\n  setUInt8(Offset::pitch(), 0);\n}\n\n\nunsigned int\nOpenGD77BaseCodeplug::NoteElement::duration() const {\n  return 100 * (unsigned int)getUInt8(Offset::duration());\n}\n\nvoid\nOpenGD77BaseCodeplug::NoteElement::setDuration(unsigned int ms) {\n  ms = Limit::Range<unsigned int>{100,25500}.limit(ms);\n  setUInt8(Offset::duration(), ms/100);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::BootMelodyElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::BootMelodyElement::BootMelodyElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::BootMelodyElement::clear() {\n  memset(_data, 0, size());\n  setUInt32_le(Offset::blockId(), magic());\n  setUInt32_le(Offset::segmentSize(), size()-8);\n}\n\n\nbool\nOpenGD77BaseCodeplug::BootMelodyElement::encode(Context &ctx, const Melody *melody, const ErrorStack &err) {\n  Q_UNUSED(err); Q_UNUSED(ctx);\n\n  clear();\n  auto notes = melody->toTones();\n  unsigned int numNotes = std::min(Limit::notes(), (unsigned int)notes.size());\n  for (unsigned int i=0; i<numNotes; i++) {\n    NoteElement note(_data + Offset::notes() + i*Offset::betweenNotes());\n    if (0 == notes[i].first)\n      note.setPause();\n    else\n      note.setFrequency(notes[i].first);\n    note.setDuration(notes[i].second);\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::BootMelodyElement::decode(Context &ctx, Melody *melody, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  QVector<QPair<double, unsigned int>> notes;\n  for (unsigned int i=0; i<Limit::notes(); i++) {\n    NoteElement note(_data + Offset::notes() + i*Offset::betweenNotes());\n    if (! note.isValid())\n      break;\n    notes.append({note.frequency(), note.duration()});\n  }\n\n  if (notes.isEmpty())\n    return false;\n\n  melody->infer(notes);\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug::AdditionalSettingsElement\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::AdditionalSettingsElement::AdditionalSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenGD77BaseCodeplug::AdditionalSettingsElement::AdditionalSettingsElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nbool\nOpenGD77BaseCodeplug::AdditionalSettingsElement::isValid() const {\n  return Element::isValid() && (\"OpenGD77\" ==  magic()) && (1 == version());\n}\n\nvoid\nOpenGD77BaseCodeplug::AdditionalSettingsElement::clear() {\n  memset(_data, 0xff, size());\n  writeASCII(Offset::magicString(), \"OpenGD77\", Limit::magicStringLength(), 0xff);\n  setUInt32_le(Offset::versionNumber(), 1);\n}\n\nQString\nOpenGD77BaseCodeplug::AdditionalSettingsElement::magic() const {\n  return readASCII(Offset::magicString(), Limit::magicStringLength(), 0xff);\n}\n\nunsigned int\nOpenGD77BaseCodeplug::AdditionalSettingsElement::version() const {\n  return getUInt32_le(Offset::versionNumber());\n}\n\nbool\nOpenGD77BaseCodeplug::AdditionalSettingsElement::hasSettings(Settings set) const {\n  if (! isValid())\n    return false;\n\n  for (unsigned int offset = Offset::blocks(); offset < size(); ) {\n    uint32_t magic = getUInt32_le(offset),\n        content_size = getUInt32_le(offset+4);\n    if (magic == (uint32_t)set)\n      return true;\n    if (0xffffffff == magic)\n      return false;\n    offset += 8 + content_size;\n  }\n\n  return false;\n}\n\nOpenGD77BaseCodeplug::SatelliteBankElement\nOpenGD77BaseCodeplug::AdditionalSettingsElement::satellites() const {\n  if (! isValid())\n    return SatelliteBankElement(nullptr);\n\n  for (unsigned int offset = Offset::blocks(); offset < size(); ) {\n    uint32_t magic = getUInt32_le(offset),\n        content_size = getUInt32_le(offset+4);\n    if (0xffffffff == magic) {\n      SatelliteBankElement(_data + offset).clear();\n      return SatelliteBankElement(_data + offset);\n    } else if (magic == (uint32_t)Settings::SatelliteOrbitals) {\n      return SatelliteBankElement(_data + offset);\n    }\n    offset += 8 + content_size;\n  }\n\n  return SatelliteBankElement(nullptr);\n}\n\n\nOpenGD77BaseCodeplug::BootMelodyElement\nOpenGD77BaseCodeplug::AdditionalSettingsElement::bootMelody() const {\n  if (! isValid())\n    return BootMelodyElement(nullptr);\n\n  for (unsigned int offset = Offset::blocks(); offset < size(); ) {\n    uint32_t magic = getUInt32_le(offset),\n        content_size = getUInt32_le(offset+4);\n    if (0xffffffff == magic) {\n      BootMelodyElement(_data + offset).clear();\n      return BootMelodyElement(_data + offset);\n    } if (magic == (uint32_t)Settings::BootMelody) {\n      return BootMelodyElement(_data + offset);\n    }\n    offset += 8 + content_size;\n  }\n\n  return BootMelodyElement(nullptr);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseCodeplug\n * ********************************************************************************************* */\nOpenGD77BaseCodeplug::OpenGD77BaseCodeplug(QObject *parent)\n  : Codeplug{parent}\n{\n  // pass...\n}\n\n\nvoid\nOpenGD77BaseCodeplug::clear() {\n  // Clear general config\n  clearGeneralSettings();\n  clearDTMFSettings();\n  clearAPRSSettings();\n  // clear DTMF contacts\n  clearDTMFContacts();\n  // clear channel\n  clearChannels();\n  // clear boot settings\n  clearBootSettings();\n  // clear VFO settings\n  clearVFOSettings();\n  // clear zones\n  clearZones();\n  // Clear contacts\n  clearContacts();\n  // clear group lists\n  clearGroupLists();\n}\n\nbool\nOpenGD77BaseCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // Map radio IDs\n  for (int i=0; i<config->radioIDs()->count(); i++) {\n    if (config->radioIDs()->get(i)->is<DMRContact>())\n      ctx.add(config->radioIDs()->get(i)->as<DMRContact>(), i+1);\n  }\n\n  // Map digital and DTMF contacts\n  for (int i=0, d=0, a=0; i<config->contacts()->count(); i++) {\n    if (config->contacts()->contact(i)->is<DMRContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DMRContact>(), d); d++;\n    } else if (config->contacts()->contact(i)->is<DTMFContact>()) {\n      ctx.add(config->contacts()->contact(i)->as<DTMFContact>(), a); a++;\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i);\n\n  // Map channels\n  for (int i=0; i<config->channelList()->count(); i++)\n    ctx.add(config->channelList()->channel(i), i);\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i);\n\n  // Map FM APRS systems\n  for (int i=0,a=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<FMAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<FMAPRSSystem>(), a); a++;\n    }\n  }\n\n  return true;\n}\n\n\nConfig *\nOpenGD77BaseCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot pre-process OpenGD77 codeplug.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(intermediate, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot split zone for OpenGD77 codeplug.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nOpenGD77BaseCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  // Check if default DMR id is set.\n  if (config->settings()->defaultIdRef()->isNull()) {\n    errMsg(err) << \"No default radio ID specified.\";\n    return false;\n  }\n\n  // Create index<->object table.\n  Context ctx(config);\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Cannot index configuration objects.\";\n    return false;\n  }\n\n  return this->encodeElements(flags, ctx);\n}\n\n\nbool\nOpenGD77BaseCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  // General config\n  if (! this->encodeGeneralSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode general settings.\";\n    return false;\n  }\n\n  if (! this->encodeDTMFSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode DTMF settings.\";\n    return false;\n  }\n\n  if (! this->encodeAPRSSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode APRS settings.\";\n    return false;\n  }\n\n  if (! this->encodeDTMFContacts(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode DTMF contacts.\";\n    return false;\n  }\n\n  if (! this->encodeChannels(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode channels\";\n    return false;\n  }\n\n  if (! this->encodeBootSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode boot text.\";\n    return false;\n  }\n\n  if (! this->encodeZones(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  // Define Contacts\n  if (! this->encodeContacts(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  if (! this->encodeGroupLists(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenGD77BaseCodeplug::decode(Config *config, const ErrorStack &err) {\n  // Clear config object\n  config->clear();\n\n  // Create index<->object table.\n  Context ctx(config);\n  return this->decodeElements(ctx, err);\n}\n\nbool\nOpenGD77BaseCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process Radioddy codeplug.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot merg zones in decoded Radioddity codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nOpenGD77BaseCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! this->decodeGeneralSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings.\";\n    return false;\n  }\n\n  if (! this->decodeDTMFSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode DTMF settings.\";\n    return false;\n  }\n\n  if (! this->decodeAPRSSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode APRS settings.\";\n    return false;\n  }\n\n  if (! this->decodeBootSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode boot settings.\";\n    return false;\n  }\n\n  if (! this->createContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create contacts.\";\n    return false;\n  }\n\n  if (! this->createDTMFContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create DTMF contacts\";\n    return false;\n  }\n\n  if (! this->createChannels(ctx, err)) {\n    errMsg(err) << \"Cannot create channels.\";\n    return false;\n  }\n\n  if (! this->createZones(ctx, err)) {\n    errMsg(err) << \"Cannot create zones.\";\n    return false;\n  }\n\n  if (! this->createGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot create group lists.\";\n    return false;\n  }\n\n  if (! this->linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  if (! this->linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  if (! this->linkGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n\n  if (! this->linkAPRSSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode APRS settings.\";\n    return false;\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/opengd77base_codeplug.hh",
    "content": "#ifndef OPENGD77BASE_CODEPLUG_HH\n#define OPENGD77BASE_CODEPLUG_HH\n\n#include \"channel.hh\"\n#include \"codeplug.hh\"\n#include \"gpssystem.hh\"\n#include \"contact.hh\"\n#include \"zone.hh\"\n#include \"satellitedatabase.hh\"\n#include \"melody.hh\"\n\n#include <QGeoCoordinate>\n\n/** Base codeplug for all OpenGD77 based firmware variants.\n * @ingroup ogd77 */\nclass OpenGD77BaseCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Possible image types. */\n  enum ImageType { EEPROM = 0, FLASH = 1 };\n\npublic:\n  /** Encodes an angle used to store locations. */\n  static uint32_t encodeAngle(double degee);\n  /** Decodes an angle used to store locations. */\n  static double decodeAngle(uint32_t code);\n\n  /** Encodes a selective call (tx/rx tone). */\n  static uint16_t encodeSelectiveCall(const SelectiveCall &call);\n  /** Decodes a selective call (tx/rx tone). */\n  static SelectiveCall decodeSelectiveCall(uint16_t code);\n\npublic:\n  /** Implements the base for all OpenGD77 channel encodings. */\n  class ChannelElement: public Codeplug::Element\n  {\n  public:\n    /** Possible channel types. */\n    enum Mode {\n      MODE_ANALOG = 0,   ///< Analog channel, aka FM.\n      MODE_DIGITAL = 1   ///< Digital channel, aka DMR.\n    };\n\n    /** Alias to transmit. */\n    enum class Alias {\n      None = 0, APRS = 1, Text = 2, Both = 3\n    };\n\n    /** Possible squelch modes. */\n    enum class SquelchMode {\n      Global, Open, Normal, Closed\n    };\n\n  protected:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructs a channel from the given memory. */\n    explicit ChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelElement();\n\n    /** The size of the channel. */\n    static constexpr unsigned int size() { return 0x0038; }\n\n    /** Resets the channel. */\n    virtual void clear();\n\n    /** Returns the name of the channel. */\n    virtual QString name() const;\n    /** Sets the name of the channel. */\n    virtual void setName(const QString &n);\n\n    /** Returns the RX frequency of the channel. */\n    virtual Frequency rxFrequency() const;\n    /** Sets the RX frequency of the channel. */\n    virtual void setRXFrequency(const Frequency &freq);\n    /** Returns the TX frequency of the channel. */\n    virtual Frequency txFrequency() const;\n    /** Sets the TX frequency of the channel. */\n    virtual void setTXFrequency(const Frequency &freq);\n\n    /** Returns the channel mode. */\n    virtual Mode mode() const;\n    /** Sets the channel mode. */\n    virtual void setMode(Mode mode);\n\n    /** Returns @c true if the power-setting is global. */\n    virtual bool globalPower() const;\n    /** Returns the power setting of the channel. */\n    virtual Channel::Power power() const;\n    /** Sets the power setting of the channel. */\n    virtual void setPower(Channel::Power pwr);\n    /** Clears the power setting. The global power setting is used. */\n    virtual void clearPower();\n\n    /** Returns the transmit timeout.\n     * An invalid interval indicates an infinite timeout. */\n    virtual Interval transmitTimeout() const;\n    /** Sets a transmit timeout.\n     * To disable the timeout, set an invalid interval. */\n    virtual void setTransmitTimeout(const Interval &interval);\n\n    /** Returns @c true, if a fixed position is set for the channel. */\n    virtual bool fixedPositionEnabled() const;\n    /** Returns the fixed position. */\n    virtual QGeoCoordinate fixedPosition() const;\n    /** Sets the fixed position for this channel. */\n    virtual void setFixedPosition(const QGeoCoordinate &coordinate);\n    /** Resets the fixed position. */\n    virtual void enableFixedPosition(bool enable);\n\n    /** Returns the RX subtone. */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the RX subtone. */\n    virtual void setRXTone(const SelectiveCall &code);\n    /** Returns the TX subtone. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the TX subtone. */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns @c true if the channel is set to simplex. */\n    virtual bool isSimplex() const;\n    /** Sets the channel to simplex. */\n    virtual void enableSimplex(bool enable);\n\n    /** Returns @c true, if the power-save feature is enabled. */\n    virtual bool powerSave() const;\n    /** Enables/disables power-save. */\n    virtual void enablePowerSave(bool enable);\n\n    /** Returns @c true, if the \"beep\" is enabled. */\n    virtual bool beep() const;\n    /** Enables/disables \"the beep\". */\n    virtual void enableBeep(bool enable);\n\n    /** Returns @c true if the global DMR ID is overridden. */\n    virtual bool hasDMRId() const;\n    /** Returns the DMR ID for this channel. */\n    virtual unsigned int dmrId() const;\n    /** Sets the DMR ID for this channel. */\n    virtual void setDMRId(unsigned int id);\n    /** Resets the DMR ID for this channel to the global one. */\n    virtual void clearDMRId();\n\n    /** Returns @c true if a group list is set. */\n    virtual bool hasGroupList() const;\n    /** Returns the group-list index. */\n    virtual unsigned groupListIndex() const;\n    /** Sets the group-list index. */\n    virtual void setGroupListIndex(unsigned index);\n    /** Clears the group list index. */\n    virtual void clearGroupListIndex();\n\n    /** Returns the color code. */\n    virtual unsigned colorCode() const;\n    /** Sets thecolor code. */\n    virtual void setColorCode(unsigned cc);\n\n    /** Returns @c true, if the APRS system index is set. */\n    virtual bool hasAPRSIndex() const;\n    /** Returns the APRS system index. */\n    virtual unsigned int aprsIndex() const;\n    /** Sets the APRS system index. */\n    virtual void setAPRSIndex(unsigned int index);\n    /** Resets the APRS system index. */\n    virtual void clearAPRSIndex();\n\n    /** Returns @c true, if the TX contact is set. */\n    virtual bool hasTXContact() const;\n    /** Returns the TX contact index. */\n    virtual unsigned int txContactIndex() const;\n    /** Sets the TX contact index. */\n    virtual void setTXContactIndex(unsigned int index);\n    /** Clears the TX contact index. */\n    virtual void clearTXContact();\n\n    /** Returns the alias transmitted on time slot 1. */\n    virtual OpenGD77ChannelExtension::TalkerAlias aliasTimeSlot1() const;\n    /** Sets the alias transmitted on time slot 1. */\n    virtual void setAliasTimeSlot1(OpenGD77ChannelExtension::TalkerAlias alias);\n    /** Returns the alias transmitted on time slot 2. */\n    virtual OpenGD77ChannelExtension::TalkerAlias aliasTimeSlot2() const;\n    /** Sets the alias transmitted on time slot 2. */\n    virtual void setAliasTimeSlot2(OpenGD77ChannelExtension::TalkerAlias alias);\n\n    /** Returns the time slot of the channel. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot of the channel. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns the bandwidth. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the bandwidth. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns @c true if RX only is enabled. */\n    virtual bool rxOnly() const;\n    /** Enables/disables RX only. */\n    virtual void enableRXOnly(bool enable);\n\n    /** Returns @c true if channel is skipped in a scan. */\n    virtual bool skipScan() const;\n    /** Enables/disables skipping in scan. */\n    virtual void enableSkipScan(bool enable);\n    /** Returns @c true if channel is skipped in zone scan. */\n    virtual bool skipZoneScan() const;\n    /** Enables/disables skipping in zone scan. */\n    virtual void enableSkipZoneScan(bool enable);\n\n    /** Returns @c true if VOX is enabled. */\n    virtual bool vox() const;\n    /** Enables/disables VOX. */\n    virtual void enableVOX(bool enable);\n\n    /** Returns the squelch mode*/\n    virtual SquelchMode squelchMode() const;\n    /** Returns the squelch level. */\n    virtual unsigned int squelchLevel() const;\n    /** Set the squelch level and mode. Level is ignored, if mode is not normal.*/\n    virtual void setSquelch(SquelchMode mode, unsigned int level);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool link(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual bool encode(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      /** The maximum length of the name. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the channel element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int rxFrequency() { return 0x0010; }\n      static constexpr unsigned int txFrequency() { return 0x0014; }\n      static constexpr unsigned int mode() { return 0x0018; }\n      static constexpr unsigned int power() { return 0x0019; }\n      static constexpr unsigned int latitude0() { return 0x001a; }\n      static constexpr unsigned int txTimeout() { return 0x001b; }\n      static constexpr unsigned int latitude1() { return 0x001c; }\n      static constexpr unsigned int latitude2() { return 0x001d; }\n      static constexpr unsigned int longitude0() { return 0x001e; }\n      static constexpr unsigned int longitude1() { return 0x001f; }\n      static constexpr unsigned int rxTone() { return 0x0020; }\n      static constexpr unsigned int txTone() { return 0x0022; }\n      static constexpr unsigned int longitude2() { return 0x0024; }\n      static constexpr Bit simplex() { return {0x0026, 2}; }\n      static constexpr Bit useFixedLocation() { return {0x0026, 3}; }\n      static constexpr Bit disablePowerSave() { return {0x0026, 5}; }\n      static constexpr Bit disableBeep() { return {0x0026, 6}; }\n      static constexpr Bit overrideDMRID() { return {0x0026, 7}; }\n      static constexpr unsigned int dmrId() { return 0x0027; }\n      static constexpr unsigned int groupList() { return 0x002b; }\n      static constexpr unsigned int colorCode() { return 0x002c; }\n      static constexpr unsigned int aprsIndex() { return 0x002d; }\n      static constexpr unsigned int txContact() { return 0x002e; }\n      static constexpr Bit aliasTimeSlot2() { return { 0x030, 2}; }\n      static constexpr Bit aliasTimeSlot1() { return { 0x030, 0}; }\n      static constexpr Bit timeSlot() { return {0x0031, 6}; }\n      static constexpr Bit bandwidth() { return {0x0033, 1}; }\n      static constexpr Bit enableMonitor() { return {0x0033, 3}; }\n      static constexpr Bit rxOnly() { return {0x0033, 2}; }\n      static constexpr Bit skipScan() { return {0x0033, 4}; }\n      static constexpr Bit skipZoneScan() { return {0x0033, 5}; }\n      static constexpr Bit vox() { return {0x0033, 6}; }\n      static constexpr unsigned int squelch() { return 0x0037; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the base for channel banks in Radioddity codeplugs.\n   *\n   * Memory layout of a channel bank:\n   * @verbinclude radioddity_channelbank.txt */\n  class ChannelBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelBankElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelBankElement();\n\n    /** The size of the channel bank. */\n    static constexpr unsigned int size() { return 0x1c10; }\n\n    /** Clears the bank. */\n    void clear();\n\n    /** Returns @c true if the channel is enabled. */\n    virtual bool isEnabled(unsigned idx) const ;\n    /** Enable/disable a channel in the bank. */\n    virtual void enable(unsigned idx, bool enabled);\n    /** Returns a pointer to the channel at the given index. */\n    virtual uint8_t *get(unsigned idx) const;\n    /** Returns the n-th channel. */\n    ChannelElement channel(unsigned int n);\n\n  public:\n    /** Some limits for the channel bank. */\n    struct Limit {\n      /** The maximum number of channels. */\n      static constexpr unsigned int channelCount() { return 128; }\n    };\n\n  protected:\n    /** Some internal offset within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bitmask() { return 0x0000; }\n      static constexpr unsigned int channels() { return 0x0010; }\n      /// @endcond\n    };\n  };\n\n\n  /** VFO Channel representation within the binary codeplug.\n   *\n   * Each channel requires 0x38b:\n   * @verbinclude radioddity_vfochannel.txt */\n  class VFOChannelElement: public ChannelElement\n  {\n  public:\n    /** Possible offset frequency modes. */\n    enum class OffsetMode {\n      Off = 0,       ///< Disables transmit frequency offset.\n      Positive = 1,  ///< Transmit offset frequency is positive (TX above RX).\n      Negative = 2   ///< Transmit offset frequency is negative (TX below RX).\n    };\n\n    /** Possible tuning step sizes. */\n    enum class StepSize {\n      SS2_5kHz = 0,  ///< 2.5kHz\n      SS5kHz = 1,    ///< 5kHz\n      SS6_25kHz = 2, ///< 6.25kHz\n      SS10kHz = 3,   ///< 10kHz\n      SS12_5kHz = 4, ///< 12.5kHz\n      SS20kHz = 5,   ///< 20kHz\n      SS30kHz = 6,   ///< 30kHz\n      SS50kHz = 7    ///< 50kHz\n    };\n\n  protected:\n    /** Hidden constructor. */\n    VFOChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit VFOChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The VFO channel has no name. */\n    QString name() const;\n    /** The VFO channel has no name. */\n    void setName(const QString &name);\n\n    /** Returns the tuning step-size in kHz. */\n    virtual double stepSize() const;\n    /** Sets the tuning step-size in kHz. */\n    virtual void setStepSize(double kHz);\n\n    /** Returns the transmit frequency offset mode. */\n    virtual OffsetMode offsetMode() const;\n    /** Returns the transmit frequency offset. */\n    virtual double txOffset() const;\n    /** Sets the transmit frequency offset in MHz. */\n    virtual void setTXOffset(double f);\n    /** Sets the transmit frequency offset mode. */\n    virtual void setOffsetMode(OffsetMode mode);\n\n  protected:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public ChannelElement::Offset {\n      static constexpr Bit stepSize()                         { return {0x0036, 4} ; }\n      static constexpr Bit offsetMode()                       { return {0x0036, 2} ; }\n      static constexpr unsigned int txOffset()                { return 0x0034; }\n    };\n    /// @endcond\n  };\n\n\n  /** Encodes the settings element for all OpenGD77 codeplugs. */\n  class GeneralSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0090; }\n\n    void clear();\n\n    /** Returns the UHF minimum frequency. */\n    virtual Frequency uhfMinFrequency() const;\n    /** Sets the UHF minimum frequency. */\n    virtual void setUHFMinFrequency(const Frequency &f);\n    /** Returns the UHF maximum frequency. */\n    virtual Frequency uhfMaxFrequency() const;\n    /** Sets the UHF maximum frequency. */\n    virtual void setUHFMaxFrequency(const Frequency &f);\n\n    /** Returns the VHF minimum frequency. */\n    virtual Frequency vhfMinFrequency() const;\n    /** Sets the VHF minimum frequency. */\n    virtual void setVHFMinFrequency(const Frequency &f);\n    /** Returns the VHF maximum frequency. */\n    virtual Frequency vhfMaxFrequency() const;\n    /** Sets the VHF maximum frequency. */\n    virtual void setVHFMaxFrequency(const Frequency &f);\n\n    /** Returns the radio callsign. */\n    virtual QString call() const;\n    /** Sets the radio callsign. */\n    virtual void setCall(const QString &call);\n\n    /** Returns the DMR ID. */\n    virtual unsigned int radioId() const;\n    /** Sets the DMR ID. */\n    virtual void setRadioId(unsigned int id);\n\n    /** Encodes the settings. */\n    virtual bool encode(const Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Decodes the settings. */\n    virtual bool decode(const Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: public Element::Limit {\n      /** The maximum call length. */\n      static constexpr unsigned int callLength() { return 8; }\n    };\n\n  protected:\n    /** Some internal offset within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int uhfMinFrequency() { return 0x0000; }\n      static constexpr unsigned int uhfMaxFrequency() { return 0x0002; }\n      static constexpr unsigned int vhfMinFrequency() { return 0x0004; }\n      static constexpr unsigned int vhfMaxFrequency() { return 0x0006; }\n      static constexpr unsigned int call()            { return 0x0060; }\n      static constexpr unsigned int dmrId()           { return 0x0068; }\n      /// @endcond\n    };\n  };\n\n\n  /** APRS system for OpenGD77 devices. */\n  class APRSSettingsElement: public Element\n  {\n  public:\n    /** Possible APRS baud rates. */\n    enum class BaudRate {\n      Baud300 = 1, Baud1200 = 0\n    };\n\n    /** Possible position precisions. */\n    enum class PositionPrecision {\n      Max = 0,\n      Mask1_8sec = 1,\n      Mask3_6sec = 2,\n      Mask18sec = 3,\n      Mask36sec = 4,\n      Mask3min = 5,\n      Mask6min = 6,\n      Mask30min = 7\n    };\n\n  public:\n    /** Constructor from pointer. */\n    explicit APRSSettingsElement(uint8_t *ptr);\n\n  protected:\n    /** Hidden constructor. */\n    APRSSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** The size of the channel bank. */\n    static constexpr unsigned int size() { return 0x40; }\n\n    /** Clears the bank. */\n    void clear();\n\n    /** Returns @c true, if the system is valid. */\n    virtual bool isValid() const;\n\n    /** Returns the name of the system. */\n    virtual QString name() const;\n    /** Sets the name of the system. */\n    virtual void setName(const QString &name);\n\n    /** Returns the source SSID. */\n    virtual unsigned int sourceSSID() const;\n    /** Sets the source SSID. */\n    virtual void setSourceSSID(unsigned int ssid);\n\n    /** Returns @c true, if a fixed position is send. */\n    virtual bool fixedPositionEnabled() const;\n    /** Returns the fixed position. */\n    virtual QGeoCoordinate fixedPosition() const;\n    /** Sets the fixed position. */\n    virtual void setFixedPosition(const QGeoCoordinate &coor);\n    /** Resets the fixed position. */\n    virtual void enableFixedPosition(bool enable);\n\n    /** Returns the posiiton reporting precision. */\n    virtual PositionPrecision positionPrecision() const;\n    /** Sets the position reporting precision in degrees. */\n    virtual void setPositionPrecision(PositionPrecision prec);\n\n    /** Returns @c true, if the first via node is set. */\n    virtual bool hasVia1() const;\n    /** Returns the first via node call. */\n    virtual QString via1Call() const;\n    /** Returns the first via node ssid. */\n    virtual unsigned int via1SSID() const;\n    /** Sets the first via node. */\n    virtual void setVia1(const QString &call, unsigned int ssid);\n    /** Clears the first via node. */\n    virtual void clearVia1();\n\n    /** Returns @c true, if the second via node is set. */\n    virtual bool hasVia2() const;\n    /** Returns the second via node call. */\n    virtual QString via2Call() const;\n    /** Returns the second via node ssid. */\n    virtual unsigned int via2SSID() const;\n    /** Sets the second via node. */\n    virtual void setVia2(const QString &call, unsigned int ssid);\n    /** Clears the second via node. */\n    virtual void clearVia2();\n\n    /** Returns the icon. */\n    virtual FMAPRSSystem::Icon icon() const;\n    /** Sets the icon. */\n    virtual void setIcon(FMAPRSSystem::Icon icon);\n\n    /** Returns the comment text. */\n    virtual QString comment() const;\n    /** Sets the comment text. */\n    virtual void setComment(const QString &text);\n\n    /** Returns the baud-rate. */\n    virtual BaudRate baudRate() const;\n    /** Sets the baud rate. */\n    virtual void setBaudRate(BaudRate rate);\n\n    Frequency fmFrequency() const;\n    void setFMFrequency(Frequency f);\n\n    /** Encodes the APRS settings. */\n    virtual bool encode(const FMAPRSSystem *system, const Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes some APRS settings. */\n    virtual FMAPRSSystem *decode(const Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the ARPS settings. */\n    virtual bool link(FMAPRSSystem *system, const Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit: public Element::Limit {\n      /** The maximum name length in chars. */\n      static constexpr unsigned int nameLength() { return 8; }\n      /** The maximum comment length in chars. */\n      static constexpr unsigned int commentLength() { return 23; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset\n    {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int sourceSSID() { return 0x0008; }\n      static constexpr unsigned int latitude() { return 0x0009; }\n      static constexpr unsigned int longitude() { return 0x000c; }\n      static constexpr unsigned int via1Call() { return 0x000f; }\n      static constexpr unsigned int via1SSID() { return 0x0015; }\n      static constexpr unsigned int via2Call() { return 0x0016; }\n      static constexpr unsigned int via2SSID() { return 0x001c; }\n      static constexpr unsigned int iconTable() { return 0x001d; }\n      static constexpr unsigned int iconIndex() { return 0x001e; }\n      static constexpr unsigned int comment() { return 0x001f; }\n      static constexpr unsigned int fmFrequency() { return 0x0037; }\n      static constexpr Bit positionPrecision() { return { 0x003d, 4}; }\n      static constexpr Bit transmitQSY() { return { 0x003d, 2}; }\n      static constexpr Bit useFixedPosition() { return { 0x003d, 1}; }\n      static constexpr Bit baudRate() { return { 0x003d, 0}; }\n      static constexpr unsigned int unknownBytes() { return 0x003e; }\n      /// @endcond\n    };\n  };\n\n\n  /** APRS System bank. */\n  class APRSSettingsBankElement: public Element\n  {\n  public:\n    /** Constructor from pointer. */\n    explicit APRSSettingsBankElement(uint8_t *ptr);\n\n  protected:\n    /** Hidden constructor. */\n    APRSSettingsBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** The size of the channel bank. */\n    static constexpr unsigned int size() { return 0x40; }\n\n    /** Clears the bank. */\n    void clear();\n\n    /** Returns the n-th APRS system. */\n    APRSSettingsElement system(unsigned int idx) const;\n\n    /** Encodes all FM APRS systems. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes all FM APRS systems. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links all FM APRS systems. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the bank. */\n    struct Limit: public Element::Limit {\n      /** The total number of APRS systems. */\n      static constexpr unsigned int systems() { return 8; }\n    };\n\n  public:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int systems() { return 0x0000; }\n      static constexpr unsigned int betweenSystems() { return APRSSettingsElement::size(); }\n    };\n    /// @endcond\n  };\n\n\n  /** DTMF contact element.\n   * Just a name and DTMF number. */\n  class DTMFContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit DTMFContactElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the name. */\n    virtual QString name() const;\n    /** Sets the name. */\n    virtual void setName(const QString &name);\n\n    /** Returns the DTMF number. */\n    virtual QString number() const;\n    /** Sets the DTMF number. */\n    virtual void setNumber(const QString &number);\n\n    /** Encodes a number. */\n    virtual bool encode(const DTMFContact *contact, const Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes a number. */\n    virtual DTMFContact *decode(const Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. Ü*/\n    struct Limit: public Element::Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** The maximum length of the number. */\n      static constexpr unsigned int numberLength() { return 16; }\n    };\n\n  protected:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int number() { return 0x0010; }\n    };\n    /// @endcond\n  };\n\n\n  /** DTMF contact bank. */\n  class DTMFContactBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFContactBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    DTMFContactBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return Limit::contacts()*DTMFContactElement::size(); }\n\n    void clear();\n\n    /** Returns the n-th DTMF contact. */\n    DTMFContactElement contact(unsigned int n) const;\n\n    /** Encodes all DTMF contacts. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes all DTMF contacts. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the bank. */\n    struct Limit: public Element::Limit {\n      /** The total number of contacts. */\n      static constexpr unsigned int contacts() { return 64; }\n    };\n\n  public:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int contacts() { return 0x0000; }\n      static constexpr unsigned int betweenContacts() { return DTMFContactElement::size(); }\n    };\n    /// @endcond\n  };\n\n\n  /** Implements the base class of boot settings for all OpenGD77 codeplugs. */\n  class BootSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BootSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit BootSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~BootSettingsElement();\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns @c true if the text is shown on boot, other wise an image is shown. */\n    virtual bool bootText() const;\n    /** Enables/disables boot text. */\n    virtual void enableBootText(bool enable);\n\n    /** Returns @c true if the boot password is enabled. */\n    virtual bool bootPasswordEnabled() const;\n    /** Returns the boot password (6 digit). */\n    virtual unsigned bootPassword() const;\n    /** Sets the boot password (6 digit). */\n    virtual void setBootPassword(unsigned passwd);\n    /** Clear boot password. */\n    virtual void clearBootPassword();\n\n    /** Returns the first line. */\n    virtual QString line1() const;\n    /** Sets the first line. */\n    virtual void setLine1(const QString &text);\n    /** Returns the Second line. */\n    virtual QString line2() const;\n    /** Sets the second line. */\n    virtual void setLine2(const QString &text);\n\n    /** Encodes boot text settings from configuration. */\n    virtual bool encode(const Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Updates the configuration with the boot text settings. */\n    virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: public Element::Limit {\n      /** The total number of contacts. */\n      static constexpr unsigned int lineLength() { return 16; }\n    };\n\n  public:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int bootText() { return 0x0000; }\n      static constexpr unsigned int bootPasswdEnable() { return 0x0001; }\n      static constexpr unsigned int bootPasswd() { return 0x000c; }\n      static constexpr unsigned int line1() { return 0x0028; }\n      static constexpr unsigned int line2() { return 0x0038; }\n    };\n    /// @endcond\n  };\n\n\n  /** Represents a zone within OpenGD77 codeplugs. */\n  class ZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ZoneElement(uint8_t *ptr);\n    virtual ~ZoneElement();\n\n    /** The size of the zone element. */\n    static constexpr unsigned int size() { return 0x00b0; }\n\n    /** Resets the zone. */\n    void clear();\n    /** Returns @c true if the zone is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if a member is stored at the given index. */\n    virtual bool hasMember(unsigned n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned member(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMember(unsigned n, unsigned idx);\n    /** Clears the n-th member index. */\n    virtual void clearMember(unsigned n);\n\n    /** Resets this codeplug zone representation from the given generic @c Zone object. */\n    virtual bool encode(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Constructs a generic @c Zone object from this codeplug zone. */\n    virtual Zone *decode(const Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed @c Zone object to the rest of the configuration. That is\n     * linking to the referred channels. */\n    virtual bool link(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  public:\n    /** Some limits for zone elements. */\n    struct Limit: public Element::Limit {\n      /** The maximum length of the zone name. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount() { return 80; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()  { return 0x0000; }\n      static constexpr unsigned int channels() { return 0x0010; }\n      static constexpr unsigned int betweenChannels() { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the base class for all zone banks of OpenGD77 codeplugs. */\n  class ZoneBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ZoneBankElement(uint8_t *ptr);\n    /** Destructor. */\n    ~ZoneBankElement();\n\n    /** The size of the zone element. */\n    static constexpr unsigned int size() {\n      return 0x0020 + Limit::zoneCount()*ZoneElement::size();\n    }\n\n    /** Resets the bank. */\n    void clear();\n\n    /** Returns @c true if the zone is enabled. */\n    virtual bool isEnabled(unsigned idx) const ;\n    /** Enable/disable a zone in the bank. */\n    virtual void enable(unsigned idx, bool enabled);\n\n    /** Returns the n-th zone. */\n    ZoneElement zone(unsigned int n);\n\n    /** Encodes all zones. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes all zones. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links all zones. */\n    virtual bool link(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the zone bank. */\n    struct Limit: public Element::Limit {\n      /** The maximum number of zones in this bank. */\n      static constexpr unsigned int zoneCount() { return 68; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bitmap() { return 0x0000; }\n      static constexpr unsigned int zones()  { return 0x0020; }\n      static constexpr unsigned int betweenZones() { return ZoneElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements digital contacts in OpenGD77 codeplugs. */\n  class ContactElement: public Element\n  {\n  public:\n    /** Possible values for the time-slot override option.\n     * Encoded values are correct for firmware 2022-02-28 (0118581D) to 2025-03-23 (1bd23ea). */\n    enum class TimeSlotOverride {\n      None = 0x01,                  ///< Do not override time-slot of channel.\n      TS1  = 0x00,                  ///< Force time-slot to TS1.\n      TS2  = 0x02                   ///< Force time-slot to TS2.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ContactElement();\n\n    /** The size of the contact element. */\n    static constexpr unsigned int size() { return 0x0018; }\n\n    /** Resets the contact. */\n    void clear();\n\n    /** Returns @c true if the contact is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString name);\n\n    /** Returns the DMR number of the contact. */\n    virtual unsigned number() const;\n    /** Sets the DMR number of the contact. */\n    virtual void setNumber(unsigned id);\n\n    /** Returns the call type. */\n    virtual DMRContact::Type type() const;\n    /** Sets the call type. */\n    virtual void setType(DMRContact::Type type);\n\n    /** Returns the time slot override of the contact. */\n    virtual TimeSlotOverride timeSlotOverride() const;\n    /** Sets the time slot override. */\n    virtual void setTimeSlotOverride(TimeSlotOverride ts);\n\n    /** Constructs a @c DigitalContact instance from this codeplug contact. */\n    virtual DMRContact *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug contact from the given @c DigitalContact. */\n    virtual bool encode(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the contact. */\n    struct Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int number() { return 0x0010; }\n      static constexpr unsigned int type() { return 0x0014; }\n      static constexpr unsigned int timeSlotOverride() { return 0x0017; }\n      /// @endcond\n    };\n  };\n\n\n  /** Encodes the contact bank. */\n  class ContactBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactBankElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() {\n      return Limit::contactCount() * ContactElement::size();\n    }\n\n    /** Resets the contact. */\n    void clear();\n\n    /** Returns the i-th contact element. */\n    ContactElement contact(unsigned int idx) const;\n\n    /** Encodes all DMR contacts. */\n    virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Decodes all DMR contacts. */\n    virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Maximum number of contacts. */\n      static constexpr unsigned int contactCount() { return 1024; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contacts() { return 0x0000; }\n      static constexpr unsigned int betweenContacts() { return ContactElement::size(); }\n      // @endcond\n    };\n  };\n\n\n  /** Encodes a group list for all OpenGD77 codeplugs. */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0050; }\n\n    /** Clears the group list. */\n    void clear();\n\n    /** Returns the name of the group list. */\n    virtual QString name() const;\n    /** Sets the name of the group list. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true, if the i-th contact is set. */\n    virtual bool hasContactIndex(unsigned int i) const;\n    /** Returns the i-th contact index. */\n    virtual unsigned int contactIndex(unsigned int i) const;\n    /** Sets the i-th contact index. */\n    virtual void setContactIndex(unsigned int i, unsigned int contactIdx);\n    /** Clears the i-th contact index. */\n    virtual void clearContactIndex(unsigned int i);\n\n    /** Encodes group list element. */\n    virtual bool encode(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes group list element. */\n    virtual RXGroupList *decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links the group list element. */\n    virtual bool link(RXGroupList *lst, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 15; }\n      /** Maximum number of contacts. */\n      static constexpr unsigned int contactCount() { return 32; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int contacts() { return 0x0010; }\n      static constexpr unsigned int betweenContacts() { return 0x0002; }\n      // @endcond\n    };\n  };\n\n\n  /** Encodes a group list bank for all OpenGD77 codeplugs. */\n  class GroupListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListBankElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x1840; }\n\n    /** Clears the group list bank. */\n    void clear();\n\n    /** Returns @c true, if the i-th group list is encoded. */\n    virtual bool hasGroupList(unsigned int i) const;\n    /** Returns the number of contacts in the given group list. */\n    virtual unsigned int groupListContactCount(unsigned int i) const;\n    /** Sets the number of contacts in the given group list. */\n    virtual void setGroupListContactCount(unsigned int i, unsigned int count);\n    /** Returns the i-th group list. */\n    virtual GroupListElement groupList(unsigned int i) const;\n    /** Clears the i-th group list. */\n    virtual void clearGroupList(unsigned int i);\n\n    /** Encodes all group lists. */\n    virtual bool encode(Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Decodes all group lists. */\n    virtual bool decode(Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Links all group lists. */\n    virtual bool link(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Maximum number of group lists. */\n      static constexpr unsigned int groupListCount() { return 76; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int length() { return 0x0000; }\n      static constexpr unsigned int groupLists() { return 0x0080; }\n      static constexpr unsigned int betweenGroupLists() { return GroupListElement::size(); }\n      // @endcond\n    };\n  };\n\n\n  /** Encodes a satellite for the  OpenGD77 devices.\n   * That is a set of orbital elements and transponder information. */\n  class SatelliteElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    SatelliteElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    SatelliteElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0064; }\n\n    void clear();\n\n    /** Sets the name of the element. */\n    virtual void setName(const QString &name);\n\n    /** Sets the epoch. */\n    virtual void setEpoch(const ::OrbitalElement::Epoch &epoch);\n    /** Sets the first derivative of mean motion. */\n    virtual void setMeanMotion(double mm);\n    /** Sets the first derivative of mean motion. */\n    virtual void setMeanMotionDerivative(double dmm);\n    /** Sets the inclination. */\n    virtual void setInclination(double incl);\n    /** Right ascension of the ascending node. */\n    virtual void setAscension(double asc);\n    /** Sets eccentricity. */\n    virtual void setEccentricity(double ecc);\n    /** Sets argument of perigee. */\n    virtual void setPerigee(double arg);\n    /** Set the mean anomaly. */\n    virtual void setMeanAnomaly(double ma);\n    /** Sets the revolution number at epoch. */\n    virtual void setRevolutionNumber(unsigned int num);\n\n    /** Sets the downlink frequency. */\n    void setFMDownlink(const Frequency &f);\n    /** Sets the uplink frequency. */\n    void setFMUplink(const Frequency &f);\n    /** Sets the CTCSS tone. */\n    void setCTCSS(const SelectiveCall &call);\n    /** Sets the APRS downlink frequency. */\n    void setAPRSDownlink(const Frequency &f);\n    /** Sets the APRS uplink frequency. */\n    void setAPRSUplink(const Frequency &f);\n\n    /** Sets the beacon frequency. */\n    void setBeacon(const Frequency &f);\n\n    /** Sets the APRS path. */\n    void setAPRSPath(const QString &path);\n\n    /** Encodes a satellite. */\n    virtual bool encode(const Satellite &sat, const ErrorStack &err = ErrorStack());\n\n  protected:\n    /** Writes a fixed point value as a BCD number. Using 0-9 as digits, ah as decimal dot and bh\n     * as blank.\n     * @param offset Specifies, where to write the fixed point value.\n     * @param value The value to write.\n     * @param sign If @c true, a sign is written.\n     * @param dec The number of digits in the integer part.\n     * @param frac The number of digits in the fractional part.\n     */\n    void writeFixedPoint(const Offset::Bit &offset, double value, bool sign, unsigned int dec, unsigned int frac);\n    /** Writes a fixed point value as a BCD number. Using 0-9 as digits and bh as blank. In contrast\n     * to @c writeFixedPoint, this function expects no integer part.\n     * @param offset Specifies, where to write the fixed point value.\n     * @param value The value to write.\n     * @param sign If @c true, a sign is written.\n     * @param frac The number of digits in the fractional part.\n     */\n    void writeFractional(const Offset::Bit &offset, double value, bool sign, unsigned int frac);\n    /** Write a fixed digit integer value. */\n    void writeInteger(const Offset::Bit &offset, int value, bool sign, unsigned dec);\n    /** Writes a single digit at the given offset. */\n    void writeDigit(const Offset::Bit &offset, uint8_t digit);\n\n  public:\n    /** Some limits for the zone bank. */\n    struct Limit: public Element::Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength() { return 8; }\n      /** Maximum length of the APRS path. */\n      static constexpr unsigned int pathLength() { return 24; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()         { return 0x0000; }\n      static constexpr Bit epochYear()             { return {0x0008, 4}; }\n      static constexpr Bit epochJulienDay()        { return {0x0009, 4}; }\n      static constexpr Bit meanMotionDerivative()  { return {0x000f, 4}; }\n      static constexpr Bit inclination()           { return {0x0014, 4}; }\n      static constexpr Bit ascension()             { return {0x0018, 4}; }\n      static constexpr Bit eccentricity()          { return {0x001c, 4}; }\n      static constexpr Bit perigee()               { return {0x001f, 0}; }\n      static constexpr Bit meanAnomaly()           { return {0x0023, 0}; }\n      static constexpr Bit meanMotion()            { return {0x0027, 0}; }\n      static constexpr Bit revolutionNumber()      { return {0x002d, 4}; }\n      static constexpr unsigned int fmDownlink()   { return 0x0030; }\n      static constexpr unsigned int fmUplink()     { return 0x0034; }\n      static constexpr unsigned int ctcss()        { return 0x0038; }\n      static constexpr unsigned int aprsDownlink() { return 0x003c; }\n      static constexpr unsigned int aprsUplink()   { return 0x0040; }\n      static constexpr unsigned int beacon()       { return 0x0044; }\n      static constexpr unsigned int aprsPath()     { return 0x004c; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the satellite config bank. Holding all satellites to track. */\n  class SatelliteBankElement: Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    SatelliteBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    SatelliteBankElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size()  { return 0x09e0; }\n    /** The magic number of the element. */\n    static constexpr unsigned int magic() { return 0x0003; }\n\n    void clear();\n\n    /** Returns the i-th satellite. */\n    SatelliteElement satellite(unsigned int idx);\n    /** Encodes the given satellite database. */\n    bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the satellite config. */\n    struct Limit {\n      /** The maximum number of satellites. */\n      static constexpr unsigned int satellites() { return 25; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int blockId()           { return 0x0000; }\n      static constexpr unsigned int segmentSize()       { return 0x0004; }\n      static constexpr unsigned int satellites()        { return 0x0008; }\n      static constexpr unsigned int betweenSatellites() { return SatelliteElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  class NoteElement: public Element\n  {\n  public:\n    explicit NoteElement(uint8_t *ptr);\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x0002; }\n\n    void clear() override;\n    bool isValid() const override;\n\n    /** Retunrs @c true if the note is a pause. */\n    bool isPause() const;\n    /** Returns the pitch in Hz. */\n    double frequency() const;\n    /** Sets the pitch in Hz. */\n    void setFrequency(double pitch);\n    /** Set note as pause. */\n    void setPause();\n\n    /** Returns the duration in ms. */\n    unsigned int duration() const;\n    /** Sets the interval. */\n    void setDuration(unsigned int ms);\n\n  protected:\n    /** Internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int pitch()    { return 0x0000; }\n      static constexpr unsigned int duration() { return 0x0001; }\n      /// @endcond\n    };\n\n    static constexpr double _lut[] = {\n      0110.00, 0116.54, 0123.47,\n      0130.81, 0138.59, 0146.83, 0155.56, 0164.81, 0174.61, 0185.00, 0196.00, 0207.65, 0220.00, 0233.08, 0246.94,\n      0261.62, 0277.18, 0293.66, 0311.13, 0329.63, 0349.23, 0369.99, 0392.00, 0415.30, 0440.00, 0466.16, 0493.88,\n      0523.25, 0554.37, 0587.33, 0622.25, 0659.25, 0698.46, 0739.99, 0783.99, 0830.61, 0880.00, 0932.33, 0987.77,\n      1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91\n    };\n  };\n\n  /** Encodes the boot melody. */\n  class BootMelodyElement: public Element\n  {\n  public:\n    /** Constructor. */\n    BootMelodyElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0208; }\n    /** The magic number of the element. */\n    static constexpr unsigned int magic() { return 0x0002; }\n\n    void clear();\n\n    /** Encodes the given melody. */\n    virtual bool encode(Context &ctx, const Melody *melody, const ErrorStack &err=ErrorStack());\n    /** Decodes the boot melody. */\n    virtual bool decode(Context &ctx, Melody *melody, const ErrorStack &err=ErrorStack()) const;\n\n  public:\n    /** Some limits for the melody config. */\n    struct Limit {\n      /** The maximum number of notes/pauses. */\n      static constexpr unsigned int notes() { return 256; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int blockId()           { return 0x0000; }\n      static constexpr unsigned int segmentSize()       { return 0x0004; }\n      static constexpr unsigned int notes()             { return 0x0008; }\n      static constexpr unsigned int betweenNotes() { return NoteElement::size(); }\n      /// @endcond\n    };\n  };\n\n\n  /** Encodes some additional settings for OpenGD77 based radios. These\n   * settings include boot image, boot melody, satellite orbital elements and themes, if the radio\n   * supports it. */\n  class AdditionalSettingsElement: public Element\n  {\n  public:\n    enum Settings {\n      BootImage         = 1,\n      BootMelody        = 2,\n      SatelliteOrbitals = 3,\n      LightTheme        = 4,\n      DarkTheme         = 5\n    };\n\n  protected:\n    /** Hidden constructor. */\n    AdditionalSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    AdditionalSettingsElement(uint8_t *ptr);\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x11a0; }\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the magic string. */\n    virtual QString magic() const;\n    /** Returns the version number. */\n    virtual unsigned int version() const;\n\n    /** Returns @c true, if the given settings is stored. */\n    virtual bool hasSettings(Settings set) const;\n    /** Returns statellite settings bank, if present. If not, a new empty setting is returned. */\n    virtual SatelliteBankElement satellites() const;\n    /** Returns boot melody settings, if present. If not, a new empty setting is returned. */\n    virtual BootMelodyElement bootMelody() const;\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Size of magic string. */\n      static constexpr unsigned int magicStringLength() { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int magicString()    { return 0x0000; }\n      static constexpr unsigned int versionNumber()  { return 0x0008; }\n      static constexpr unsigned int blocks()         { return 0x000c; }\n      // @endcond\n    };\n  };\n\n\nprotected:\n  /** Default hidden constructor. */\n  explicit OpenGD77BaseCodeplug(QObject *parent = nullptr);\n\npublic:\n  /** Clears and resets the complete codeplug to some default values. */\n  virtual void clear();\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n  bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n\n  Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n  bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Decodes the binary codeplug and stores its content in the given generic configuration using\n   * the given context. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes the given generic configuration as a binary codeplug using the given context. */\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Clears the general settings in the codeplug. */\n  virtual void clearGeneralSettings() = 0;\n  /** Updates the general settings from the given configuration. */\n  virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Updates the given configuration from the general settings. */\n  virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the DTMF settings. */\n  virtual void clearDTMFSettings() = 0;\n  /** Encodes DTMF settings. */\n  virtual bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the DTMF settings. */\n  virtual bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the APRS settings. */\n  virtual void clearAPRSSettings() = 0;\n  /** Encodes APRS settings. */\n  virtual bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the APRS settings. */\n  virtual bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links the APRS settings. */\n  virtual bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all DTMF contacts in the codeplug. */\n  virtual void clearDTMFContacts() = 0;\n  /** Encodes all DTMF contacts. */\n  virtual bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds all DTMF contacts to the configuration. */\n  virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clear all channels. */\n  virtual void clearChannels() = 0;\n  /** Encode all channels. */\n  virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds all defined channels to the configuration. */\n  virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all channels. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clear boot settings. */\n  virtual void clearBootSettings() = 0;\n  /** Encodes boot settings. */\n  virtual bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the boot settings. */\n  virtual bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the VFO settings. */\n  virtual void clearVFOSettings() = 0;\n\n  /** Clears all zones. */\n  virtual void clearZones() = 0;\n  /** Encodes zones. */\n  virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds zones to the configuration. */\n  virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all zones within the configuration. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all contacts in the codeplug. */\n  virtual void clearContacts() = 0;\n  /** Encodes all digital contacts in the configuration into the codeplug. */\n  virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a digital contact to the configuration for each one in the codeplug. */\n  virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all group lists. */\n  virtual void clearGroupLists() = 0;\n  /** Encodes all group lists. */\n  virtual bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Creates all group lists. */\n  virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all group lists. */\n  virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n};\n\n#endif // OPENGD77BASE_CODEPLUG_HH\n"
  },
  {
    "path": "lib/opengd77base_satelliteconfig.cc",
    "content": "#include \"opengd77base_satelliteconfig.hh\"\n#include \"satellitedatabase.hh\"\n\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenGD77BaseSatelliteConfig\n * ********************************************************************************************* */\nOpenGD77BaseSatelliteConfig::OpenGD77BaseSatelliteConfig(QObject *parent)\n  : SatelliteConfig{parent}\n{\n  addImage(\"OpenGD77 satellite configuration EEPROM\");\n  addImage(\"OpenGD77 satellite configuration FLASH\");\n}\n"
  },
  {
    "path": "lib/opengd77base_satelliteconfig.hh",
    "content": "#ifndef OPENGD77BASE_SATELLITECONFIG_HH\n#define OPENGD77BASE_SATELLITECONFIG_HH\n\n#include <orbitalelementsdatabase.hh>\n#include <satelliteconfig.hh>\n#include <frequency.hh>\n#include <signaling.hh>\n#include <satellitedatabase.hh>\n#include <codeplug.hh>\n\n\n/** Implements the satellite tracking configuration for the OpenGD77 type radios.\n * @ingroup ogd77 */\nclass OpenGD77BaseSatelliteConfig : public SatelliteConfig\n{\n  Q_OBJECT\n\npublic:\n  /** Possible image types. */\n  enum ImageType { EEPROM = 0, FLASH = 1 };\n\n\npublic:\n  /** Default constructor. */\n  explicit OpenGD77BaseSatelliteConfig(QObject *parent = nullptr);\n\n  /** Size of the image to write. */\n  static constexpr unsigned int size() { return 0x11a0; }\n\n  /** Returns @c true, if the additional settings element is valid, that should contain the\n   * satellite settings. */\n  virtual bool isValid() const = 0;\n\n  /** Initializes and clears the additional settings element. */\n  virtual void initialize() = 0;\n\n  /** Encodes the given satellite database. */\n  virtual bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack()) = 0;\n};\n\n#endif // OPENGD77BASE_SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/openrtx.cc",
    "content": "#include \"openrtx.hh\"\n\n#include \"openrtx_interface.hh\"\n#include \"logger.hh\"\n#include \"config.hh\"\n\n\n#define BSIZE 32\n\nOpenRTX::OpenRTX(OpenRTXInterface *device, QObject *parent)\n  : Radio(parent), _name(\"Open RTX\"), _dev(device), _config(nullptr), _codeplug()\n{\n  if (! connect())\n    return;\n  logDebug() << \"Connected to radio '\" << _name << \"'.\";\n}\n\nOpenRTX::~OpenRTX() {\n  if (_dev && _dev->isOpen())  {\n    logDebug() << \"Closing device.\";\n    _dev->reboot();\n    _dev->close();\n  }\n  if (_dev) {\n    logDebug() << \"Deleting device.\";\n    _dev->deleteLater();\n    _dev = nullptr;\n  }\n}\n\nconst QString &\nOpenRTX::name() const {\n  return _name;\n}\n\nconst Codeplug &\nOpenRTX::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nOpenRTX::codeplug() {\n  return _codeplug;\n}\n\nRadioInfo\nOpenRTX::defaultRadioInfo() {\n  return RadioInfo(\n    RadioInfo::OpenRTX, \"openrtx\", \"OpenRTX\",\n    {OpenRTXInterface::interfaceInfo()});\n}\n\n\nbool\nOpenRTX::startDownload(bool blocking, const ErrorStack &err) {\n  if (StatusIdle != _task) {\n    errMsg(err) << \"Cannot download from radio, radio is not idle.\";\n    return false;\n  }\n\n  _task = StatusDownload;\n\n  if (blocking) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for download\n  start();\n  return true;\n}\n\n\nbool\nOpenRTX::startUpload(Config *config, bool blocking, const Codeplug::Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags)\n\n  logDebug() << \"Start upload to \" << name() << \"...\";\n\n  if (StatusIdle != _task) {\n    errMsg(err) << \"Cannot upload to radio, radio is not idle.\";\n    return false;\n  }\n\n  if (_config)\n    delete _config;\n\n  if (! (_config = config)) {\n    errMsg(err) << \"Cannot upload to radio, no config given.\";\n    return false;\n  }\n  _config->setParent(this);\n\n  _task = StatusUpload;\n  if (blocking) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  // If non-blocking -> move device to this thread\n  if (_dev && _dev->isOpen())\n    _dev->moveToThread(this);\n  // start thread for upload\n  start();\n\n  return true;\n}\n\nbool\nOpenRTX::startUploadCallsignDB(UserDatabase *db, bool blocking,\n                               const CallsignDB::Flags &selection,const ErrorStack &err)\n{\n  Q_UNUSED(db); Q_UNUSED(blocking); Q_UNUSED(selection)\n  errMsg(err) << \"OpenRTX has no call-sign DB implemented.\";\n  return false;\n}\n\n\nvoid\nOpenRTX::run() {\n  if (StatusDownload == _task) {\n    if (! connect()) {\n      emit downloadError(this);\n      return;\n    }\n\n    if (! download()) {\n      _task = StatusError;\n      _dev->read_finish();\n      _dev->reboot();\n      _dev->close();\n      emit downloadError(this);\n      return;\n    }\n\n    _dev->read_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit downloadFinished(this, &_codeplug);\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if (! connect()) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! upload()) {\n      _task = StatusError;\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->write_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    emit uploadError(this);\n    return;\n  }\n}\n\n\nbool\nOpenRTX::connect(const ErrorStack &err) {\n  if (_dev && _dev->isOpen())\n    return true;\n  if (_dev)\n    _dev->deleteLater();\n\n  _dev = new OpenRTXInterface(USBDeviceDescriptor());\n  if (! _dev->isOpen()) {\n    _task = StatusError;\n    errMsg(err) << \"Cannot connect to radio.\";\n    _dev->deleteLater();\n    _dev = nullptr;\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenRTX::download(const ErrorStack &err)\n{\n  emit downloadStarted();\n\n  if (_codeplug.numImages() != 2) {\n    errMsg(err) << \"Cannot download codeplug: Codeplug does not contain two images.\";\n    return false;\n  }\n\n  // Check every segment in the codeplug\n  if (! _codeplug.isAligned(BSIZE)) {\n    errMsg(err) << \"Cannot download codeplug: Codeplug is not aligned with blocksize \"\n                << BSIZE << \".\";\n    return false;\n  }\n\n  size_t totb = _codeplug.memSize();\n\n  if (! _dev->read_start(0, 0, err)) {\n    errMsg(err) << \"Cannot start codeplug download.\";\n    _dev->close();\n    return false;\n  }\n\n  // Then download codeplug\n  size_t bcount = 0;\n  for (int image=0; image<_codeplug.numImages(); image++) {\n    uint32_t bank = 0;\n\n    for (int n=0; n<_codeplug.image(image).numElements(); n++) {\n      unsigned addr = _codeplug.image(image).element(n).address();\n      unsigned size = _codeplug.image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) {\n          errMsg(err) << \"Cannot read block \"<< (b0+b) <<\".\";\n          return false;\n        }\n        QThread::usleep(100);\n        emit downloadProgress(float(bcount*100)/totb);\n      }\n    }\n    _dev->read_finish(err);\n  }\n\n  return true;\n}\n\n\nbool\nOpenRTX::upload(const ErrorStack &err)\n{\n  emit uploadStarted();\n\n  if (_codeplug.numImages() != 2) {\n    errMsg(err) << \"Cannot download codeplug: Codeplug does not contain two images.\";\n    return false;\n  }\n\n  // Check every segment in the codeplug\n  if (! _codeplug.isAligned(BSIZE)) {\n    errMsg(err) << \"Cannot upload code-plug: Codeplug is not aligned with blocksize \"\n                << BSIZE << \".\";\n    return false;\n  }\n\n  size_t totb = _codeplug.memSize();\n\n  if (! _dev->read_start(0, 0, err)) {\n    errMsg(err) << \"Cannot start codeplug download.\";\n    return false;\n  }\n\n  // Then download codeplug\n  size_t bcount = 0;\n  for (int image=0; image<_codeplug.numImages(); image++) {\n    uint32_t bank = 0;\n\n    for (int n=0; n<_codeplug.image(image).numElements(); n++) {\n      unsigned addr = _codeplug.image(image).element(n).address();\n      unsigned size = _codeplug.image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) {\n          errMsg(err) << \"Cannot read block \" << (b0+b) << \".\";\n          return false;\n        }\n        QThread::usleep(100);\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n    }\n    _dev->read_finish(err);\n  }\n\n  // Encode config into codeplug\n  _codeplug.encode(_config, Codeplug::Flags(), err);\n\n  if (! _dev->write_start(0,0, err)) {\n    errMsg(err) << \"Cannot start codeplug upload.\";\n    return false;\n  }\n\n  // Then upload codeplug\n  for (int image=0; image<_codeplug.numImages(); image++) {\n    uint32_t bank = 0;\n\n    for (int n=0; n<_codeplug.image(image).numElements(); n++) {\n      unsigned addr = _codeplug.image(image).element(n).address();\n      unsigned size = _codeplug.image(image).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->write(bank, (b0+b)*BSIZE, _codeplug.data((b0+b)*BSIZE, image), BSIZE, err)) {\n          errMsg(err) << \"Cannot write block \" << (b0+b) << \".\";\n          return false;\n        }\n        QThread::usleep(100);\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n    }\n    _dev->write_finish(err);\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/openrtx.hh",
    "content": "/** @defgroup ortx Open RTX Firmware\n * Implements a radio running the Open RTX firmware.\n * @ingroup dsc */\n\n#ifndef OPENGRTX_HH\n#define OPENGRTX_HH\n\n#include \"radio.hh\"\n//#include \"openrtx_interface.hh\"\n#include \"openrtx_codeplug.hh\"\n\nclass OpenRTXInterface;\n\n/** Implements an USB interface to radios running the Open RTX firmware.\n *\n * @ingroup ortx */\nclass OpenRTX: public Radio\n{\n\tQ_OBJECT\n\npublic:\n\t/** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit OpenRTX(OpenRTXInterface *device=nullptr, QObject *parent=nullptr);\n  virtual ~OpenRTX();\n\n\tconst QString &name() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(bool blocking=false, const ErrorStack &err=ErrorStack());\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, bool blocking=false,\n                   const Codeplug::Flags &flags = Codeplug::Flags(),\n                   const ErrorStack &err=ErrorStack());\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, bool blocking=false,\n                             const CallsignDB::Flags &selection=CallsignDB::Flags(),\n                             const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n\tvoid run();\n\n  /** Connects to the radio, if a radio interface is passed to the constructor, this interface\n   * instance is used. */\n  bool connect(const ErrorStack &err=ErrorStack());\n  /** Implements the actual download process. */\n  bool download(const ErrorStack &err=ErrorStack());\n  /** Implements the actual codeplug upload process. */\n  bool upload(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** The device identifier. */\n\tQString _name;\n  /** The interface to the radio. */\n  OpenRTXInterface *_dev;\n  /** The generic configuration. */\n\tConfig *_config;\n  /** The actual binary codeplug representation. */\n  OpenRTXCodeplug _codeplug;\n};\n\n#endif // OPENGD77_HH\n"
  },
  {
    "path": "lib/openrtx_codeplug.cc",
    "content": "#include \"openrtx_codeplug.hh\"\n#include \"logger.hh\"\n#include \"scanlist.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"config.hh\"\n#include \"config.h\"\n#include \"intermediaterepresentation.hh\"\n#include <QtEndian>\n\n\nQVector<unsigned int> _openrtx_ctcss_tone_table{\n    670, 693, 719, 744, 770, 797, 825, 854, 885, 915, 948, 974, 1000, 1034,\n    1072, 1109, 1148, 1188, 1230, 1273, 1318, 1365, 1413, 1462, 1514, 1567,\n    1598, 1622, 1655, 1679, 1713, 1738, 1773, 1799, 1835, 1862, 1899, 1928,\n    1966, 1995, 2035, 2065, 2107, 2181, 2257, 2291, 2336, 2418, 2503, 2541\n};\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenRTXCodeplug::HeaderElement\n * ********************************************************************************************* */\nOpenRTXCodeplug::HeaderElement::HeaderElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::HeaderElement::HeaderElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0058)\n{\n  // pass...\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::clear() {\n  memset(_data, 0, _size);\n  setUInt64_le(OffsetMagic, MagicNumber);\n  setVersion();\n  setTimestamp();\n}\n\nbool\nOpenRTXCodeplug::HeaderElement::isValid() const {\n  return Codeplug::Element::isValid() && (MagicNumber == getUInt64_le(OffsetMagic));\n}\n\nuint16_t\nOpenRTXCodeplug::HeaderElement::version() const {\n  return getUInt16_le(OffsetVersion);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setVersion() {\n  setUInt16_le(OffsetVersion, SupportedVersion);\n}\n\n\nQString\nOpenRTXCodeplug::HeaderElement::author() const {\n  return readASCII(OffsetAuthor, StringLength, 0);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setAuthor(const QString &name) {\n  writeASCII(OffsetAuthor, name, StringLength, 0);\n}\n\nQString\nOpenRTXCodeplug::HeaderElement::description() const {\n  return readASCII(OffsetDescription, StringLength, 0);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setDescription(const QString description) {\n  writeASCII(OffsetDescription, description, StringLength, 0);\n}\n\n\nQDateTime\nOpenRTXCodeplug::HeaderElement::timestamp() const {\n  return QDateTime::fromSecsSinceEpoch(getUInt64_le(OffsetTimestamp), QTimeZone::utc());\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setTimestamp(const QDateTime timestamp) {\n  setUInt64_le(OffsetTimestamp, timestamp.toUTC().toSecsSinceEpoch());\n}\n\n\nunsigned int\nOpenRTXCodeplug::HeaderElement::contactCount() const {\n  return getUInt16_le(OffsetContactCount);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setContactCount(unsigned int n) {\n  setUInt16_le(OffsetContactCount, n);\n}\n\nunsigned int\nOpenRTXCodeplug::HeaderElement::channelCount() const {\n  return getUInt16_le(OffsetChannelCount);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setChannelCount(unsigned int n) {\n  setUInt16_le(OffsetChannelCount, n);\n}\n\nunsigned int\nOpenRTXCodeplug::HeaderElement::zoneCount() const {\n  return getUInt16_le(OffsetZoneCount);\n}\n\nvoid\nOpenRTXCodeplug::HeaderElement::setZoneCount(unsigned int n) {\n  return setUInt16_le(OffsetZoneCount, n);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenRTXCodeplug::ChannelElement\n * ********************************************************************************************* */\nOpenRTXCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, 0x5a)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::ChannelElement::~ChannelElement() {\n  // pass...\n}\n\nbool\nOpenRTXCodeplug::ChannelElement::isValid() const {\n  return Codeplug::Element::isValid() && (Mode_None != mode());\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::clear() {\n  memset(_data, 0, _size);\n}\n\n\nOpenRTXCodeplug::Mode\nOpenRTXCodeplug::ChannelElement::mode() const {\n  return (OpenRTXCodeplug::Mode)getUInt8(OffsetMode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setMode(Mode mode) {\n  setUInt8(Offsets::OffsetMode, (uint8_t)mode);\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::rxOnly() const {\n  return getBit(OffsetRXOnly, BitRXOnly);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setRXOnly(bool enable) {\n  setBit(OffsetRXOnly, BitRXOnly, enable);\n}\n\n\nOpenRTXCodeplug::ChannelElement::Bandwidth\nOpenRTXCodeplug::ChannelElement::bandwidth() const {\n  return (Bandwidth)getUInt2(OffsetBandwidth, BitBandwidth);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setBandwidth(Bandwidth bw) {\n  setUInt2(OffsetBandwidth, BitBandwidth, bw);\n}\n\n\nfloat\nOpenRTXCodeplug::ChannelElement::power() const {\n  return 10.+0.2*getUInt8(OffsetPower);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setPower(float dBm) {\n  if (dBm < 10)\n    dBm = 10;\n  setUInt8(OffsetPower, (uint8_t)((dBm-10)*5));\n}\n\n\nFrequency\nOpenRTXCodeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(getUInt32_le(OffsetRXFrequency));\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setRXFrequency(Frequency MHz) {\n  setUInt32_le(OffsetRXFrequency, MHz.inHz());\n}\n\nFrequency\nOpenRTXCodeplug::ChannelElement::txFrequency() const {\n  return Frequency::fromHz(getUInt32_le(OffsetTXFrequency));\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setTXFrequency(Frequency MHz) {\n  setUInt32_le(OffsetTXFrequency, MHz.inHz());\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::hasScanListIndex() const {\n  return 0 != scanListIndex();\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(OffsetScanList);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setScanListIndex(unsigned int index) {\n  setUInt8(OffsetScanList, index);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::clearScanListIndex() {\n  setScanListIndex(0);\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::hasGroupListIndex() const {\n  return 0 != groupListIndex();\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(OffsetGroupList);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setGroupListIndex(unsigned int index) {\n  setUInt8(OffsetGroupList, index);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::clearGroupListIndex() {\n  setGroupListIndex(0);\n}\n\n\nQString\nOpenRTXCodeplug::ChannelElement::name() const {\n  return readASCII(OffsetName, StringLength, 0x00);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setName(const QString &name) {\n  writeASCII(OffsetName, name, StringLength, 0x00);\n}\n\nQString\nOpenRTXCodeplug::ChannelElement::description() const {\n  return readASCII(OffsetDescription, StringLength, 0x00);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setDescription(const QString &description) {\n  writeASCII(OffsetDescription, description, StringLength, 0x00);\n}\n\n\nfloat\nOpenRTXCodeplug::ChannelElement::latitude() const {\n  return getInt8(OffsetChLatInt) + ((float)getUInt16_le(OffsetChLatDec))/65536;\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setLatitude(float lat) {\n  setInt8(OffsetChLatInt, (int)lat);\n  setUInt16_le(OffsetChLatDec, std::abs(lat-((int)lat))*65536);\n}\n\nfloat\nOpenRTXCodeplug::ChannelElement::longitude() const {\n  return getInt8(OffsetChLonInt) + ((float)getUInt16_le(OffsetChLonDec))/65536;\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setLongitude(float lon) {\n  setInt8(OffsetChLonInt, (int)lon);\n  setUInt16_le(OffsetChLonDec, std::abs(lon-((int)lon))*65536);\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::altitude() const {\n  return getUInt16_le(OffsetChAltitude);\n}\nvoid\nOpenRTXCodeplug::ChannelElement::setAltitude(unsigned int alt) {\n  setUInt16_le(OffsetChAltitude, alt);\n}\n\n\nSelectiveCall\nOpenRTXCodeplug::ChannelElement::rxTone() const {\n  if (! getBit(OffsetRXTone, 0))\n    return SelectiveCall();\n  int idx = getUInt8(OffsetRXTone)>>1;\n  if (idx >= _openrtx_ctcss_tone_table.size())\n    return SelectiveCall();\n  return SelectiveCall(double(_openrtx_ctcss_tone_table[idx])/10);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setRXTone(const SelectiveCall &code, const ErrorStack &err) {\n  if (code.isInvalid()) {\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n  if (! code.isCTCSS()) {\n    errMsg(err) << \"Can only encode CTCSS tones.\";\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n  if (! _openrtx_ctcss_tone_table.contains((unsigned int)(code.Hz()*10))) {\n    errMsg(err) << \"Cannot encode CTCSS frequency \" << code.Hz() << \"Hz: \"\n                << \"Not supported.\";\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n\n  uint8_t index = _openrtx_ctcss_tone_table.indexOf(\n        (unsigned int)(code.Hz()*10));\n  setUInt8(OffsetRXTone, (index<<1)|1);\n}\n\nSelectiveCall\nOpenRTXCodeplug::ChannelElement::txTone() const {\n  if (! getBit(OffsetTXTone, 0))\n    return SelectiveCall();\n  int idx = getUInt8(OffsetTXTone)>>1;\n  if (idx >= _openrtx_ctcss_tone_table.size())\n    return SelectiveCall();\n  return SelectiveCall(float(_openrtx_ctcss_tone_table[idx])/10);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setTXTone(const SelectiveCall &code, const ErrorStack &err) {\n  if (code.isInvalid()) {\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n  if (! code.isCTCSS()) {\n    errMsg(err) << \"Can only encode CTCSS tones.\";\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n  if (! _openrtx_ctcss_tone_table.contains((unsigned int)(code.Hz()*10))) {\n    errMsg(err) << \"Cannot encode CTCSS frequency \" << code.Hz() << \"Hz: \"\n                << \"Not supported.\";\n    setBit(OffsetRXTone, 0, false);\n    return;\n  }\n\n  uint8_t index = _openrtx_ctcss_tone_table.indexOf(\n        (unsigned int)(code.Hz()*10));\n  setUInt8(OffsetTXTone, (index<<1)|1);\n}\n\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::rxColorCode() const {\n  return getUInt4(OffsetRXColorCode, BitRXColorCode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setRXColorCode(unsigned int cc) {\n  setUInt4(OffsetRXColorCode, BitRXColorCode, cc);\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::txColorCode() const {\n  return getUInt4(OffsetTXColorCode, BitTXColorCode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setTXColorCode(unsigned int cc) {\n  setUInt4(OffsetTXColorCode, BitTXColorCode, cc);\n}\n\n\nDMRChannel::TimeSlot\nOpenRTXCodeplug::ChannelElement::timeslot() const {\n  if (1 == getUInt8(OffsetTimeSlot))\n    return DMRChannel::TimeSlot::TS1;\n  return DMRChannel::TimeSlot::TS2;\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setTimeslot(DMRChannel::TimeSlot ts) {\n  if (DMRChannel::TimeSlot::TS1 == ts)\n    setUInt8(OffsetTimeSlot, 1);\n  else\n    setUInt8(OffsetTimeSlot, 2);\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::hasDMRContactIndex() const {\n  return 0 != dmrContactIndex();\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::dmrContactIndex() const {\n  return getUInt16_le(OffsetDMRContact);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setDMRContactIndex(unsigned int idx) {\n  setUInt16_le(OffsetDMRContact, idx);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::clearDMRContactIndex() {\n  setDMRContactIndex(0);\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::rxChannelAccessNumber() const {\n  return getUInt4(OffsetRXCAN, BitRXCAN);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setRXChannelAccessNumber(unsigned int cc) {\n  setUInt4(OffsetRXCAN, BitRXCAN, cc);\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::txChannelAccessNumber() const {\n  return getUInt4(OffsetTXCAN, BitTXCAN);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setTXChannelAccessNumber(unsigned int cc) {\n  setUInt4(OffsetTXCAN, BitTXCAN, cc);\n}\n\n\nOpenRTXCodeplug::ChannelElement::EncryptionMode\nOpenRTXCodeplug::ChannelElement::encryptionMode() const {\n  return (EncryptionMode)getUInt4(OffsetEncrMode, BitEncrMode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setEncryptionMode(EncryptionMode mode) {\n  return setUInt4(OffsetEncrMode, BitEncrMode, mode);\n}\n\n\nOpenRTXCodeplug::ChannelElement::ChannelMode\nOpenRTXCodeplug::ChannelElement::channelMode() const {\n  return (ChannelMode) getUInt4(OffsetM17ChMode, BitM17ChMode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setChannelMode(ChannelMode mode) {\n  setUInt4(OffsetM17ChMode, BitM17ChMode, mode);\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::gpsDataEnabled() const {\n  return 1 == getUInt8(OffsetM17GPSMode);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::enableGPSData(bool enable) {\n  if (enable)\n    setUInt8(OffsetM17GPSMode, 1);\n  else\n    setUInt8(OffsetM17GPSMode, 0);\n}\n\n\nbool\nOpenRTXCodeplug::ChannelElement::hasM17ContactIndex() const {\n  return 0 != m17ContactIndex();\n}\n\nunsigned int\nOpenRTXCodeplug::ChannelElement::m17ContactIndex() const {\n  return getUInt16_le(OffsetM17Contact);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::setM17ContactIndex(unsigned int idx) {\n  setUInt16_le(OffsetM17Contact, idx);\n}\n\nvoid\nOpenRTXCodeplug::ChannelElement::clearM17ContactIndex() {\n  setM17ContactIndex(0);\n}\n\n\nChannel *\nOpenRTXCodeplug::ChannelElement::toChannelObj(Codeplug::Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid channel.\";\n    return nullptr;\n  }\n\n  if (Mode_M17 == mode()) {\n    errMsg(err) << \"Cannot decode M17 channel. Not implemented yet.\";\n    return nullptr;\n  }\n\n  Channel *ch = nullptr;\n  if (Mode_FM == mode()) {\n    FMChannel *an = new FMChannel(); ch = an;\n    switch(bandwidth()) {\n    case BW_12_5kHz: an->setBandwidth(FMChannel::Bandwidth::Narrow); break;\n    case BW_20kHz:\n    case BW_25kHz: an->setBandwidth(FMChannel::Bandwidth::Wide); break;\n    }\n    an->setRXTone(rxTone());\n    an->setTXTone(txTone());\n  } else if (Mode_DMR == mode()) {\n    DMRChannel *dmr = new DMRChannel(); ch = dmr;\n    dmr->setAdmit(DMRChannel::Admit::ColorCode);\n    dmr->setColorCode(rxColorCode());\n    dmr->setTimeSlot(timeslot());\n  }\n\n  // Common settings\n  ch->setName(name());\n  ch->setRXOnly(rxOnly());\n  if (30 > power()) { // less than 30dBm (1W) min\n    ch->setPower(Channel::Power::Min);\n  } else if (34 > power()) {\n    ch->setPower(Channel::Power::Low);\n  } else if (37 > power()) {\n    ch->setPower(Channel::Power::Mid);\n  } else if (38 > power()) {\n    ch->setPower(Channel::Power::High);\n  } else {\n    ch->setPower(Channel::Power::Max);\n  }\n  ch->setRXFrequency(rxFrequency());\n  ch->setTXFrequency(txFrequency());\n\n  return ch;\n}\n\nbool\nOpenRTXCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err) const {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link invalid channel '\" << c->name() << \"'.\";\n    return false;\n  }\n\n  if (Mode_M17 == mode()) {\n    errMsg(err) << \"Cannot link M17 channel '\" << c->name() << \"', not implemented yet.\";\n    return false;\n  } else if (Mode_DMR == mode()) {\n    DMRChannel *dmr = c->as<DMRChannel>();\n\n    // Link group list, if set\n    /*if (hasGroupListIndex()) {\n      if (! ctx.has<RXGroupList>(groupListIndex())) {\n        errMsg(err) << \"Cannot link group list index \" << groupListIndex()\n                    << \" for channel '\" << c->name() << \"': Index not found.\";\n        return false;\n      }\n      dmr->setGroupListObj(ctx.get<RXGroupList>(groupListIndex()));\n    }*/\n\n    // Link contact, if set\n    if (hasDMRContactIndex()) {\n      if (! ctx.has<DMRContact>(dmrContactIndex())) {\n        errMsg(err) << \"Cannot link DMR contact index \" << dmrContactIndex()\n                    << \" for channel '\" << c->name() << \"': Index not found.\";\n        return false;\n      }\n      dmr->setContact(ctx.get<DMRContact>(dmrContactIndex()));\n    }\n  }\n\n  // Link scan list, if set\n  /*if (hasScanListIndex()) {\n    if (! ctx.has<ScanList>(scanListIndex())) {\n      errMsg(err) << \"Cannot link scan list index \" << scanListIndex()\n                  << \" for channel '\" << c->name() << \"': Index not found.\";\n      return false;\n    }\n    c->setScanList(ctx.get<ScanList>(scanListIndex()));\n  }*/\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err) {\n  clear();\n\n  setName(c->name());\n  setRXOnly(c->rxOnly());\n  switch (c->power()) {\n  case Channel::Power::Min: setPower(27); break;\n  case Channel::Power::Low: setPower(30); break;\n  case Channel::Power::Mid: setPower(34); break;\n  case Channel::Power::High: setPower(37); break;\n  case Channel::Power::Max: setPower(38.5); break;\n  }\n  setRXFrequency(c->rxFrequency());\n  setTXFrequency(c->txFrequency());\n  if (c->scanListRef()->isNull())\n    clearScanListIndex();\n  else\n    setScanListIndex(ctx.index(c->scanList()));\n  clearGroupListIndex();\n\n  if (c->is<FMChannel>()) {\n    const FMChannel *fm = c->as<FMChannel>();\n    setMode(Mode_FM);\n    setRXTone(fm->rxTone(), err);\n    setTXTone(fm->txTone(), err);\n  } else if (c->is<DMRChannel>()) {\n    const DMRChannel *dmr = c->as<DMRChannel>();\n    setMode(Mode_DMR);\n    if (! dmr->groupListRef()->isNull())\n      setGroupListIndex(ctx.index(dmr->groupList()));\n    setTXColorCode(dmr->colorCode());\n    setRXColorCode(dmr->colorCode());\n    setTimeslot(dmr->timeSlot());\n    if (! dmr->contactRef()->isNull())\n      setDMRContactIndex(ctx.index(dmr->contact()));\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenRTXCodeplug::ContactElement\n * ********************************************************************************************* */\nOpenRTXCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, 0x0027)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::ContactElement::~ContactElement() {\n  // pass...\n}\n\nvoid\nOpenRTXCodeplug::ContactElement::clear() {\n  memset(_data, 0, _size);\n}\n\nbool\nOpenRTXCodeplug::ContactElement::isValid() const {\n  return Codeplug::Element::isValid() && ( (Mode_M17 == mode()) || (Mode_DMR == mode()) );\n}\n\n\nQString\nOpenRTXCodeplug::ContactElement::name() const {\n  return readASCII(OffsetName, StringLength, 0);\n}\n\nvoid\nOpenRTXCodeplug::ContactElement::setName(const QString &name) {\n  writeASCII(OffsetName, name, StringLength, 0);\n}\n\n\nOpenRTXCodeplug::Mode\nOpenRTXCodeplug::ContactElement::mode() const {\n  return (Mode) getUInt8(OffsetMode);\n}\n\nvoid\nOpenRTXCodeplug::ContactElement::setMode(Mode mode) {\n  setUInt8(OffsetMode, mode);\n}\n\n\nunsigned int\nOpenRTXCodeplug::ContactElement::dmrId() const {\n  return getUInt32_le(OffsetDMRId);\n}\n\nvoid\nOpenRTXCodeplug::ContactElement::setDMRId(unsigned int id) {\n  setUInt32_le(OffsetDMRId, id);\n}\n\n\nbool\nOpenRTXCodeplug::ContactElement::dmrRing() const {\n  return getBit(OffsetDMRRing, BitDMRRing);\n}\n\nvoid\nOpenRTXCodeplug::ContactElement::enableDMRRing(bool enable) {\n  return setBit(OffsetDMRRing, BitDMRRing, enable);\n}\n\n\nDMRContact::Type\nOpenRTXCodeplug::ContactElement::dmrContactType() const {\n  // This is not specified yet?!?\n  return (DMRContact::Type)getUInt2(OffsetDMRCallType, BitDMRCallType);\n}\nvoid\nOpenRTXCodeplug::ContactElement::setDMRContactType(DMRContact::Type type) {\n  setUInt2(OffsetDMRCallType, BitDMRCallType, type);\n}\n\n\nQString\nOpenRTXCodeplug::ContactElement::m17Call() const {\n  static const char charMap[] = \"xABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-/.\";\n  uint64_t encoded; memcpy(((uint8_t *)&encoded)+2, _data+OffsetM17Address, 6);\n  QString result;\n  while (encoded) {\n    result.append(charMap[encoded%40]);\n    encoded /= 40;\n  }\n  return result;\n}\n\nbool\nOpenRTXCodeplug::ContactElement::setM17Call(const QString &call, const ErrorStack &err) {\n  if (call.size() > 9) {\n    errMsg(err) << \"Cannot encode calls longer than 9 chars.\";\n    return false;\n  }\n  QString C = call.toUpper();\n\n  uint64_t encoded = 0;\n  for (QString::const_reverse_iterator it=C.rbegin(); it!=C.rend(); it++) {\n    encoded *= 40;\n    char c = QChar(*it).toLatin1();\n    if (('A' <= c) && ('Z' >= c))\n      encoded += (c-'A')+1;\n    else if (('0' <= c) && ('9' >= c))\n      encoded += (c-'0')+27;\n    else if ('-' == c)\n      encoded += 37;\n    else if ('/' == c)\n      encoded += 38;\n    else if ('.' == c)\n      encoded += 39;\n    else {\n      errMsg(err) << \"Invalid char '\" << *it << \"' for an M17 call [A-Z,0-9,-,/,.].\";\n      return false;\n    }\n  }\n  memcpy(_data+OffsetM17Address, ((uint8_t*)&encoded)+2, 6);\n  return true;\n}\n\nDigitalContact *OpenRTXCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid contacts.\";\n    return nullptr;\n  }\n\n  if (Mode_DMR == mode()) {\n    DMRContact *contact = new DMRContact();\n\n    contact->setName(name());\n    contact->setRing(dmrRing());\n    contact->setNumber(dmrId());\n    contact->setType(dmrContactType());\n\n    return contact;\n  } else if (Mode_M17 == mode()) {\n    M17Contact *contact = new M17Contact();\n\n    contact->setName(name());\n    contact->setRing(dmrRing());\n    contact->setCall(m17Call());\n\n    return contact;\n  }\n\n  return nullptr;\n}\n\nbool\nOpenRTXCodeplug::ContactElement::fromContactObj(const DigitalContact *cont, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n\n  setName(cont->name());\n  enableDMRRing(cont->ring());\n\n  if (cont->is<DMRContact>()) {\n    const DMRContact *dmr = cont->as<DMRContact>();\n    setMode(Mode_DMR);\n    setDMRId(dmr->number());\n    setDMRContactType(dmr->type());\n  } else if (cont->is<M17Contact>()) {\n    const M17Contact *m17 = cont->as<M17Contact>();\n    setM17Call(m17->call());\n  } else {\n    errMsg(err) << \"Cannot encode contact '\" << cont->name() << \"': Contact type '\"\n                << cont->metaObject()->className() << \"' not supported by OpenRTX firmware.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenRTXCodeplug::ZoneElement\n * ********************************************************************************************* */\nOpenRTXCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nOpenRTXCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element(ptr, 0x0022)\n{\n  _size = 0x22 + channelCount()*4;\n}\n\nOpenRTXCodeplug::ZoneElement::~ZoneElement() {\n  // pass...\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::clear() {\n  memset(_data, 0, 0x22);\n  setChannelCount(0);\n}\n\nbool\nOpenRTXCodeplug::ZoneElement::isValid() const {\n  return Codeplug::Element::isValid();\n}\n\n\nQString\nOpenRTXCodeplug::ZoneElement::name() const {\n  return readASCII(OffsetName, StringLength, 0);\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::setName(const QString &name) {\n  writeASCII(OffsetName, name, StringLength, 0);\n}\n\n\nunsigned int\nOpenRTXCodeplug::ZoneElement::channelCount() const {\n  return getUInt16_le(OffsetCount);\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::setChannelCount(unsigned int n) {\n  setUInt16_le(OffsetCount, n);\n  _size = 0x22 + sizeof(uint32_t)*n;\n}\n\n\nunsigned int\nOpenRTXCodeplug::ZoneElement::channelIndex(unsigned int n) const {\n  return getUInt32_le(OffsetChannel + sizeof(uint32_t)*n);\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::setChannelIndex(unsigned int n, unsigned int idx) {\n  setUInt32_le(OffsetChannel + sizeof(uint32_t)*n, idx);\n}\n\n\nZone *\nOpenRTXCodeplug::ZoneElement::toZoneObj(Context &ctx) const {\n  Q_UNUSED(ctx)\n  Zone *zone = new Zone();\n  zone->setName(name());\n  return zone;\n}\n\nbool\nOpenRTXCodeplug::ZoneElement::linkZoneObj(Zone *zone, Context &ctx, bool putInB, const ErrorStack &err) const {\n  for (unsigned int i=0; i<channelCount(); i++) {\n    if (! ctx.has<Channel>(channelIndex(i))) {\n      errMsg(err) << \"Cannot link zone '\" << zone->name() << \"': Channel index \" << channelIndex(i)\n                  << \" not known.\";\n      return false;\n    }\n    if (putInB)\n      zone->B()->add(ctx.get<Channel>(channelIndex(i)));\n    else\n      zone->A()->add(ctx.get<Channel>(channelIndex(i)));\n  }\n  return true;\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::fromZoneObjA(const Zone *zone, Context &ctx) {\n  if (zone->B()->count())\n    setName(zone->name()+\" A\");\n  else\n    setName(zone->name());\n  setChannelCount(zone->A()->count());\n  for (int i=0; i<zone->A()->count(); i++) {\n    setChannelIndex(i, ctx.index(zone->A()->get(i)));\n  }\n}\n\nvoid\nOpenRTXCodeplug::ZoneElement::fromZoneObjB(const Zone *zone, Context &ctx) {\n  setName(zone->name() + \" B\");\n  setChannelCount(zone->B()->count());\n  for (int i=0; i<zone->B()->count(); i++) {\n    setChannelIndex(i, ctx.index(zone->B()->get(i)));\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of OpenRTXCodeplug\n * ********************************************************************************************* */\nOpenRTXCodeplug::OpenRTXCodeplug(QObject *parent)\n  : Codeplug(parent)\n{\n  addImage(\"OpenRTX codeplug v0.1\");\n  image(0).addElement(0x0000, HeaderSize);\n}\n\nOpenRTXCodeplug::~OpenRTXCodeplug() {\n  // pass...\n}\n\nvoid\nOpenRTXCodeplug::clear() {\n  remImage(0);\n  addImage(\"OpenRTX codeplug v0.1\");\n  image(0).addElement(0x0000, HeaderSize);\n}\n\nbool\nOpenRTXCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // All indices as 1-based. That is, the first channel gets index 1 etc.\n\n  // Map DMR contacts\n  for (int i=0, c=0; i<config->contacts()->count(); i++) {\n    Contact *contact = config->contacts()->contact(i);\n    if (contact->is<DMRContact>() || contact->is<M17Contact>()) {\n      ctx.add(contact, c+1); c++;\n    } else {\n      logInfo() << \"Cannot index contact '\" << contact->name()\n                << \"'. Contact type '\" << contact->metaObject()->className()\n                << \"' not supported by or implemented for OpenRTX devices.\";\n    }\n  }\n\n  // Map channels\n  for (int i=0; i<config->channelList()->count(); i++) {\n    Channel *channel = config->channelList()->channel(i);\n    ctx.add(channel, i+1);\n  }\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i+1);\n\n  return true;\n}\n\n\nConfig *\nOpenRTXCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot pre-process OpenRTX codeplug.\";\n    return nullptr;\n  }\n\n  // Remove all AM channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject};\n  if (! amFilter.process(intermediate, err)) {\n    errMsg(err) << \"Remove AM channels.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot split zone for OpenRTX codeplug.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nOpenRTXCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  // Check if default DMR id is set.\n  if (nullptr == config->settings()->defaultId()) {\n    errMsg(err) << \"Cannot encode OpenRTX codeplug: No default radio ID specified.\";\n    return false;\n  }\n\n  // Create index<->object table.\n  Context ctx(config);\n  if (! index(config, ctx, err))\n    return false;\n\n  return this->encodeElements(flags, ctx, err);\n}\n\n\nbool\nOpenRTXCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  HeaderElement header(data(0));\n  header.clear();\n  header.setAuthor(ctx.config()->settings()->defaultId()->name());\n  header.setDescription(\"Encoded by qdmr v\" VERSION_STRING);\n\n  // Define Contacts\n  if (! this->encodeContacts(ctx.config(), flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  if (! this->encodeChannels(ctx.config(), flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode channels.\";\n    return false;\n  }\n\n  if (! this->encodeZones(ctx.config(), flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenRTXCodeplug::decode(Config *config, const ErrorStack &err) {\n  // Clear config object\n  config->clear();\n\n  // Create index<->object table.\n  Context ctx(config);\n\n  return this->decodeElements(ctx, err);\n}\n\n\nbool\nOpenRTXCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process Radioddy codeplug.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot merg zones in decoded Radioddity codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nbool\nOpenRTXCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! this->createContacts(ctx.config(), ctx, err)) {\n    errMsg(err) << \"Cannot create contacts.\";\n    return false;\n  }\n\n  if (! this->createChannels(ctx.config(), ctx, err)) {\n    errMsg(err) << \"Cannot create channels.\";\n    return false;\n  }\n\n  if (! this->createZones(ctx.config(), ctx, err)) {\n    errMsg(err) << \"Cannot create zones.\";\n    return false;\n  }\n\n  if (! this->linkChannels(ctx.config(), ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  if (! this->linkZones(ctx.config(), ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  return true;\n}\n\nunsigned int\nOpenRTXCodeplug::numContacts() {\n  return HeaderElement(data(0x0000)).contactCount();\n}\n\nunsigned int\nOpenRTXCodeplug::offsetContact(unsigned int n) {\n  return HeaderSize + n*ContactSize;\n}\n\nbool\nOpenRTXCodeplug::encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(config); Q_UNUSED(flags)\n\n  /// @todo Limit number of contacts.\n  unsigned int numContacts = ctx.count<DigitalContact>();\n  HeaderElement(data(0x0000)).setContactCount(numContacts);\n  image(0).addElement(offsetContact(0), numContacts*ContactSize);\n\n  for (unsigned int i=0; i<ctx.count<DigitalContact>(); i++) {\n    ContactElement contact(data(offsetContact(i)));\n    if (! contact.fromContactObj(ctx.get<DigitalContact>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot encode contact '\" << ctx.get<DigitalContact>(i+1)->name() <<\"'.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::createContacts(Config *config, Context &ctx, const ErrorStack &err) {\n  unsigned int numContacts = HeaderElement(data(0x0000)).contactCount();\n\n  for (unsigned int i=0; i<numContacts; i++) {\n    DigitalContact *contact = ContactElement(data(offsetContact(i))).toContactObj(ctx, err);\n    if (nullptr == contact) {\n      errMsg(err) << \"Cannot create \" << (i+1) << \"-th contact.\";\n      return false;\n    }\n    config->contacts()->add(contact);\n    ctx.add(contact, i+1);\n  }\n\n  return true;\n}\n\n\nunsigned int\nOpenRTXCodeplug::numChannels() {\n  return HeaderElement(data(0x0000)).channelCount();\n}\n\nunsigned int\nOpenRTXCodeplug::offsetChannel(unsigned int n) {\n  unsigned int numContacts = HeaderElement(data(0x0000)).contactCount();\n  return HeaderSize + numContacts*ContactSize + n*ChannelSize;\n}\n\nbool\nOpenRTXCodeplug::encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags)\n\n  /// @todo Limit number of channels.\n  unsigned int numChannels = config->channelList()->count();\n  HeaderElement(data(0x0000)).setChannelCount(numChannels);\n  image(0).addElement(offsetChannel(0), numChannels*ChannelSize);\n\n  for (int i=0; i<config->channelList()->count(); i++) {\n    ChannelElement ch(data(offsetChannel(i)));\n    if (! ch.fromChannelObj(config->channelList()->channel(i), ctx, err)) {\n      errMsg(err) << \"Cannot encode \" << (i+1) << \"-th channel '\"\n                  << config->channelList()->channel(i)->name() << \"'.\";\n      return false;\n    }\n    ctx.add(config->channelList()->channel(i), i+1);\n  }\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::createChannels(Config *config, Context &ctx, const ErrorStack &err) {\n  unsigned int numChannels = HeaderElement(data(0x0000)).channelCount();\n  unsigned int offsetChannels = offsetChannel(0);\n  for (unsigned int i=0; i<numChannels; i++) {\n    ChannelElement ch(data(offsetChannels + i*ChannelSize));\n    Channel *chObj = ch.toChannelObj(ctx, err);\n    if (nullptr == chObj) {\n      errMsg(err) << \"Cannot decode \" << (i+1) << \"-th channel.\";\n      return false;\n    }\n    config->channelList()->add(chObj);\n    ctx.add(chObj, i+1);\n  }\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::linkChannels(Config *config, Context &ctx, const ErrorStack &err) {\n  unsigned int numChannels = HeaderElement(data(0x0000)).channelCount();\n  unsigned int offsetChannels = offsetChannel(0);\n  for (unsigned int i=0; i<numChannels; i++) {\n    ChannelElement ch(data(offsetChannels + i*ChannelSize));\n    Channel *chObj = config->channelList()->channel(i);\n    if (! ch.linkChannelObj(chObj, ctx, err)) {\n      errMsg(err) << \"Cannot link \" << (i+1) << \"-th channel \"\n                  << chObj->name() << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nunsigned int\nOpenRTXCodeplug::numZones() {\n  return HeaderElement(data(0x0000)).zoneCount();\n}\n\nunsigned int\nOpenRTXCodeplug::offsetZoneOffsets() {\n  HeaderElement header(data(0x0000));\n  unsigned int numContacts = header.contactCount();\n  unsigned int numChannels = header.channelCount();\n  return  HeaderSize + numContacts*ContactSize + numChannels*ChannelSize;\n}\n\nunsigned int\nOpenRTXCodeplug::offsetZone(unsigned int n) {\n  HeaderElement header(data(0x0000));\n  unsigned int numContacts = header.contactCount();\n  unsigned int numChannels = header.channelCount();\n  uint32_t *ptr = (uint32_t *)data(HeaderSize + numContacts*ContactSize + numChannels*ChannelSize\n                                   + n*sizeof(uint32_t));\n  return qFromLittleEndian(*ptr);\n}\n\nbool\nOpenRTXCodeplug::encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n\n  // Allocate zone offsets\n  HeaderElement(data(0x0000)).setZoneCount(config->zones()->count());\n  image(0).addElement(offsetZoneOffsets(), (config->zones()->count())*sizeof(uint32_t));\n  uint32_t *offsets = (uint32_t *)data(offsetZoneOffsets());\n\n  // Allocate and encode zones\n  uint32_t currentOffset = offsetZoneOffsets() + (config->zones()->count())*sizeof(uint32_t);\n  for (int z=0, i=0; i<config->zones()->count(); i++,z++) {\n    // Allocate & encode zone A\n    unsigned int zoneSize = ZoneHeaderSize+config->zones()->zone(z)->A()->count()*sizeof(uint32_t);\n    image(0).addElement(currentOffset, zoneSize);\n    offsets[i] = qToLittleEndian(currentOffset);\n    ZoneElement(data(currentOffset)).fromZoneObjA(config->zones()->zone(z), ctx);\n    currentOffset += zoneSize;\n  }\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::createZones(Config *config, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  unsigned int zoneCount = numZones();\n  uint32_t *zoneOffsets = (uint32_t *) data(offsetZoneOffsets());\n\n  for (unsigned int i=0; i<zoneCount; i++) {\n    ZoneElement zone(data(qFromLittleEndian(zoneOffsets[i])));\n    if (! zone.isValid())\n      continue;\n    Zone *obj = new Zone(zone.name());\n    config->zones()->add(obj); ctx.add(obj, i+1);\n  }\n\n  return true;\n}\n\nbool\nOpenRTXCodeplug::linkZones(Config *config, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(config); Q_UNUSED(err)\n\n  uint32_t *zoneOffsets = (uint32_t *) data(offsetZoneOffsets());\n\n  for (unsigned int i=0, z=0; i<numZones(); i++, z++) {\n    ZoneElement zone(data(qFromLittleEndian(zoneOffsets[i])));\n    if (! zone.isValid())\n      continue;\n    if (! ctx.has<Zone>(i+1))\n      continue;\n    Zone *obj = ctx.get<Zone>(i+1);\n    for (unsigned int i=0; i<zone.channelCount(); i++) {\n      if (! ctx.has<Channel>(zone.channelIndex(i))) {\n        logWarn() << \"Cannot link channel with index \" << zone.channelIndex(i)\n                  << \" channel not defined.\";\n        continue;\n      }\n      obj->A()->add(ctx.get<Channel>(zone.channelIndex(i)));\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/openrtx_codeplug.hh",
    "content": "#ifndef OPENRTX_CODEPLUG_HH\n#define OPENRTX_CODEPLUG_HH\n\n#include \"codeplug.hh\"\n#include \"signaling.hh\"\n#include \"channel.hh\"\n#include \"contact.hh\"\n\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\n\n/** Implements the binary encoding and decoding of the OpenRTX codeplug.\n *\n * @section ortxcpl Codeplug structure within radio\n * The binary codeplug does not use fixed offsets. It is just a concatenation arrays of codeplug\n * elements. This codeplug implements the revision 0.1.\n * <table>\n *  <tr><th>Content</th></tr>\n *  <tr><td>Header see @c HeaderElement.</td></tr>\n *  <tr><td>Max. 65536 contacts as specified in the header. See @c ContactElement.</td></tr>\n *  <tr><td>Max. 65536 channels as specified in the header. See @c ChannelElement.</td></tr>\n *  <tr><td>Max. 65536 bank (zone) offsets as specified in the header. Each offset is stored as a\n *      ??? and specifies the offset to the bank w.r.t. ??? in ???.</td></tr>\n *  <tr><td>Max. 65536 banks (zones). See @c ZoneElement.</td></tr>\n * </table>\n *\n * @ingroup ortx */\nclass OpenRTXCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Possible modes for a channel or contact. */\n  enum Mode {\n    Mode_None = 0, ///< Disabled?\n    Mode_FM   = 1, ///< FM Channel.\n    Mode_DMR  = 2, ///< DMR Channel.\n    Mode_M17  = 3  ///< M17 Channel.\n  };\n\npublic:\n  /** Implements the codeplug header element.\n   *\n   * Binary representation of the header (size 0058h bytes):\n   * @verbinclude openrtx_header.txt */\n  class HeaderElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    HeaderElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    HeaderElement(uint8_t *ptr);\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the version number (MAJOR<<8)|MINOR. */\n    virtual uint16_t version() const;\n    /** Sets the version number. This number is fixed to 0.1, the supported version. */\n    virtual void setVersion();\n\n    /** Returns the author name. */\n    virtual QString author() const;\n    /** Sets the author name. */\n    virtual void setAuthor(const QString &name);\n    /** Returns the description. */\n    virtual QString description() const;\n    /** Sets the description. */\n    virtual void setDescription(const QString description);\n\n    /** Returns the timestamp. */\n    virtual QDateTime timestamp() const;\n    /** Sets the timestamp. */\n    virtual void setTimestamp(const QDateTime timestamp=QDateTime::currentDateTime());\n\n    /** Returns the contact count. */\n    virtual unsigned int contactCount() const;\n    /** Sets the contact count. */\n    virtual void setContactCount(unsigned int n);\n    /** Returns the channel count. */\n    virtual unsigned int channelCount() const;\n    /** Sets the channel count. */\n    virtual void setChannelCount(unsigned int n);\n    /** Returns the zone count. */\n    virtual unsigned int zoneCount() const;\n    /** Sets the zone count. */\n    virtual void setZoneCount(unsigned int n);\n\n  protected:\n    /** Just holds the offsets within the header and other constants. */\n    enum Offsets {\n      OffsetMagic = 0x00, MagicNumber = 0x43585452, OffsetVersion = 0x08, OffsetAuthor = 0x0a,\n      OffsetDescription = 0x2a, OffsetTimestamp = 0x4a, OffsetContactCount = 0x52,\n      OffsetChannelCount = 0x54, OffsetZoneCount = 0x56, StringLength = 0x20,\n      SupportedVersion = ((0<<8)|1)\n    };\n  };\n\n  /** Implements the binary representation of a channel.\n   *\n   * Binary representation (size 005ah bytes):\n   * @verbinclude openrtx_channel.txt */\n  class ChannelElement: public Codeplug::Element\n  {\n  public:\n    /** Specifies the possible bandwidth settings. */\n    enum Bandwidth {\n      BW_12_5kHz = 0,\n      BW_20kHz   = 1,\n      BW_25kHz   = 2\n    };\n\n    /** Specifies the DMR time slot settings. */\n    enum Timeslot {\n      Timeslot1 = 1,\n      Timeslot2 = 2\n    };\n\n    /** Specifies the M17 channel mode. */\n    enum ChannelMode {\n      M17Voice = 1,\n      M17Data = 2,\n      M17VoiceData = 3\n    };\n\n    /** Specifies the M17 encryption modes. */\n    enum EncryptionMode {\n      EncrNone      = 0,\n      EncrAES256    = 1,\n      EncrScrambler = 2\n    };\n\n  protected:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructs a channel from the given memory. */\n    explicit ChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelElement();\n\n    bool isValid() const;\n\n    /** Resets the channel. */\n    virtual void clear();\n\n    /** Returns the channel mode. */\n    virtual Mode mode() const;\n    /** Sets the channel mode. */\n    virtual void setMode(Mode mode);\n\n    /** Returns @c true, if the channel is RX only. */\n    virtual bool rxOnly() const;\n    /** Enables/disables RX only for the channel. */\n    virtual void setRXOnly(bool enable);\n\n    /** Returns the bandwidth of the channel. */\n    virtual Bandwidth bandwidth() const;\n    /** Sets the bandwidth of the channel. */\n    virtual void setBandwidth(Bandwidth bw);\n\n    /** Power in dBm. */\n    virtual float power() const;\n    /** Set power in dBm. */\n    virtual void setPower(float dBm);\n\n    /** Returns the RX frequency in MHz. */\n    virtual Frequency rxFrequency() const;\n    /** Sets the RX frequency in MHz. */\n    virtual void setRXFrequency(Frequency MHz);\n    /** Returns the TX frequency in MHz. */\n    virtual Frequency txFrequency() const;\n    /** Sets the TX frequency in MHz. */\n    virtual void setTXFrequency(Frequency MHz);\n\n    /** Retrusn @c true if the scan list is set. */\n    virtual bool hasScanListIndex() const;\n    /** Returns the scan list index. */\n    virtual unsigned int scanListIndex() const;\n    /** Sets the scan list index. */\n    virtual void setScanListIndex(unsigned int index);\n    /** Clears the scan list index. */\n    virtual void clearScanListIndex();\n\n    /** Retrusn @c true if the group list is set. */\n    virtual bool hasGroupListIndex() const;\n    /** Returns the group list index. */\n    virtual unsigned int groupListIndex() const;\n    /** Sets the group list index. */\n    virtual void setGroupListIndex(unsigned int index);\n    /** Clears the group list index. */\n    virtual void clearGroupListIndex();\n\n    /** Returns the channel name. */\n    virtual QString name() const;\n    /** Sets the channel name. */\n    virtual void setName(const QString &name);\n    /** Returns the channel description. */\n    virtual QString description() const;\n    /** Sets the channel description. */\n    virtual void setDescription(const QString &description);\n\n    /** Returns the channel latitude. */\n    virtual float latitude() const;\n    /** Sets the latitude. */\n    virtual void setLatitude(float lat);\n    /** Returns the channel longitude. */\n    virtual float longitude() const;\n    /** Sets the longitude. */\n    virtual void setLongitude(float lat);\n    /** Returns the height in meters. */\n    virtual unsigned int altitude() const;\n    /** Sets the height in meters. */\n    virtual void setAltitude(unsigned int alt);\n\n    /** Returns the CTCSS RX sub-tone. Only valid for FM channels. */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the CTCSS RX sub-tone. Only valid for FM channels. */\n    virtual void setRXTone(const SelectiveCall &code, const ErrorStack &err=ErrorStack());\n    /** Returns the CTCSS TX sub-tone. Only valid for FM channels. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the CTCSS TX sub-tone. Only valid for FM channels. */\n    virtual void setTXTone(const SelectiveCall &code, const ErrorStack &err=ErrorStack());\n\n    /** Returns the RX color code. Only valid for DMR channels. */\n    virtual unsigned int rxColorCode() const;\n    /** Sets the RX color code. Only valid for DMR channels. */\n    virtual void setRXColorCode(unsigned int cc);\n    /** Returns the TX color code. Only valid for DMR channels. */\n    virtual unsigned int txColorCode() const;\n    /** Sets the TX color code. Only valid for DMR channels. */\n    virtual void setTXColorCode(unsigned int cc);\n\n    /** Returns the times slot for the channel. Only valid for DMR channels. */\n    virtual DMRChannel::TimeSlot timeslot() const;\n    /** Sets the timeslot for the channel. Only valid for DMR channels. */\n    virtual void setTimeslot(DMRChannel::TimeSlot ts);\n\n    /** Returns @c true if the DMR contact index is set. */\n    virtual bool hasDMRContactIndex() const;\n    /** Returns the contact index for the DMR contact. Only valid for DMR channels. */\n    virtual unsigned int dmrContactIndex() const;\n    /** Sets the DMR contact index. Only valid for DMR channels. */\n    virtual void setDMRContactIndex(unsigned int idx);\n    /** Clears the DMR contact index. */\n    virtual void clearDMRContactIndex();\n\n    /** Returns the RX channel access number. Only valid for M17 channels. */\n    virtual unsigned int rxChannelAccessNumber() const;\n    /** Sets the RX channel access number. Only valid for M17 channels. */\n    virtual void setRXChannelAccessNumber(unsigned int cc);\n    /** Returns the TX color code. Only valid for M17 channels. */\n    virtual unsigned int txChannelAccessNumber() const;\n    /** Sets the TX color code. Only valid for M17 channels. */\n    virtual void setTXChannelAccessNumber(unsigned int cc);\n\n    /** Returns the encryption mode of the channel. Only valid for M17 channels. */\n    virtual EncryptionMode encryptionMode() const;\n    /** Sets the encryption mode for the channel. Only valid for M17 channels. */\n    virtual void setEncryptionMode(EncryptionMode mode);\n\n    /** Returns the channel mode. Only valid for M17 channels. */\n    virtual ChannelMode channelMode() const;\n    /** Sets the channel mode. Only valid for M17 channels. */\n    virtual void setChannelMode(ChannelMode mode);\n\n    /** Returns @c true if GPS position is sent as meta-data. Only valid for M17 channels. */\n    virtual bool gpsDataEnabled() const;\n    /** Enables/disables sending of GPS position as meta-data. Only valid for M17 channels. */\n    virtual void enableGPSData(bool enable);\n\n    /** Returns @c true if the M17 contact index is set. */\n    virtual bool hasM17ContactIndex() const;\n    /** Returns the M17 contact index. Only valid for M17 channels. */\n    virtual unsigned int m17ContactIndex() const;\n    /** Sets the M17 contact index. Only valid for M17 channels. */\n    virtual void setM17ContactIndex(unsigned int idx);\n    /** Clears the M17 contact index. */\n    virtual void clearM17ContactIndex();\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Just contains the offsets within the channel element. */\n    enum Offsets {\n      OffsetMode = 0x00, OffsetBandwidth = 0x01, BitBandwidth = 0x00, OffsetRXOnly = 0x01,\n      BitRXOnly = 0x02, OffsetPower = 0x02, OffsetRXFrequency = 0x03, OffsetTXFrequency = 0x07,\n      OffsetScanList = 0x0b, OffsetGroupList = 0x0c, OffsetName = 0x0d, OffsetDescription = 0x2d,\n      OffsetChLatInt = 0x4d, OffsetChLatDec = 0x4e, OffsetChLonInt = 0x50, OffsetChLonDec = 0x51,\n      OffsetChAltitude = 0x53, OffsetRXTone = 0x55, OffsetTXTone = 0x56, OffsetRXColorCode = 0x55,\n      BitRXColorCode = 0x00, OffsetTXColorCode = 0x55, BitTXColorCode = 0x04, OffsetTimeSlot = 0x56,\n      OffsetDMRContact = 0x57, OffsetRXCAN = 0x55, BitRXCAN = 0x00, OffsetTXCAN = 0x55,\n      BitTXCAN = 0x04, OffsetEncrMode = 0x56, BitEncrMode = 0x04, OffsetM17ChMode = 0x56,\n      BitM17ChMode = 0x00, OffsetM17GPSMode = 0x57, OffsetM17Contact = 0x58, StringLength = 0x20\n    };\n  };\n\n  /** Implements the digital contact for the OpenRTX firmware.\n   *\n   * Binary representation (size 0027h bytes):\n   * @verbinclude openrtx_contact.txt */\n  class ContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ContactElement();\n\n    /** Resets the contact. */\n    void clear();\n    /** Returns @c true if the contact is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Returns the mode of the contact (either DMR or M17). */\n    virtual Mode mode() const;\n    /** Sets the mode of the contact. */\n    virtual void setMode(Mode mode);\n\n    /** Returns the DMR ID. Only valid for DMR contacts. */\n    virtual unsigned int dmrId() const;\n    /** Sets the DMR ID. Only valid for DMR contacts. */\n    virtual void setDMRId(unsigned int id);\n\n    /** Returns @c true if the RX tone is enabled (ring). Only valid for DMR contacts. */\n    virtual bool dmrRing() const;\n    /** Enables/disables RX tone (ring). Only valid for DMR contacts. */\n    virtual void enableDMRRing(bool enable);\n\n    /** Returns the contact type. Only valid for DMR contacts. */\n    virtual DMRContact::Type dmrContactType() const;\n    /** Sets the contact type. */\n    virtual void setDMRContactType(DMRContact::Type type);\n\n    /** Returns the contact call. */\n    virtual QString m17Call() const;\n    /** Sets the M17 call. */\n    virtual bool setM17Call(const QString &call, const ErrorStack &err=ErrorStack());\n\n    /** Constructs a @c DigitalContact instance from this codeplug contact. */\n    virtual DigitalContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug contact from the given @c DigitalContact. */\n    virtual bool fromContactObj(const DigitalContact *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  protected:\n    /** Just holds the offsets within the codeplug. */\n    enum Offsets {\n      OffsetName = 0x00, OffsetMode = 0x20, OffsetDMRId = 0x21, OffsetDMRCallType = 0x25,\n      BitDMRCallType = 0, OffsetDMRRing = 0x25, BitDMRRing = 0x02, OffsetM17Address = 0x21,\n      StringLength = 0x20\n    };\n\n  };\n\n\n  /** The binary encoding of a zone.\n   *\n   * Binary representation (variable size):\n   * @verbinclude openrtx_zone.txt */\n  class ZoneElement: Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ZoneElement(uint8_t *ptr);\n    virtual ~ZoneElement();\n\n    /** Resets the zone. */\n    void clear();\n    /** Returns @c true if the zone is valid. */\n    bool isValid() const;\n\n    /** Returns the zone name. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Returns the number of channels in zone. */\n    virtual unsigned int channelCount() const;\n    /** Sets the number of channels in zone. */\n    virtual void setChannelCount(unsigned int n);\n\n    /** Returns the n-th channel index. */\n    virtual unsigned int channelIndex(unsigned int n) const;\n    /** Sets the n-th channel index. */\n    virtual void setChannelIndex(unsigned int n, unsigned int idx);\n\n    /** Constructs a generic @c Zone object from this codeplug zone. */\n    virtual Zone *toZoneObj(Context &ctx) const;\n    /** Links a previously constructed @c Zone object to the rest of the configuration. That is\n     * linking to the referred channels. */\n    virtual bool linkZoneObj(Zone *zone, Context &ctx, bool putInB, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug zone representation from the given generic @c Zone object. */\n    virtual void fromZoneObjA(const Zone *zone, Context &ctx);\n    /** Resets this codeplug zone representation from the given generic @c Zone object. */\n    virtual void fromZoneObjB(const Zone *zone, Context &ctx);\n\n  protected:\n    /** Just defines the offsets with the element. */\n    enum Offsets {\n      OffsetName = 0x00, OffsetCount = 0x20, OffsetChannel = 0x22, StringLength = 0x20\n    };\n  };\n\npublic:\n  /** Hidden constructor, use a device specific class to instantiate. */\n  explicit OpenRTXCodeplug(QObject *parent=nullptr);\n\n  /** Destructor. */\n  virtual ~OpenRTXCodeplug();\n\n  /** Clears and resets the complete codeplug to some default values. */\n  virtual void clear();\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n  bool postprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n\n  Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n  bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Decodes the binary codeplug and stores its content in the given generic configuration using\n   * the given context. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes the given generic configuration as a binary codeplug using the given context. */\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Returns the number of stored contacts. */\n  virtual unsigned int numContacts();\n  /** Returns the offset to the n-th contact element. */\n  virtual unsigned int offsetContact(unsigned int n);\n  /** Encodes all digital contacts in the configuration into the codeplug. */\n  virtual bool encodeContacts(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Adds a digital contact to the configuration for each one in the codeplug. */\n  virtual bool createContacts(Config *config, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Returns the number of stored channels. */\n  virtual unsigned int numChannels();\n  /** Returns the offset to the n-th channel element. */\n  virtual unsigned int offsetChannel(unsigned int n);\n  /** Encode all channels. */\n  virtual bool encodeChannels(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Adds all defined channels to the configuration. */\n  virtual bool createChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links all channels. */\n  virtual bool linkChannels(Config *config, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Returns the number of stored zones. */\n  virtual unsigned int numZones();\n  /** Returns the offset to the zone offset array. */\n  virtual unsigned int offsetZoneOffsets();\n  /** Returns the offset to the n-th zone element. */\n  virtual unsigned int offsetZone(unsigned int n);\n  /** Encodes zones. */\n  virtual bool encodeZones(Config *config, const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Adds zones to the configuration. */\n  virtual bool createZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Links all zones within the configuration. */\n  virtual bool linkZones(Config *config, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Just stores some sizes. */\n  enum Offsets {\n    HeaderSize = 0x58, ChannelSize = 0x5a, ContactSize = 0x27, ZoneHeaderSize=0x22\n  };\n};\n\n#endif // OPENRTX_CODEPLUG_HH\n"
  },
  {
    "path": "lib/openrtx_interface.cc",
    "content": "#include \"openrtx_interface.hh\"\n#include \"usbserial.hh\"\n#include \"logger.hh\"\n#include <QSerialPort>\n#include <QSerialPortInfo>\n\n#define USB_VID 0x0483\n#define USB_PID 0x5740\n\n\nOpenRTXInterface::OpenRTXInterface(const USBDeviceDescriptor &descriptor, const ErrorStack &err, QObject *parent)\n  : QObject(parent), RadioInterface(), _rtxLink(nullptr)\n{\n  if (USBDeviceInfo::Class::Serial != descriptor.interfaceClass()) {\n    errMsg(err) << \"Cannot open serial port for a non-serial descriptor: \"\n                << descriptor.description();\n    return;\n  }\n\n  auto serial = new QSerialPort(this);\n  logDebug() << \"Try to open \" << descriptor.description() << \".\";\n  QSerialPortInfo port(descriptor.device().toString());\n  serial->setPort(port);\n  if (! serial->open(QIODevice::ReadWrite)) {\n    errMsg(err) << \"Cannot open port: \" << serial->errorString();\n    return;\n  }\n\n  auto slip = new SlipStream(serial, this);\n  _rtxLink = new OpenRTXLink(slip, this);\n}\n\n\nbool\nOpenRTXInterface::isOpen() const {\n  return _rtxLink && _rtxLink->isOpen();\n}\n\nvoid\nOpenRTXInterface::close() {\n  if (_rtxLink) _rtxLink->close();\n}\n\n\nRadioInfo\nOpenRTXInterface::identifier(const ErrorStack &err) {\n  QByteArray id = _rtxLink->cat()->info(err);\n  if (id.isEmpty())\n    return RadioInfo();\n\n  logInfo() << \"Detected '\" << QString::fromLatin1(id) << \"'.\";\n  return RadioInfo::byID(RadioInfo::OpenRTX);\n}\n\n\nbool\nOpenRTXInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  return false;\n}\n\nbool\nOpenRTXInterface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  return false;\n}\n\nbool\nOpenRTXInterface::read_finish(const ErrorStack &err) {\n  return false;\n}\n\n\nbool\nOpenRTXInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  return false;\n}\n\nbool\nOpenRTXInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  return false;\n}\n\nbool\nOpenRTXInterface::write_finish(const ErrorStack &err) {\n  return false;\n}\n\nbool\nOpenRTXInterface::reboot(const ErrorStack &err) {\n  return false;\n}\n\n\n\nUSBDeviceInfo\nOpenRTXInterface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::Serial, USB_VID, USB_PID, false);\n}\n\nQList<USBDeviceDescriptor>\nOpenRTXInterface::detect(bool saveOnly) {\n  QList<USBDeviceDescriptor> interfaces;\n  // Find matching serial port by VID/PID.\n  logDebug() << \"Search for serial port with matching VID:PID \" <<\n    QString::number(USB_VID, 16) << \":\" << QString::number(USB_PID, 16) << \".\";\n  QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();\n  foreach (QSerialPortInfo port, ports) {\n    if (port.hasProductIdentifier() && (USB_PID == port.productIdentifier()) &&\n        port.hasVendorIdentifier() && (USB_VID == port.vendorIdentifier())) {\n      interfaces.append(USBSerial::Descriptor(USB_VID, USB_PID, port.portName(), saveOnly));\n      logDebug() << \"Found \" << port.portName() << \" (USB \"\n                 << QString::number(USB_VID, 16) << \":\" << QString::number(USB_PID, 16) << \").\";\n    }\n  }\n  return interfaces;\n}\n"
  },
  {
    "path": "lib/openrtx_interface.hh",
    "content": "#ifndef OPENRTXINTERFACE_HH\n#define OPENRTXINTERFACE_HH\n\n#include \"radiointerface.hh\"\n#include \"packetstream.hh\"\n#include \"usbserial.hh\"\n#include \"xmodem.hh\"\n\n/** Implements the communication interface to radios running the OpenRTX firmware.\n *\n * The protocol is called rtxlink and is documented at https://openrtx.org/#/rtxlink. The protocol\n * has several layers. The lowest is a serial interface either as a VCOM (UBS CDC-ACM) or a proper\n * hardware UART. Ontop of that, there is SLIP. Followed by a simple framing layer, that determines\n * the higher-level protocol.\n * @ingroup ortx */\nclass OpenRTXInterface : public QObject, public RadioInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param descr The USB device descriptor. Used to identify a specific USB device.\n   * @param err The stack of error messages.\n   * @param parent The QObject parent. */\n  explicit OpenRTXInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent = nullptr);\n\n  bool isOpen() const;\n  void close();\n\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\n  bool reboot(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about this interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** OpenRTX link interface. This is the protocol dispatcher for all implemented data transfer\n   * protocols. */\n  OpenRTXLink *_rtxLink;\n};\n\n#endif // OPENRTXINTERFACE_HH\n"
  },
  {
    "path": "lib/openrtx_link.cc",
    "content": "#include \"openrtx_link.hh\"\n\n\n/* ******************************************************************************************** *\n * Implements the OpenRTX link stream socket\n * ******************************************************************************************** */\nOpenRTXLinkStream::OpenRTXLinkStream(OpenRTXLink::Protocol proto, OpenRTXLink *link)\n  : QIODevice(link), _proto(proto), _inBuffer(), _link(link)\n{\n  // pass...\n}\n\nqint64\nOpenRTXLinkStream::writeData(const char *data, qint64 len) {\n  QByteArray buffer(data, len);\n\n  ErrorStack err;\n  if (! _link->send(OpenRTXLink::Protocol::Stdio, buffer, -1, err)) {\n    setErrorString(err.format());\n    return -1;\n  }\n\n  return len;\n}\n\nqint64\nOpenRTXLinkStream::readData(char *data, qint64 maxlen) {\n  ErrorStack err;\n  qint64 n_read = 0, n_toread=maxlen;\n\n  while (0 < n_toread) {\n    if (_inBuffer.size()) {\n      qint64 n = std::min((qint64)_inBuffer.size(), n_toread);\n      memcpy(data, _inBuffer.data(), n);\n      _inBuffer.remove(0, n);\n      n_toread -= n; data+= n; n_read += n;\n    }\n    if (0 == n_toread)\n      return n_read;\n    if (! _link->receive(OpenRTXLink::Protocol::Stdio, _inBuffer, -1, err)) {\n      setErrorString(err.format());\n      return -1;\n    }\n  }\n\n  return n_read;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implements the OpenRTX link datagram socket\n * ******************************************************************************************** */\nOpenRTXLinkDatagram::OpenRTXLinkDatagram(OpenRTXLink::Protocol proto, OpenRTXLink *link)\n  : PacketStream(link), _proto(proto), _link(link)\n{\n  // pass...\n}\n\nbool\nOpenRTXLinkDatagram::isOpen() const {\n  return _link->isOpen();\n}\n\nvoid\nOpenRTXLinkDatagram::close() {\n  // Cannot close data-gram socket by its own.\n}\n\nbool\nOpenRTXLinkDatagram::receive(QByteArray &buffer, int timeout, const ErrorStack &err) {\n  return _link->receive(_proto, buffer, timeout, err);\n}\n\nbool\nOpenRTXLinkDatagram::send(const QByteArray &buffer, int timeout, const ErrorStack &err) {\n  return _link->send(_proto, buffer, timeout, err);\n}\n\n\n/* ******************************************************************************************** *\n * Implements the OpenRTX CAT interface\n * ******************************************************************************************** */\nOpenRTXCAT::OpenRTXCAT(OpenRTXLink *link)\n  : OpenRTXLinkDatagram(OpenRTXLink::Protocol::CAT, link)\n{\n  // pass...\n}\n\nQByteArray\nOpenRTXCAT::info(const ErrorStack &err) {\n  if (! send(QByteArray(\"GIN\",3), _link->timeout(), err)) {\n    errMsg(err) << \"Cannot request device info.\";\n    return QByteArray();\n  }\n\n  QByteArray res;\n  if (! receive(res, _link->timeout(), err)) {\n    errMsg(err) << \"Cannot receive device info.\";\n    return QByteArray();\n  }\n\n  if ('A' == res[0]) {\n    errMsg(err) << \"Cannot obtain device info. Received error code \" << (int8_t)res[1];\n    return QByteArray();\n  } else if ('D' == res[0]) {\n    return res.right(16);\n  }\n\n  errMsg(err) << \"Obtained unexpected response \" << res.toHex(' ');\n  return QByteArray();\n}\n\n\n\n/* ******************************************************************************************** *\n * Implements the OpenRTX link protocol dispatcher\n * ******************************************************************************************** */\nOpenRTXLink::OpenRTXLink(PacketStream *link, QObject *parent)\n  : QObject{parent}, _link(link),\n    _stdio(new OpenRTXLinkStream(Protocol::Stdio, this)),\n    _cat(new OpenRTXCAT(this)),\n    _fmp(new OpenRTXLinkDatagram(Protocol::FMP, this)),\n    _xmodem(new OpenRTXLinkStream(Protocol::XMODEM, this)),\n    _timeout(2000)\n{\n  if (_link) _link->setParent(this);\n}\n\nOpenRTXLink::~OpenRTXLink() {\n  if (_link)\n    _link->close();\n}\n\nbool\nOpenRTXLink::isOpen() const {\n  return _link && _link->isOpen();\n}\n\nvoid\nOpenRTXLink::close() {\n  if (_link) _link->close();\n}\n\n\nOpenRTXLinkStream *\nOpenRTXLink::stdio() const {\n  return _stdio;\n}\n\nOpenRTXCAT *OpenRTXLink::cat() const {\n  return _cat;\n}\n\nOpenRTXLinkDatagram *\nOpenRTXLink::fmp() const {\n  return _fmp;\n}\n\nOpenRTXLinkStream *\nOpenRTXLink::xmodem() const {\n  return _xmodem;\n}\n\nunsigned int\nOpenRTXLink::timeout() const {\n  return _timeout;\n}\n\nuint16_t\nOpenRTXLink::crc16(const QByteArray &data) {\n  uint16_t x = 0, crc = 0;\n  for (qsizetype i=0; i<data.length(); i++) {\n    x = (crc >> 8) ^ data[i];\n    x ^= x >> 4;\n    crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;\n  }\n  return crc;\n}\n\nbool\nOpenRTXLink::send(Protocol proto, const QByteArray &data, int timeout, const ErrorStack &err) {\n  QByteArray packet; packet.reserve(data.size()+3);\n  packet.append((char)proto);\n  packet.append(data);\n  uint16_t crc = crc16(data);\n  packet.append(crc&0xff);\n  packet.append(crc>>8);\n  return this->_link->send(packet, timeout, err);\n}\n\nbool\nOpenRTXLink::receive(Protocol proto, QByteArray &data, int timeout, const ErrorStack &err) {\n  QByteArray buffer;\n  while (true) {\n    // Receive a datagram\n    if (! this->_link->receive(buffer, timeout, err))\n      return false;\n\n    // Check CRC\n    if (0 != crc16(buffer)) {\n      errMsg(err) << \"Invalid CRC in RTXLink packet.\";\n      return false;\n    }\n\n    // Dispatch by type\n    Protocol rxProto = (Protocol)buffer.at(0);\n    // If requested type matches -> done\n    if (proto == rxProto) {\n      data = buffer.mid(1, buffer.size()-2);\n      return true;\n    }\n\n    // Otherwise, store and receive next\n    if (! _inBuffers.contains((unsigned int)rxProto))\n      _inBuffers.insert((unsigned int)rxProto, QList<QByteArray>());\n    _inBuffers[(unsigned int)rxProto].append(buffer.mid(1,buffer.size()-2));\n  }\n}\n"
  },
  {
    "path": "lib/openrtx_link.hh",
    "content": "#ifndef OPENRTXLINK_HH\n#define OPENRTXLINK_HH\n\n#include <QObject>\n#include <QHash>\n#include <QIODevice>\n#include \"packetstream.hh\"\n\n// Forward declaration\nclass OpenRTXLink;\nclass OpenRTXLinkStream;\nclass OpenRTXLinkDatagram;\nclass OpenRTXCAT;\n\n\n/** Implements the OpenRTX link protocol. This is a datagram-oriented protocol, that dispatches\n * several different protocols to talk to the radio. It provides a stream, to the stdio of the\n * radio, a CAT interface, a file-system management protocol (FMP) as well as XMODEM to transfer\n * files. The file transfer must be initialized via the FMP.\n *\n * All these protocols are exposed through specialized interface objects, accessible through this\n * classs.\n *\n * @code\n *  +------------------+-------------------+-------------------+------------------+\n *  |      stdio       |        CAT        |        FMP        |       XMODEM     |\n *  +------------------+-------------------+-------------------+------------------+\n *  |                                 OpenRTXLink                                 |\n *  +-----------------------------------------------------------------------------+\n *  |                              SLIP (SlipStream)                              |\n *  +-----------------------------------------------------------------------------+\n *  |                  USB CDC-ACM (VCOM/Serial-over-USB, USBSerial)              |\n *  +-----------------------------------------------------------------------------+\n * @endcode\n * @ingroup ortx */\nclass OpenRTXLink: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** The possible protocols, encapsulated in OpenRTX link. */\n  enum class Protocol {\n    Stdio = 0, CAT = 1, FMP = 2, XMODEM = 3\n  };\n\npublic:\n  /** Constructor.\n   * @param link Specifies the datagram socket to talk to the radio (SLIP).\n   * @param parent Specifies the QObject parent. */\n  explicit OpenRTXLink(PacketStream *link, QObject *parent = nullptr);\n  virtual ~OpenRTXLink();\n\n  virtual bool isOpen() const;\n  virtual void close();\n\n  /** Returns a stream to the stdio of the radio. Implements the @c QIODevice interface. */\n  OpenRTXLinkStream *stdio() const;\n  /** The CAT interface to the radio. */\n  OpenRTXCAT *cat() const;\n  /** The file-system management protocol interface to the radio. */\n  OpenRTXLinkDatagram *fmp() const;\n  /** An XMODEM channel to transfer files. */\n  OpenRTXLinkStream *xmodem() const;\n\n  /** Returns the timeout in milliseconds. */\n  unsigned int timeout() const;\n\nprotected:\n  /** Dispatcher to receive datagrams over OpenRTXLink. */\n  bool receive(Protocol proto, QByteArray &data, int timeout=-1, const ErrorStack &err=ErrorStack());\n  /** Dispatcher to send datagrams over OpenRTXLink. */\n  bool send(Protocol proto, const QByteArray &data, int timeout=-1, const ErrorStack &err=ErrorStack());\n\n  static uint16_t crc16(const QByteArray &data);\n\nprotected:\n  /** Owns the packet stream to the device. */\n  PacketStream *_link;\n  QHash<unsigned int, QList<QByteArray>> _inBuffers;\n  OpenRTXLinkStream *_stdio;\n  OpenRTXCAT        *_cat;\n  OpenRTXLinkDatagram *_fmp;\n  OpenRTXLinkStream *_xmodem;\n  unsigned int _timeout;\n\n  friend class OpenRTXLinkStream;\n  friend class OpenRTXLinkDatagram;\n  friend class OpenRTXCAT;\n};\n\n\n/** An abstract stream encapsulated within OpenRTXLink. */\nclass OpenRTXLinkStream: public QIODevice\n{\n  Q_OBJECT\n\nprotected:\n  OpenRTXLinkStream(OpenRTXLink::Protocol proto, OpenRTXLink *link);\n\nprotected:\n  qint64 writeData(const char *data, qint64 len);\n  qint64 readData(char *data, qint64 maxlen);\n\nprotected:\n  OpenRTXLink::Protocol _proto;\n  QByteArray _inBuffer;\n  OpenRTXLink *_link;\n\n  friend class OpenRTXLink;\n};\n\n\nclass OpenRTXLinkDatagram: public PacketStream\n{\n  Q_OBJECT\n\nprotected:\n  OpenRTXLinkDatagram(OpenRTXLink::Protocol proto, OpenRTXLink *link);\n\npublic:\n  bool isOpen() const override;\n  void close() override;\n  bool receive(QByteArray &buffer, int timeout, const ErrorStack &err) override;\n  bool send(const QByteArray &buffer, int timeout, const ErrorStack &err) override;\n\nprotected:\n  OpenRTXLink::Protocol _proto;\n  OpenRTXLink *_link;\n\n  friend class OpenRTXLink;\n};\n\n\n/** Implements the CAT interface to OpenRTX devices. */\nclass OpenRTXCAT: public OpenRTXLinkDatagram\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constrcutor. An instance of this class can be obtained from @c OpenRTXLink.\n   *  @param link Specifies the unerlying RTX-link to the the device. */\n  explicit OpenRTXCAT(OpenRTXLink *link);\n\npublic:\n  QByteArray info(const ErrorStack &err=ErrorStack());\n  bool enterFMPMode(const ErrorStack &err=ErrorStack());\n\nprotected:\n  friend class OpenRTXLink;\n};\n\n#endif // OPENRTXLINK_HH\n"
  },
  {
    "path": "lib/openuv380.cc",
    "content": "#include \"openuv380.hh\"\n#include \"openuv380_satelliteconfig.hh\"\n\n\nOpenUV380::OpenUV380(OpenGD77Interface *device, QObject *parent)\n  : OpenGD77Base(device, parent), _name(\"Open MD-UV380\"), _codeplug(),\n  _callsigns(device->extendedCallsignDB())\n{\n  _satelliteConfig = new OpenUV380SatelliteConfig(this);\n}\n\n\nconst QString &\nOpenUV380::name() const {\n  return _name;\n}\n\n\nconst Codeplug &\nOpenUV380::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nOpenUV380::codeplug() {\n  return _codeplug;\n}\n\n\nconst CallsignDB *\nOpenUV380::callsignDB() const {\n  return &_callsigns;\n}\n\nCallsignDB *\nOpenUV380::callsignDB() {\n  return &_callsigns;\n}\n\n\nRadioInfo\nOpenUV380::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::OpenUV380, \"openuv380\", \"OpenMDUV380\", \"OpenGD77 Project\",\n        {OpenGD77Interface::interfaceInfo()});\n}\n\n\n"
  },
  {
    "path": "lib/openuv380.hh",
    "content": "#ifndef OPENUV380_HH\n#define OPENUV380_HH\n\n#include \"opengd77base.hh\"\n#include \"openuv380_codeplug.hh\"\n#include \"openuv380_callsigndb.hh\"\n\n\n/** Implements an USB interface to Open UV380 VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * @ingroup ogd77 */\nclass OpenUV380 : public OpenGD77Base\n{\n\tQ_OBJECT\n\npublic:\n\t/** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit OpenUV380(OpenGD77Interface *device=nullptr, QObject *parent=nullptr);\n\n\tconst QString &name() const;\n\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprotected:\n  /** The device identifier. */\n\tQString _name;\n  /** The actual binary codeplug representation. */\n  OpenUV380Codeplug _codeplug;\n  /** The actual binary callsign DB representation. */\n  OpenUV380CallsignDB _callsigns;\n};\n\n#endif // OPENGD77_HH\n"
  },
  {
    "path": "lib/openuv380_callsigndb.cc",
    "content": "#include \"openuv380_callsigndb.hh\"\n#include \"utils.hh\"\n#include \"userdatabase.hh\"\n#include \"logger.hh\"\n#include <QtEndian>\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenUV380CallsignDB::DatabaseEntryElement\n * ******************************************************************************************** */\nOpenUV380CallsignDB::DatabaseEntryElement::DatabaseEntryElement(uint8_t *ptr)\n  : OpenGD77BaseCallsignDB::DatabaseEntryElement(ptr, size())\n{\n  // pass...\n}\n\nvoid\nOpenUV380CallsignDB::DatabaseEntryElement::clear() {\n  memset(_data, 0x00, size());\n}\n\nvoid\nOpenUV380CallsignDB::DatabaseEntryElement::setText(const QString &text) {\n  QByteArray data = pack(text);\n  auto n = std::min(3*Limit::textLength()/4, (unsigned int)data.size());\n  memset(_data+Offset::text(), 0, 3*Limit::textLength()/4);\n  memcpy(_data+Offset::text(), data.constData(), n);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenUV380CallsignDB\n * ******************************************************************************************** */\nOpenUV380CallsignDB::OpenUV380CallsignDB(bool extended, QObject *parent)\n  : OpenGD77BaseCallsignDB(parent)\n{\n  addImage(\"OpenUV380 call-sign database\");\n  if (extended)\n    logDebug() << \"Used extended call-sign DB memory.\";\n}\n\n\nbool\nOpenUV380CallsignDB::encode(UserDatabase *calldb, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Limit entry count\n  auto n = std::min((unsigned int)calldb->count(), Limit::entries());\n  if (selection.hasCountLimit())\n    n = std::min(n, (unsigned int)selection.countLimit());\n  // If there are no entries -> done.\n  if (0 == n)\n    return true;\n  auto n0 = std::min(n, Limit::entries0()),\n      n1 = std::min(n - n0, Limit::entries1());\n\n  // Select first n entries and sort them in ascending order of their IDs\n  QVector<UserDatabase::User> users;\n  for (unsigned i=0; i<n; i++)\n    users.append(calldb->user(i));\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n  if (n)\n    logDebug() << \"Store \" << n << \" entries for OpenUV380 starting from \"\n               << users.front().id << \":\" << users.front().call << \", \" << users.front().name << \" in \" << users.front().city\n               << \" to \" << users.back().id << \":\" << users.back().call << \", \" << users.back().name << \" in \" << users.back().city;\n\n  // Allocate segment0 for user db if requested\n  unsigned size = align_size(DatabaseHeaderElement::size()+n0*DatabaseEntryElement::size(),\n                             Limit::blockSize());\n  this->image(0).addElement(Offset::header(), size);\n  size = align_size(n1*DatabaseEntryElement::size(), Limit::blockSize());\n  if (n1)\n    this->image(0).addElement(Offset::entries1(), size);\n\n  // Encode user DB\n  DatabaseHeaderElement header(this->data(Offset::header()));\n  header.clear(); header.setEntrySize(DatabaseEntryElement::size()); header.setEntryCount(n);\n  for (unsigned i=0; i<n0; i++) {\n    DatabaseEntryElement(this->data(Offset::entries0() + i*DatabaseEntryElement::size()))\n        .fromEntry(users[i]);\n  }\n  for (unsigned i=0; i<n1; i++) {\n    DatabaseEntryElement(this->data(Offset::entries1() + i*DatabaseEntryElement::size()))\n        .fromEntry(users[n0+i]);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/openuv380_callsigndb.hh",
    "content": "#ifndef OPENUV380CALLSIGNDB_HH\n#define OPENUV380CALLSIGNDB_HH\n\n#include \"opengd77base_callsigndb.hh\"\n#include \"userdatabase.hh\"\n\n/** Represents and encodes the binary format for the call-sign database within the radio.\n *\n * The memory layout of the call-sign DB is relatively simple. The DB starts at address\n * @c 0x00030000 with a maximum size of @c 0x00040000. The first 12bytes form the DB header\n * (see @c OpenGD77CallsignDB::userdb_t) followed by the DB entries (see\n * @c OpenGD77CallsignDB::userdb_entry_t).\n *\n * The entries can be of variable size. The size of each entry is encoded in the header. QDMR uses\n * a fixed size of 19bytes per entry. The entries must be sorted in ascending order to allow for an\n * efficient binary search. No index table is used here.\n *\n * @ingroup ogd77 */\nclass OpenUV380CallsignDB : public OpenGD77BaseCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  class DatabaseEntryElement: public OpenGD77BaseCallsignDB::DatabaseEntryElement\n  {\n  public:\n    /** Constructor. */\n    DatabaseEntryElement(uint8_t *ptr);\n\n    /** The size of the entry. */\n    static constexpr unsigned int size() { return 0x001b; }\n\n    void clear() override;\n\n    /** Encodes the text. */\n    void setText(const QString &text) override;\n\n  public:\n    /** Some limits. */\n    struct Limit: public Element::Limit {\n      // The length of the text.\n      static constexpr unsigned int textLength() { return 32; }\n    };\n  };\n\npublic:\n  /** Constructor.\n   * @param extended If @c true, some extended callsign db memory is used. */\n  explicit OpenUV380CallsignDB(bool extended, QObject *parent=nullptr);\n\n  static constexpr unsigned int size0() { return 0x040000; }\n  static constexpr unsigned int size1() { return 0xd28000; }\n\n  /** Encodes as many entries as possible of the given user-database. */\n  bool encode(UserDatabase *calldb, const Flags &selection=Flags(),\n              const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits of the callsign DB. */\n  struct Limit: public OpenGD77BaseCallsignDB::Limit {\n    /// Number of entries, segment 0.\n    static constexpr unsigned int entries0() {\n      return (size0()-DatabaseHeaderElement::size())/DatabaseEntryElement::size();\n    }\n    /// Number of entries, segment 1.\n    static constexpr unsigned int entries1() {\n      return size1()/DatabaseEntryElement::size();\n    }\n    static constexpr unsigned int entries() {\n      return entries0() + entries1();\n    }\n  };\n\nprotected:\n  /** Some internal offsets within the callsign db. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int header()   { return 0x050000; }\n    static constexpr unsigned int entries0() { return header() + DatabaseHeaderElement::size(); }\n    static constexpr unsigned int entries1() { return 0x0d8000; }\n    /// @endcond\n  };\n};\n\n#endif // OPENGD77CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/openuv380_codeplug.cc",
    "content": "#include \"openuv380_codeplug.hh\"\n#include \"config.hh\"\n#include \"channel.hh\"\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include <QDateTime>\n#include <QtEndian>\n#include \"opengd77_extension.hh\"\n\n\n/* ******************************************************************************************** *\n * Implementation of OpenUV380Codeplug\n * ******************************************************************************************** */\nOpenUV380Codeplug::OpenUV380Codeplug(QObject *parent)\n  : OpenGD77BaseCodeplug(parent)\n{\n  addImage(\"OpenGD77 Codeplug EEPROM\");\n  addImage(\"OpenGD77 Codeplug FLASH\");\n  image(FLASH).addElement(0x00000080, 0x00005fe0);\n  image(FLASH).addElement(0x00007500, 0x00003b00);\n  image(FLASH).addElement(0x00020000, AdditionalSettingsElement::size());\n  image(FLASH).addElement(0x0009b000, 0x00013e60);\n}\n\nvoid\nOpenUV380Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  GeneralSettingsElement el(data(Offset::settings(), ImageIndex::settings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n\n  return el.encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings(), ImageIndex::settings())).decode(ctx, err);\n}\n\n\nvoid\nOpenUV380Codeplug::clearDTMFSettings() {\n  //DTMFSettingsElement(data(Offset::settings(), ImageIndex::settings())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\nbool\nOpenUV380Codeplug::decodeDTMFSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n\n\nvoid\nOpenUV380Codeplug::clearAPRSSettings() {\n  APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  APRSSettingsBankElement el(data(Offset::aprsSettings(), ImageIndex::aprsSettings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n\n  return el.encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::decodeAPRSSettings(Context &ctx, const ErrorStack &err) {\n  return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings()))\n      .decode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::linkAPRSSettings(Context &ctx, const ErrorStack &err) {\n  return APRSSettingsBankElement(data(Offset::aprsSettings(), ImageIndex::aprsSettings()))\n      .link(ctx, err);\n}\n\n\nbool\nOpenUV380Codeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  // Encode boot melody if set\n  if (ctx.config()->settings()->tone()->bootToneEnabled() && !ctx.config()->settings()->tone()->bootMelody()->isEmpty()) {\n    AdditionalSettingsElement opt(data(Offset::additionalSettings(), ImageIndex::additionalSettings()));\n    if (! flags.updateCodeplug() && ! opt.isValid())\n      opt.clear();\n    opt.bootMelody().encode(ctx, ctx.config()->settings()->tone()->bootMelody(), err);\n  }\n  // Encode other boot settings\n  return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings()))\n      .encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) {\n  // Check if boot melody is encoded\n  AdditionalSettingsElement opt(data(Offset::additionalSettings(), ImageIndex::additionalSettings()));\n  if (opt.hasSettings(AdditionalSettingsElement::BootMelody)) {\n    ctx.config()->settings()->tone()->enableBootTone(\n      opt.bootMelody().decode(ctx, ctx.config()->settings()->tone()->bootMelody(), err));\n  }\n  return BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings()))\n      .decode(ctx, err);\n}\n\n\nvoid\nOpenUV380Codeplug::clearContacts() {\n  ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  return ContactBankElement(data(Offset::contacts(), ImageIndex::contacts())).decode(ctx, err);\n}\n\n\nvoid\nOpenUV380Codeplug::clearDTMFContacts() {\n  DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts()))\n      .encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return DTMFContactBankElement(data(Offset::dtmfContacts(), ImageIndex::dtmfContacts()))\n      .decode(ctx, err);\n}\n\n\nvoid\nOpenUV380Codeplug::clearChannels() {\n  for (unsigned int b=0; b<Limit::channelBanks(); b++) {\n    if (0 == b)\n      ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0())).clear();\n    else\n      ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(),\n                              ImageIndex::channelBank1())).clear();\n  }\n}\n\nbool\nOpenUV380Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  for (unsigned int b=0,c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i<ChannelBankElement::Limit::channelCount(); i++, c++) {\n      if (ctx.has<Channel>(c)) {\n        if (! bank.channel(i).encode(ctx.get<Channel>(c), ctx, err)) {\n          errMsg(err) << \"Cannot encode channel '\" << ctx.get<Channel>(c)->name()\n                      << \"' at index \" << i << \" of bank \" << b << \".\";\n          return false;\n        }\n        bank.enable(i, true);\n      } else {\n        bank.enable(i, false);\n      }\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenUV380Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int b=0,c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i < ChannelBankElement::Limit::channelCount(); i++) {\n      if (! bank.isEnabled(i))\n        continue;\n\n      Channel *obj = bank.channel(i).decode(ctx, err);\n      if (nullptr == obj) {\n        errMsg(err) << \"Cannot create channel from index \" << i << \" in bank \" << b << \".\";\n        return false;\n      }\n      ctx.config()->channelList()->add(obj);\n      ctx.add(obj, c++);\n    }\n  }\n\n  return true;\n}\n\nbool\nOpenUV380Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int b=0,c=0; b<Limit::channelBanks(); b++) {\n    ChannelBankElement bank(nullptr);\n    if (0 == b)\n      bank = ChannelBankElement(data(Offset::channelBank0(), ImageIndex::channelBank0()));\n    else\n      bank = ChannelBankElement(data(Offset::channelBank1() + (b-1)*ChannelBankElement::size(), ImageIndex::channelBank1()));\n\n    for (unsigned int i=0; i < ChannelBankElement::Limit::channelCount(); i++) {\n      if (! bank.isEnabled(i))\n        continue;\n\n      Channel *obj = ctx.get<Channel>(c);\n      ChannelElement element = bank.channel(i);\n\n      assert(obj->name() == element.name());\n\n      if (! element.link(obj, ctx, err)) {\n        errMsg(err) << \"Cannot link channel '\" << obj->name()\n                    << \"' from index \" << i << \" in bank \" << b << \".\";\n        return false;\n      }\n      c++;\n    }\n  }\n\n  return true;\n}\n\nvoid\nOpenUV380Codeplug::clearBootSettings() {\n  BootSettingsElement(data(Offset::bootSettings(), ImageIndex::bootSettings())).clear();\n}\n\n\nvoid\nOpenUV380Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(Offset::vfoA(), ImageIndex::vfoA())).clear();\n  VFOChannelElement(data(Offset::vfoB(), ImageIndex::vfoB())).clear();\n}\n\nvoid\nOpenUV380Codeplug::clearZones() {\n  ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).decode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  return ZoneBankElement(data(Offset::zoneBank(), ImageIndex::zoneBank())).link(ctx, err);\n}\n\nvoid\nOpenUV380Codeplug::clearGroupLists() {\n  GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).clear();\n}\n\nbool\nOpenUV380Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).encode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).decode(ctx, err);\n}\n\nbool\nOpenUV380Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  return GroupListBankElement(data(Offset::groupLists(), ImageIndex::groupLists())).link(ctx, err);\n}\n\n"
  },
  {
    "path": "lib/openuv380_codeplug.hh",
    "content": "#ifndef OPENUV380_CODEPLUG_HH\n#define OPENUV380_CODEPLUG_HH\n\n#include \"opengd77base_codeplug.hh\"\n#include \"opengd77_extension.hh\"\n\n\n/** Represents, encodes and decodes the device specific codeplug for Open MD-UV380 firmware.\n *\n * This codeplug is almost identical to the original GD77 codeplug.\n *\n * @ingroup ogd77 */\nclass OpenUV380Codeplug: public OpenGD77BaseCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs an empty codeplug for the Open MD-UV380. */\n  explicit OpenUV380Codeplug(QObject *parent=nullptr);\n\npublic:\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFSettings();\n  bool encodeDTMFSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeDTMFSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearAPRSSettings();\n  bool encodeAPRSSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkAPRSSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFContacts();\n  bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearBootSettings();\n  bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearVFOSettings();\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some Limits for this codeplug. */\n  struct Limit: public Element::Limit {\n    /** Number of channel banks. */\n    static constexpr unsigned int channelBanks() { return 8; }\n  };\n\nprotected:\n  /** Internal used image indices. */\n  struct ImageIndex {\n    /// @cond DO_NOT_DOCUEMNT\n    static constexpr unsigned int settings()           { return FLASH; }\n    static constexpr unsigned int dtmfSettings()       { return FLASH; }\n    static constexpr unsigned int aprsSettings()       { return FLASH; }\n    static constexpr unsigned int dtmfContacts()       { return FLASH; }\n    static constexpr unsigned int channelBank0()       { return FLASH; }\n    static constexpr unsigned int bootSettings()       { return FLASH; }\n    static constexpr unsigned int vfoA()               { return FLASH; }\n    static constexpr unsigned int vfoB()               { return FLASH; }\n    static constexpr unsigned int zoneBank()           { return FLASH; }\n    static constexpr unsigned int additionalSettings() { return FLASH; }\n    static constexpr unsigned int channelBank1()       { return FLASH; }\n    static constexpr unsigned int contacts()           { return FLASH; }\n    static constexpr unsigned int groupLists()         { return FLASH; }\n    /// @endcond\n  };\n\n  /** Some offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUEMNT\n    static constexpr unsigned int settings()           { return 0x00000080; }\n    static constexpr unsigned int dtmfSettings()       { return 0x00001470; }\n    static constexpr unsigned int aprsSettings()       { return 0x00001588; }\n    static constexpr unsigned int dtmfContacts()       { return 0x00002f88; }\n    static constexpr unsigned int channelBank0()       { return 0x00003780; } // Channels 1-128\n    static constexpr unsigned int bootSettings()       { return 0x00007518; }\n    static constexpr unsigned int vfoA()               { return 0x00007590; }\n    static constexpr unsigned int vfoB()               { return 0x000075c8; }\n    static constexpr unsigned int zoneBank()           { return 0x00008010; }\n    static constexpr unsigned int additionalSettings() { return 0x00020000; }\n    static constexpr unsigned int channelBank1()       { return 0x0009b1b0; } // Channels 129-1024\n    static constexpr unsigned int contacts()           { return 0x000a7620; }\n    static constexpr unsigned int groupLists()         { return 0x000ad620; }\n    /// @endcond\n  };\n};\n\n#endif // OPENUV380_CODEPLUG_HH\n"
  },
  {
    "path": "lib/openuv380_satelliteconfig.cc",
    "content": "#include \"openuv380_satelliteconfig.hh\"\n#include \"errorstack.hh\"\n\n\nOpenUV380SatelliteConfig::OpenUV380SatelliteConfig(QObject *parent)\n  : OpenGD77BaseSatelliteConfig(parent)\n{\n  image(FLASH).addElement(Offset::satellites(), size()); // of 0x11a0 bytes\n}\n\nbool\nOpenUV380SatelliteConfig::isValid() const {\n  return OpenGD77BaseCodeplug::AdditionalSettingsElement((uint8_t *)data(Offset::satellites(), FLASH))\n      .isValid();\n}\n\n\nvoid\nOpenUV380SatelliteConfig::initialize() {\n  OpenGD77BaseCodeplug::AdditionalSettingsElement(data(Offset::satellites(), FLASH)).clear();\n}\n\n\nbool\nOpenUV380SatelliteConfig::encode(SatelliteDatabase *db, const ErrorStack &err) {\n  OpenGD77BaseCodeplug::AdditionalSettingsElement settings(data(Offset::satellites(), FLASH));\n  if (! settings.isValid()) {\n    errMsg(err) << \"Cannot encode satellite config for OpenUV380: Invalid settings element.\";\n    return false;\n  }\n\n  OpenGD77BaseCodeplug::SatelliteBankElement bank = settings.satellites();\n  bank.clear();\n\n  if (! bank.encode(db, err)) {\n    errMsg(err) << \"Cannot encode satellite config for OpenUV380.\";\n    return false;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/openuv380_satelliteconfig.hh",
    "content": "#ifndef OPENUV380_SATELLITECONFIG_HH\n#define OPENUV380_SATELLITECONFIG_HH\n\n#include \"opengd77base_codeplug.hh\"\n#include \"opengd77base_satelliteconfig.hh\"\n\n\nclass OpenUV380SatelliteConfig : public OpenGD77BaseSatelliteConfig\n{\n  Q_OBJECT\n\npublic:\n  /** Default constructor. */\n  explicit OpenUV380SatelliteConfig(QObject *parent = nullptr);\n\n  bool isValid() const;\n  void initialize();\n  bool encode(SatelliteDatabase *db, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Some limits for the satellite config. */\n  struct Limit {\n    /** The maximum number of satellites. */\n    static constexpr unsigned int satellites() {\n      return OpenGD77BaseCodeplug::SatelliteBankElement::Limit::satellites();\n    }\n  };\n\nprotected:\n  /** Some internal offsets. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int satellites()        { return 0x020000; }\n    /// @endcond\n  };\n};\n\n#endif // OPENUV380_SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/orbitalelementsdatabase.cc",
    "content": "#include \"orbitalelementsdatabase.hh\"\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QStandardPaths>\n#include <QFileInfo>\n#include <QNetworkReply>\n#include <QDir>\n#include \"logger.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OrbitalElement::Epoch\n * ********************************************************************************************* */\nOrbitalElement::Epoch::Epoch()\n  : year(0), month(0), day(0), hour(0), minute(0), second(0), microsecond(0)\n{\n  // pass...\n}\n\nOrbitalElement::Epoch::Epoch(unsigned int pyear, unsigned int pmonth, unsigned int pday,\n                        unsigned int phour, unsigned int pminute, unsigned int psecond,\n                        unsigned int pmicrosecond)\n  : year(pyear), month(pmonth), day(pday), hour(phour), minute(pminute), second(psecond),\n    microsecond(pmicrosecond)\n{\n  // pass...\n}\n\n\nOrbitalElement::Epoch\nOrbitalElement::Epoch::parse(const QString &datetime) {\n  QRegularExpression pattern(\"([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{6})\");\n  QRegularExpressionMatch match = pattern.match(datetime);\n  if (! match.hasMatch())\n    return Epoch();\n  return Epoch{\n    match.captured(1).toUInt(),\n    match.captured(2).toUInt(),\n    match.captured(3).toUInt(),\n    match.captured(4).toUInt(),\n    match.captured(5).toUInt(),\n    match.captured(6).toUInt(),\n    match.captured(7).toUInt()\n  };\n}\n\ndouble\nOrbitalElement::Epoch::toEpoch() const {\n  QDate date(year, month, day);\n  double res = microsecond;\n  res = (res / 1e6) + second;\n  res = (res / 60)  + minute;\n  res = (res / 60)  + hour;\n  res = (res / 24)  + date.dayOfYear();\n  return res;\n}\n\nQString\nOrbitalElement::Epoch::toString() const {\n  return QString(\"%1-%2-%3T%4:%5:%6.%7\")\n                 .arg(year, 4, 10, QChar('0'))\n                 .arg(month, 2, 10, QChar('0'))\n                 .arg(day, 2, 10, QChar('0'))\n                 .arg(hour, 2, 10, QChar('0'))\n                 .arg(minute, 2, 10, QChar('0'))\n                 .arg(second, 2, 10, QChar('0'))\n                 .arg(microsecond, 6, 10, QChar('0'));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OrbitalElement\n * ********************************************************************************************* */\nOrbitalElement::OrbitalElement()\n  : _id(0), _name(), _epoch(), _meanMotion(0.0), _meanMotionDerivative(0.0),\n    _inclination(0.0), _ascension(0.0), _eccentricity(0.0), _perigee(0.0), _meanAnomaly(0.0),\n    _revolutionNumber(0)\n{\n  // pass...\n}\n\nOrbitalElement::OrbitalElement(unsigned int id)\n  : _id(id), _name(), _epoch(), _meanMotion(0.0), _meanMotionDerivative(0.0),\n    _inclination(0.0), _ascension(0.0), _eccentricity(0.0), _perigee(0.0), _meanAnomaly(0.0),\n    _revolutionNumber(0)\n{\n  // pass...\n}\n\n\nbool\nOrbitalElement::isValid() const {\n  return 0 != _id;\n}\n\nunsigned int\nOrbitalElement::id() const {\n  return _id;\n}\n\nconst QString &\nOrbitalElement::name() const {\n  return _name;\n}\n\nconst OrbitalElement::Epoch &\nOrbitalElement::epoch() const {\n  return _epoch;\n}\n\ndouble\nOrbitalElement::meanMotion() const {\n  return _meanMotion;\n}\n\ndouble\nOrbitalElement::meanMotionDerivative() const {\n  return _meanMotionDerivative;\n}\n\ndouble\nOrbitalElement::inclination() const {\n  return _inclination;\n}\n\ndouble\nOrbitalElement::ascension() const {\n  return _ascension;\n}\n\ndouble\nOrbitalElement::eccentricity() const {\n  return _eccentricity;\n}\n\ndouble\nOrbitalElement::perigee() const {\n  return _perigee;\n}\n\ndouble\nOrbitalElement::meanAnomaly() const {\n  return _meanAnomaly;\n}\n\nunsigned int\nOrbitalElement::revolutionNumber() const {\n  return _revolutionNumber;\n}\n\n\nOrbitalElement\nOrbitalElement::fromCelesTrak(const QJsonObject &obj) {\n  OrbitalElement el;\n\n  el._name = obj.value(\"OBJECT_NAME\").toString();\n  el._epoch = Epoch::parse(obj.value(\"EPOCH\").toString());\n  el._meanMotion = obj.value(\"MEAN_MOTION\").toDouble();\n  el._meanMotionDerivative = obj.value(\"MEAN_MOTION_DOT\").toDouble();\n  el._inclination = obj.value(\"INCLINATION\").toDouble();\n  el._ascension = obj.value(\"RA_OF_ASC_NODE\").toDouble();\n  el._eccentricity = obj.value(\"ECCENTRICITY\").toDouble();\n  el._perigee = obj.value(\"ARG_OF_PERICENTER\").toDouble();\n  el._meanAnomaly = obj.value(\"MEAN_ANOMALY\").toDouble();\n  el._revolutionNumber = obj.value(\"REV_AT_EPOCH\").toInt();\n\n  el._id = obj.value(\"NORAD_CAT_ID\").toInt();\n\n  return el;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of OrbitalElementsDatabase\n * ********************************************************************************************* */\nOrbitalElementsDatabase::OrbitalElementsDatabase(bool autoLoad, unsigned int updatePeriod, QObject *parent)\n  : QAbstractTableModel{parent}, _updatePeriod(updatePeriod), _elements(), _network()\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(downloadFinished(QNetworkReply*)));\n\n  if (autoLoad) load();\n}\n\n\nbool\nOrbitalElementsDatabase::contains(unsigned int id) const {\n  return _idIndexMap.contains(id);\n}\n\nOrbitalElement\nOrbitalElementsDatabase::getById(unsigned int id) const {\n  return _elements.at(_idIndexMap.value(id));\n}\n\nconst OrbitalElement &\nOrbitalElementsDatabase::getAt(unsigned int idx) const {\n  return _elements[idx];\n}\n\nOrbitalElement &\nOrbitalElementsDatabase::getAt(unsigned int idx) {\n  return _elements[idx];\n}\n\n\nint\nOrbitalElementsDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return _elements.size();\n}\n\nint\nOrbitalElementsDatabase::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return 3;\n}\n\nQVariant\nOrbitalElementsDatabase::data(const QModelIndex &index, int role) const {\n  if (index.row() >= _elements.size())\n    return QVariant();\n\n  if (Qt::DisplayRole == role) {\n    if (0 == index.column())\n      return _elements.at(index.row()).id();\n    if (1 == index.column())\n      return _elements.at(index.row()).name();\n    if (2 == index.column())\n      return _elements.at(index.row()).epoch().toString();\n  }\n\n  return QVariant();\n}\n\nQVariant\nOrbitalElementsDatabase::headerData(int section, Qt::Orientation orientation,\n                                int role) const {\n  if (role != Qt::DisplayRole || orientation != Qt::Horizontal)\n    return QVariant();\n\n  switch (section) {\n      case 0: return QStringLiteral(\"NORAD\");\n      case 1: return QStringLiteral(\"Name\");\n      case 2: return QStringLiteral(\"Epoch\");\n  }\n\n  return QVariant();\n}\n\nunsigned\nOrbitalElementsDatabase::dbAge() const {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/elements.json\";\n  QFileInfo info(path);\n  if (! info.exists())\n    return -1;\n  return info.lastModified().daysTo(QDateTime::currentDateTime());\n}\n\n\nvoid\nOrbitalElementsDatabase::load() {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/elements.json\";\n  if ((! load(path)) || (_updatePeriod < dbAge()))\n    download();\n}\n\n\nbool\nOrbitalElementsDatabase::load(const QString &filename) {\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    QString msg = QString(\"Cannot open orbital elements '%1': %2\").arg(filename).arg(file.errorString());\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  QByteArray data = file.readAll();\n  file.close();\n\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(data, &err);\n  if (doc.isEmpty()) {\n    QString msg = \"Failed to load orbital elements: \" + err.errorString();\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  if (! doc.isArray()) {\n    QString msg = \"Failed to load orbital elements: JSON document is not an array!\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  beginResetModel();\n\n  QJsonArray array = doc.array();\n  _elements.clear();\n  _elements.reserve(array.size());\n  for (int i=0; i<array.size(); i++) {\n    OrbitalElement element = OrbitalElement::fromCelesTrak(array.at(i).toObject());\n    if (element.isValid()) {\n      _elements.append(element);\n    }\n  }\n  // Sort repeater w.r.t. their IDs\n  std::stable_sort(_elements.begin(), _elements.end(),\n                   [](const OrbitalElement &a, const OrbitalElement &b){ return a.id() < b.id(); });\n\n  _idIndexMap.clear();\n  for (unsigned int i=0; i<(unsigned int)_elements.size(); i++)\n    _idIndexMap.insert(_elements.at(i).id(), i);\n\n  // Done.\n  endResetModel();\n\n  logDebug() << \"Loaded orbital elements with \" << _elements.size() << \" entries from \" << filename << \".\";\n\n  emit loaded();\n\n  return true;\n}\n\n\nvoid\nOrbitalElementsDatabase::download() {\n  QUrl url(\"https://celestrak.org/NORAD/elements/gp.php?GROUP=amateur&FORMAT=json\");\n  QNetworkRequest request(url);\n  _network.get(request);\n}\n\nvoid\nOrbitalElementsDatabase::downloadFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    QString msg = QString(\"Cannot download orbital elements: %1\").arg(reply->errorString());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFile file(path+\"/elements.json\");\n\n  QDir directory;\n  if ((! directory.exists(path)) && (!directory.mkpath(path))) {\n    QString msg = QString(\"Cannot create path '%1'.\").arg(path);\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  if (! file.open(QIODevice::WriteOnly)) {\n    QString msg = QString(\"Cannot save orbital elements at '%1'.\").arg(file.fileName());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  file.write(reply->readAll());\n  file.flush();\n  file.close();\n\n  load();\n  reply->deleteLater();\n}\n\n"
  },
  {
    "path": "lib/orbitalelementsdatabase.hh",
    "content": "/** @defgroup sat Satellite tracking settings.\n * @ingroup conf */\n\n#ifndef ORBITALELEMENTSDATABASE_HH\n#define ORBITALELEMENTSDATABASE_HH\n\n#include <QAbstractTableModel>\n#include <QNetworkAccessManager>\n\n\n/** Defines a single orbital element, enabling the tracking of a single satellite. This dataset\n * does not contain any transponder information.\n * @ingroup sat */\nclass OrbitalElement\n{\npublic:\n  /** Represents a Julien day epoch since a specified year. */\n  struct Epoch {\n    /** The year of the epoch. */\n    unsigned int year;\n    /** The month. */\n    unsigned int month;\n    /** The day. */\n    unsigned int day;\n    /** The hour. */\n    unsigned int hour;\n    /** The minute. */\n    unsigned int minute;\n    /** The second. */\n    unsigned int second;\n    /** The microsecond. */\n    unsigned int microsecond;\n\n    /** Default constructor. */\n    Epoch();\n    /** Constructor */\n    Epoch(unsigned int year, unsigned int month, unsigned int day,\n          unsigned int hour, unsigned int minute, unsigned int second, unsigned int microsecond);\n    /** Copy constructor. */\n    Epoch(const Epoch &other) = default;\n    /** Copy assignment. */\n    Epoch &operator =(const Epoch &other) = default;\n\n    /** Parses a date-time string into the epoch. */\n    static Epoch parse(const QString &datetime);\n    /** Computes the decimal epoch as the day of year a  */\n    double toEpoch() const;\n    /** Encodes the Epoch as YYYY-MM-DDThh:mm:ss.uuuuuu. */\n    QString toString() const;\n  };\n\npublic:\n  /** Default constructor. */\n  OrbitalElement();\n  /** Constructor from ID. */\n  OrbitalElement(unsigned int id);\n  /** Copy constructor. */\n  OrbitalElement(const OrbitalElement &other) = default;\n  /** Copy assignment. */\n  OrbitalElement &operator=(const OrbitalElement &other) = default;\n\n  /** Returns @c true, if this represents a valid satellite information. */\n  bool isValid() const;\n  /** Returns the NORAD catalog id. */\n  unsigned int id() const;\n  /** Returns the name of the satellite. */\n  const QString &name() const;\n\n  /** Epoch of the orbital elements. */\n  const Epoch &epoch() const;\n  /** Returns the mean motion. */\n  double meanMotion() const;\n  /** Returns the first derivative of the mean motion. */\n  double meanMotionDerivative() const;\n  /** Returns the inclination. */\n  double inclination() const;\n  /** Returns the right ascension of the ascending node. */\n  double ascension() const;\n  /** Returns the eccentricity. */\n  double eccentricity() const;\n  /** Returns the argument of perigee. */\n  double perigee() const;\n  /** Returns the mean anomaly. */\n  double meanAnomaly() const;\n  /** Returns the revolution number. */\n  unsigned int revolutionNumber() const;\n\npublic:\n  /** Constructs a orbital element from a CelesTrak JSON object. */\n  static OrbitalElement fromCelesTrak(const QJsonObject &obj);\n\nprotected:\n  /** NORAD id of the satellite. */\n  unsigned int _id;\n  /** Descriptive name of the satellite. */\n  QString _name;\n  /** The epoch. */\n  Epoch _epoch;\n  /** Mean motion. */\n  double _meanMotion;\n  /** First derivative of the mean motion. */\n  double _meanMotionDerivative;\n  /** Inclination. */\n  double _inclination;\n  /** Right ascension of the ascending node. */\n  double _ascension;\n  /** Eccentricity. */\n  double _eccentricity;\n  /** Argument of perigee. */\n  double _perigee;\n  /** Mean anomaly. */\n  double _meanAnomaly;\n  /** The revolution number. */\n  unsigned int _revolutionNumber;\n};\n\n\n\n/** Downloads and updates a database of orbital elements from CelesTrak.\n * @ingroup sat */\nclass OrbitalElementsDatabase: public QAbstractTableModel\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a orbital element database.\n   * @param autoLoad [in] If @c true, the database gets downloaded and loaded automatically.\n   * @param updatePeriodDays [in] Specifies the max age of the local database cache in days.\n   * @param parent [in] Specifies the QObject parent. */\n  explicit OrbitalElementsDatabase(bool autoLoad, unsigned int updatePeriodDays=7, QObject *parent=nullptr);\n\n  /** @c returns @c true if the database contains a satellite with the given NORAD id. */\n  bool contains(unsigned int id) const;\n  /** Returns the orbital elements for the satellite with the given NORAD id. */\n  OrbitalElement getById(unsigned int id) const;\n  /** Returns the i-th orbital element. */\n  const OrbitalElement &getAt(unsigned int idx) const;\n  /** Returns the i-th orbital element. */\n  OrbitalElement &getAt(unsigned int idx);\n\n  /** Returns the current age of the cache. */\n  unsigned int dbAge() const;\n  /** If needed, downloads the database and loads all received orbital elements. */\n  void load();\n\n  /** Returns the number of elements in the database. */\n  int rowCount(const QModelIndex &parent = QModelIndex()) const;\n  /** Returns the number of columns of the database table. */\n  int columnCount(const QModelIndex &parent = QModelIndex()) const;\n  /** Returns a single cell of the database table. */\n  QVariant data(const QModelIndex &index, int role) const;\n  /** Returns a single header of the database table. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role) const;\n\nsignals:\n  /** Gets emitted once the satellite orbitals has been loaded. */\n  void loaded();\n  /** Gets emitted if the loading one of the sources fails. */\n  void error(const QString &msg);\n\npublic slots:\n  /** Starts the download of the orbital elements. */\n  void download();\n\nprivate slots:\n  /** Gets called whenever the orbital elements download is complete. */\n  void downloadFinished(QNetworkReply *reply);\n\nprotected:\n  /** Loads a database from the given filename. */\n  bool load(const QString &filename);\n\nprivate:\n  /** Update period in days. */\n  unsigned int _updatePeriod;\n  /** Holds all satellites sorted by their catalog number. */\n  QVector<OrbitalElement>  _elements;\n  /** Maps NORAD id to element. */\n  QHash<unsigned int, unsigned int> _idIndexMap;\n  /** The network access used for downloading. */\n  QNetworkAccessManager _network;\n};\n\n#endif // ORBITALELEMENTSDATABASE_HH\n"
  },
  {
    "path": "lib/packetstream.cc",
    "content": "#include \"packetstream.hh\"\n#include <QIODevice>\n\n\n/* ******************************************************************************************** *\n * Implementation of PacketStream interface\n * ******************************************************************************************** */\nPacketStream::PacketStream(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\nPacketStream::~PacketStream() {\n  // pass...\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of SlipStream\n * ******************************************************************************************** */\nSlipStream::SlipStream(QIODevice *device, QObject *parent)\n  : PacketStream(parent), _device(device), _buffer()\n{\n  if (_device)\n    _device->setParent(this);\n}\n\n\nbool\nSlipStream::isOpen() const {\n  return _device && _device->isOpen();\n}\n\n\nvoid\nSlipStream::close() {\n  if (_device)\n    _device->close();\n}\n\n\nbool\nSlipStream::receive(QByteArray &buffer, int timeout, const ErrorStack &err) {\n  while (! _buffer.contains(END_OF_PACKET)) {\n    // If there is some stuff to read -> append to buffer and continue\n    if (_device->bytesAvailable()) {\n      _buffer.append(_device->readAll());\n      continue;\n    }\n    if (! _device->waitForReadyRead(timeout)) {\n      errMsg(err) << _device->errorString();\n      errMsg(err) << \"Cannot read from device.\";\n      return false;\n    }\n    _buffer.append(_device->readAll());\n  }\n\n  // Unpack packet\n  int endIndex = _buffer.indexOf(END_OF_PACKET);\n\n  // Clear output buffer\n  buffer.clear();\n  buffer.reserve(endIndex);\n\n  for (int i=0; i<endIndex; i++) {\n    if (ESCAPE == _buffer[i]) {\n      if (((i+1) < endIndex) && (ESCAPED_C0 == _buffer[i+1])) {\n        buffer.append('\\xC0'); i+= 1;\n      } else if (((i+1) < endIndex) && (ESCAPED_DB == _buffer[i+1])) {\n        buffer.append('\\xDB'); i+= 1;\n      } else {\n        buffer.append(_buffer[i]);\n      }\n    }\n  }\n  _buffer.remove(0, endIndex+1);\n\n  return true;\n}\n\n\nbool\nSlipStream::send(const QByteArray &buffer, int timeout, const ErrorStack &err) {\n  QByteArray outBuffer;\n  outBuffer.reserve(buffer.size() + 2\n                    + buffer.count(END_OF_PACKET)\n                    + buffer.count(ESCAPE));\n\n  outBuffer.append(END_OF_PACKET);\n  for (int i=0; i<buffer.size(); i++) {\n    if (END_OF_PACKET == buffer[i]) {\n      outBuffer.append(ESCAPE);\n      outBuffer.append(ESCAPED_C0);\n    } else if (ESCAPE == buffer[i]) {\n      outBuffer.append(ESCAPE);\n      outBuffer.append(ESCAPED_DB);\n    } else {\n      outBuffer.append(buffer[i]);\n    }\n  }\n  outBuffer.append(END_OF_PACKET);\n\n  _device->write(outBuffer);\n  if (! _device->waitForBytesWritten(timeout)) {\n    errMsg(err) << \"Cannot write to the device.\";\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "lib/packetstream.hh",
    "content": "#ifndef PACKETSTREAM_HH\n#define PACKETSTREAM_HH\n\n#include <QObject>\n#include \"errorstack.hh\"\n\nclass QIODevice;\n\n/** Defines an interface for a datagram socket.\n * That is, just some methods to send and receive packets.\n * @ingroup rif */\nclass PacketStream : public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit PacketStream(QObject *parent = nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~PacketStream();\n\n  /** Returns @c true if the stream is open. */\n  virtual bool isOpen() const = 0;\n  /** Closes the stream. */\n  virtual void close() = 0;\n\n  /** Receives a datagram. Blocks for up to @c timeout milliseconds. */\n  virtual bool receive(QByteArray &buffer, int timeout=-1, const ErrorStack &err=ErrorStack()) = 0;\n  /** Receives a datagram. Blocks for up to @c timeout milliseconds. */\n  virtual bool send(const QByteArray& buffer, int timeout=-1, const ErrorStack &err=ErrorStack()) = 0;\n};\n\n\n/** Implement SLIP (serial line internet protocol).\n * @ingroup rif */\nclass SlipStream: public PacketStream\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor, takes ownership of the device. */\n  SlipStream(QIODevice *device, QObject *parent=nullptr);\n\npublic:\n  bool isOpen() const override;\n  void close() override;\n  bool receive(QByteArray &buffer, int timeout=-1, const ErrorStack &err=ErrorStack()) override;\n  bool send(const QByteArray& buffer, int timeout=-1, const ErrorStack &err=ErrorStack()) override;\n\nprotected:\n  QIODevice *_device;\n  QByteArray _buffer;\n\nprivate:\n  static constexpr char END_OF_PACKET = '\\xC0';\n  static constexpr char ESCAPE = '\\xDB';\n  static constexpr char ESCAPED_C0 = '\\xDC';\n  static constexpr char ESCAPED_DB = '\\xDD';\n};\n\n\n\n#endif // PACKETSTREAM_HH\n"
  },
  {
    "path": "lib/radio.cc",
    "content": "#include \"radio.hh\"\n\n#include \"anytone_interface.hh\"\n#include \"radioddity_interface.hh\"\n#include \"tyt_interface.hh\"\n#include \"dr1801uv_interface.hh\"\n#include \"gd73_interface.hh\"\n#include \"dm32uv_interface.hh\"\n#include \"openrtx_interface.hh\"\n\n#include \"rd5r.hh\"\n#include \"gd73.hh\"\n#include \"gd77.hh\"\n#include \"md390.hh\"\n#include \"uv390.hh\"\n#include \"md2017.hh\"\n#include \"dm1701.hh\"\n#include \"dm32uv.hh\"\n#include \"dr1801uv.hh\"\n#include \"opengd77.hh\"\n#include \"openuv380.hh\"\n#include \"openrtx.hh\"\n#include \"d868uv.hh\"\n#include \"d878uv.hh\"\n#include \"d878uv2.hh\"\n#include \"d578uv.hh\"\n#include \"d168uv.hh\"\n#include \"dmr6x2uv.hh\"\n\n#include \"logger.hh\"\n\n#include <QSet>\n\n\n/* ******************************************************************************************** *\n * Implementation of Radio\n * ******************************************************************************************** */\nRadio::Radio(QObject *parent)\n  : QThread(parent), _task(StatusIdle)\n{\n  // pass...\n}\n\nRadio::~Radio() {\n  // pass...\n}\n\nconst CallsignDB *\nRadio::callsignDB() const {\n  return nullptr;\n}\n\nCallsignDB *\nRadio::callsignDB() {\n  return nullptr;\n}\n\n\nRadio *\nRadio::detect(const USBDeviceDescriptor &descr, const RadioInfo &force, const ErrorStack &err) {\n  if (! descr.isValid()) {\n    errMsg(err) << \"Cannot detect radio: Invalid interface descriptor.\";\n    return nullptr;\n  }\n  logDebug() << \"Try to detect radio at \" << descr.description() << \".\";\n\n  if (AnytoneGD32Interface::interfaceInfo() == descr) {\n    auto anytone = new AnytoneGD32Interface(descr, err);\n    if (anytone->isOpen()) {\n      RadioInfo id = anytone->identifier(err);\n      if ((id.isValid() && (RadioInfo::D868UVE == id.id())) || (force.isValid() && (RadioInfo::D868UVE == force.id()))) {\n        return new D868UV(anytone);\n      } else if ((id.isValid() && (RadioInfo::D878UV == id.id())) || (force.isValid() && (RadioInfo::D878UV == force.id()))) {\n        return new D878UV(anytone);\n      } else if ((id.isValid() && (RadioInfo::D878UVII == id.id())) || (force.isValid() && (RadioInfo::D878UVII == force.id()))) {\n        return new D878UV2(anytone);\n      } else if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {\n        return new D578UV(anytone);\n      } else if ((id.isValid() && (RadioInfo::DMR6X2UV == id.id())) || (force.isValid() && (RadioInfo::DMR6X2UV == force.id()))) {\n        return new DMR6X2UV(anytone);\n      } else if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {\n        return new D578UV(anytone);\n      } else if (id.isValid()) {\n        errMsg(err) << tr(\"Unhandled device %1 '%2'. Device known but not implemented yet.\")\n                       .arg(id.manufacturer())\n                       .arg(id.name());\n      } else {\n        errMsg(err) << tr(\"Unknown AnyTone (or similar) device.\");\n      }\n      anytone->close();\n      anytone->deleteLater();\n      return nullptr;\n    }\n    anytone->deleteLater();\n  } else if (AnytoneSTM32Interface::interfaceInfo() == descr) {\n    auto anytone = new AnytoneSTM32Interface(descr, err);\n    if (anytone->isOpen()) {\n      RadioInfo id = anytone->identifier(err);\n      if ((id.isValid() && (RadioInfo::D578UV == id.id())) || (force.isValid() && (RadioInfo::D578UV == force.id()))) {\n        return new D578UV(anytone);\n      } else if ((id.isValid() && (RadioInfo::D168UV == id.id())) || (force.isValid() && (RadioInfo::D168UV == force.id()))) {\n        return new D168UV(anytone);\n      } else if (id.isValid()) {\n        errMsg(err) << tr(\"Unhandled device %1 '%2'. Device known but not implemented yet.\")\n                       .arg(id.manufacturer())\n                       .arg(id.name());\n      } else {\n        errMsg(err) << tr(\"Unknown AnyTone (or similar) device.\");\n      }\n      anytone->close();\n      anytone->deleteLater();\n      return nullptr;\n    }\n    anytone->deleteLater();\n  } else if (OpenGD77Interface::interfaceInfo() == descr) {\n    OpenGD77Interface *ogd77 = new OpenGD77Interface(descr, err);\n    if (ogd77->isOpen()) {\n      RadioInfo id = ogd77->identifier();\n      if ((id.isValid() && (RadioInfo::OpenGD77 == id.id())) || (force.isValid() && (RadioInfo::OpenGD77 == force.id()))) {\n        return new OpenGD77(ogd77);\n      } else if ((id.isValid() && (RadioInfo::OpenUV380 == id.id())) || (force.isValid() && (RadioInfo::OpenUV380 == force.id()))) {\n        return new OpenUV380(ogd77);\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device known but not implemented yet.\";\n      }\n      ogd77->close();\n      ogd77->deleteLater();\n      return nullptr;\n    }\n    ogd77->deleteLater();\n  } else if (OpenRTXInterface::interfaceInfo() == descr) {\n    auto rtxIf = new OpenRTXInterface(descr, err);\n    if (rtxIf->isOpen()) {\n      RadioInfo id = rtxIf->identifier();\n      if ((id.isValid() && (RadioInfo::OpenRTX == id.id())) || (force.isValid() && (RadioInfo::OpenRTX == force.id()))) {\n        logInfo() << \"Yah!\";\n        return nullptr; //new OpenRTX(rtxIf);\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n        << \". Device known but not implemented yet.\";\n      }\n      rtxIf->close();\n      rtxIf->deleteLater();\n      return nullptr;\n    }\n    rtxIf->deleteLater();\n  } else if (TyTInterface::interfaceInfo() == descr) {\n    TyTInterface *dfu = new TyTInterface(descr, err);\n    if (dfu->isOpen()) {\n      RadioInfo id = dfu->identifier();\n      if ((id.isValid() && (RadioInfo::MD390 == id.id())) || (force.isValid() && (RadioInfo::MD390 == force.id()))) {\n        return new MD390(dfu);\n      } else if ((id.isValid() && (RadioInfo::UV390 == id.id())) || (force.isValid() && (RadioInfo::UV390 == force.id()))) {\n        return new UV390(dfu);\n      } else if ((id.isValid() && (RadioInfo::MD2017 == id.id())) || (force.isValid() && (RadioInfo::MD2017 == force.id()))) {\n        return new MD2017(dfu);\n      } else if ((id.isValid() && (RadioInfo::DM1701 == id.id())) || (force.isValid() && (RadioInfo::DM1701 == force.id()))) {\n        logDebug() << \"Create DM-1701 radio object.\";\n        return new DM1701(dfu);\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device known but not implemented yet.\";\n      }\n      dfu->close();\n      dfu->deleteLater();\n      return nullptr;\n    }\n    dfu->deleteLater();\n  } else if (RadioddityInterface::interfaceInfo() == descr) {\n    RadioddityInterface *hid = new RadioddityInterface(descr, err);\n    if (hid->isOpen()) {\n      RadioInfo id = hid->identifier();\n      if ((id.isValid() && (RadioInfo::RD5R == id.id())) || (force.isValid() && (RadioInfo::RD5R == force.id()))) {\n        return new RD5R(hid);\n      } else if ((id.isValid() && (RadioInfo::GD77 == id.id())) || (force.isValid() && (RadioInfo::GD77 == force.id()))) {\n        return new GD77(hid);\n      } else if (id.isValid()) {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device known but not implemented yet.\";\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device not known.\";\n      }\n      hid->close();\n      hid->deleteLater();\n      return nullptr;\n    }\n    hid->deleteLater();\n  } else if ((DR1801UVInterface::interfaceInfo() == descr) && force.isValid() && (RadioInfo::DR1801UV==force.id())){\n    DR1801UVInterface *dif = new DR1801UVInterface(descr, err);\n    if (dif->isOpen()) {\n      RadioInfo id = dif->identifier(err);\n      if (id.isValid() && (RadioInfo::DR1801UV == id.id())) {\n        return new DR1801UV(dif);\n      } else if (id.isValid()) {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device known but not implemented yet.\";\n      } else {\n        errMsg(err) << \"Unknown device or failed connection to the device.\";\n      }\n      dif->close();\n      dif->deleteLater();\n      return nullptr;\n    }\n    dif->deleteLater();\n  } else if (C7000Device::interfaceInfo() == descr) {\n    GD73Interface *gdif = new GD73Interface(descr, err);\n    if (gdif->isOpen()) {\n      RadioInfo id = gdif->identifier();\n      if ((id.isValid() && (RadioInfo::GD73 == id.id())) ||\n          (force.isValid() && (RadioInfo::GD73 == force.id()))) {\n        return new GD73(gdif);\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n                    << \". Device known but not implemented yet.\";\n      }\n      gdif->close();\n      gdif->deleteLater();\n      return nullptr;\n    }\n    gdif->deleteLater();\n  } else if ((DM32UVInterface::interfaceInfo() == descr) && force.isValid() && (RadioInfo::DM32UV==force.id())) {\n    DM32UVInterface *dm32if = new DM32UVInterface(descr, err);\n    if (dm32if->isOpen()) {\n      RadioInfo id = dm32if->identifier(err);\n      if (! id.isValid()) {\n        errMsg(err) << \"Cannot identify device.\";\n      } else if (RadioInfo::DM32UV == id.id()) {\n        return new DM32UV(dm32if);\n      } else {\n        errMsg(err) << \"Unhandled device \" << id.manufacturer() << \" \" << id.name()\n        << \". Device known but not implemented yet.\";\n      }\n      dm32if->close();\n      dm32if->deleteLater();\n      return nullptr;\n    }\n    dm32if->deleteLater();\n    return nullptr;\n  }\n  return nullptr;\n}\n\nRadio::Status\nRadio::status() const {\n  return _task;\n}\n\nconst ErrorStack &\nRadio::errorStack() const {\n  return _errorStack;\n}\n"
  },
  {
    "path": "lib/radio.hh",
    "content": "/** @defgroup dsc Supported devices\n * This module collects all classes are device specific.\n *\n * That is, implementing device specific configurations, aka codeplugs as well as the specific\n * communication with these radios.\n */\n\n#ifndef RADIO_HH\n#define RADIO_HH\n\n#include <QThread>\n#include \"radioinfo.hh\"\n#include \"radiointerface.hh\"\n#include \"codeplug.hh\"\n#include \"userdatabase.hh\"\n#include \"callsigndb.hh\"\n#include \"errorstack.hh\"\n#include \"config.hh\"\n\nclass RadioLimits;\n\n\n/** Base class for all Radio objects.\n *\n * The radio objects represents a connected radio. This class controlles the communication\n * with the device as well as the conversion between device specific code-plugs and generic\n * configurations.\n *\n * @ingroup rif\n */\nclass Radio : public QThread\n{\n\tQ_OBJECT\n\npublic:\n  /** Possible states of the radio object. */\n\ttypedef enum {\n    StatusIdle,             ///< Idle, nothing to do.\n    StatusDownload,         ///< Downloading codeplug.\n    StatusUpload,           ///< Uploading codeplug.\n    StatusUploadCallsigns,  ///< Uploading codeplug.\n    StatusUploadSatellites, ///< Uploading satellite config.\n    StatusError             ///< An error occurred.\n  } Status;\n\npublic:\n  /** Default constructor. */\n\texplicit Radio(QObject *parent = nullptr);\n\n  virtual ~Radio();\n\n  /** Returns the name of the radio (e.g., device identifier). */\n\tvirtual const QString &name() const = 0;\n\n  /** Returns the limits for this radio.\n   *\n   * Call @c RadioLimits::verifyConfig to verify a codeplug with respect to a radio.\n   *\n   * @since Version 0.10.2 */\n  virtual const RadioLimits &limits() const = 0;\n\n  /** Returns the codeplug instance. */\n  virtual const Codeplug &codeplug() const = 0;\n  /** Returns the codeplug instance. */\n  virtual Codeplug &codeplug() = 0;\n\n  /** Returns the call-sign DB instance. */\n  virtual const CallsignDB *callsignDB() const;\n  /** Returns the call-sign DB instance. */\n  virtual CallsignDB *callsignDB();\n\n  /** Returns the current status. */\n  Status status() const;\n\n  /** Returns the error stack, passed to @c startDownload, @c startUpload or\n   * @c startUploadCallsignDB. It contains the error messages from the upload/download process. */\n  const ErrorStack &errorStack() const;\n\npublic:\n  /** Tries to detect the radio connected to the specified interface or constructs the specified\n   * radio using the @c RadioInfo passed by @c force. */\n  static Radio *detect(const USBDeviceDescriptor &descr, const RadioInfo &force=RadioInfo(),\n                       const ErrorStack &err=ErrorStack());\n\npublic slots:\n  /** Starts the download of the codeplug.\n   * Once the download finished, the codeplug can be accessed and decoded using\n   * the @c codeplug() method. */\n  virtual bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  virtual bool startUpload(\n      Config *config, const Codeplug::Flags &flags = Codeplug::Flags(),\n      const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Assembles the callsign DB from the given one and uploads it to the device. */\n  virtual bool startUploadCallsignDB(\n      UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(),\n      const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Assembles the satellite config and writes it to the device. */\n  virtual bool startUploadSatelliteConfig(\n      SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err=ErrorStack()) = 0;\n\nsignals:\n  /** Gets emitted once the codeplug download has been started. */\n\tvoid downloadStarted();\n  /** Gets emitted on download progress (e.g., for progress bars). */\n\tvoid downloadProgress(int percent);\n  /** Gets emitted once the codeplug download has been finished. */\n  void downloadFinished(Radio *radio, Codeplug *codeplug);\n  /** Gets emitted if there was an error during the codeplug download. */\n\tvoid downloadError(Radio *radio);\n\n  /** Gets emitted once the codeplug upload has been started. */\n\tvoid uploadStarted();\n  /** Gets emitted on upload progress (e.g., for progress bars). */\n\tvoid uploadProgress(int percent);\n  /** Gets emitted if there was an error during the upload. */\n\tvoid uploadError(Radio *radio);\n  /** Gets emitted once the codeplug upload has been completed successfully. */\n\tvoid uploadComplete(Radio *radio);\n\nprotected:\n  /** The current state/task. */\n  Status _task;\n  /** The error stack. */\n  ErrorStack _errorStack;\n};\n\n#endif // RADIO_HH\n"
  },
  {
    "path": "lib/radioddity_codeplug.cc",
    "content": "#include \"radioddity_codeplug.hh\"\n\n#include <QRegularExpression>\n\n#include \"utils.hh\"\n#include \"logger.hh\"\n#include \"scanlist.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"zone.hh\"\n#include \"config.hh\"\n#include \"commercial_extension.hh\"\n#include \"intermediaterepresentation.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ChannelElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ChannelElement::~ChannelElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ChannelElement::clear() {\n  setName(\"\");\n  setRXFrequency(0);\n  setTXFrequency(0);\n  setMode(MODE_ANALOG);\n  setUInt8(0x0019, 0x00); setUInt8(0x001a, 0x00);\n  setTXTimeOut(Interval::infinity());\n  setTXTimeOutRekeyDelay(0);\n  setAdmitCriterion(ADMIT_ALWAYS);\n  setUInt8(0x001e, 0x50);\n  setScanListIndex(0x00);\n  setRXTone(SelectiveCall());\n  setTXTone(SelectiveCall());\n  setUInt8(0x0024, 0x00);\n  setTXSignalingIndex(0);\n  setUInt8(0x0026, 0x00);\n  setRXSignalingIndex(0);\n  setUInt8(0x0028, 0x16);\n  setPrivacyGroup(PRIVGR_NONE);\n  setTXColorCode(0);\n  setGroupListIndex(0);\n  setRXColorCode(0);\n  setEmergencySystemIndex(0);\n  setContactIndex(0);\n  setUInt32_be(0x0030, 0); // clear all bitfields at once.\n  setUInt8(0x0034, 0); setUInt8(0x0035, 0); setUInt8(0x0036, 0);\n}\n\nQString\nRadioddityCodeplug::ChannelElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::ChannelElement::setName(const QString &n) {\n  writeASCII(Offset::name(), n, Limit::nameLength(), 0xff);\n}\n\nuint32_t\nRadioddityCodeplug::ChannelElement::rxFrequency() const {\n  return getBCD8_le(Offset::rxFrequency())*10;\n}\nvoid\nRadioddityCodeplug::ChannelElement::setRXFrequency(uint32_t freq) {\n  setBCD8_le(Offset::rxFrequency(), freq/10);\n}\nuint32_t\nRadioddityCodeplug::ChannelElement::txFrequency() const {\n  return getBCD8_le(Offset::txFrequency())*10;\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXFrequency(uint32_t freq) {\n  setBCD8_le(Offset::txFrequency(), freq/10);\n}\n\nRadioddityCodeplug::ChannelElement::Mode\nRadioddityCodeplug::ChannelElement::mode() const {\n  return (Mode)getUInt8(Offset::mode());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setMode(Mode mode) {\n  setUInt8(Offset::mode(), (unsigned)mode);\n}\n\nInterval RadioddityCodeplug::ChannelElement::txTimeOut() const {\n  if (0 == getUInt8(Offset::txTimeout()))\n    return Interval::infinity();\n  return Interval::fromSeconds(getUInt8(Offset::txTimeout())*15);\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXTimeOut(const Interval& tot) {\n  if (tot.isInfinite())\n    setUInt8(Offset::txTimeout(), 0);\n  else\n    setUInt8(Offset::txTimeout(), tot.seconds()/15);\n}\nunsigned\nRadioddityCodeplug::ChannelElement::txTimeOutRekeyDelay() const {\n  return getUInt8(Offset::txTimeoutRekeyDelay());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXTimeOutRekeyDelay(unsigned delay) {\n  setUInt8(Offset::txTimeoutRekeyDelay(), delay);\n}\n\nRadioddityCodeplug::ChannelElement::Admit\nRadioddityCodeplug::ChannelElement::admitCriterion() const {\n  return (Admit) getUInt8(Offset::admitCriterion());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setAdmitCriterion(Admit admit) {\n  setUInt8(Offset::admitCriterion(), (unsigned)admit);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::hasScanList() const {\n  return 0 != scanListIndex();\n}\nunsigned\nRadioddityCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(Offset::scanList());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setScanListIndex(unsigned index) {\n  setUInt8(Offset::scanList(), index);\n}\n\nSelectiveCall\nRadioddityCodeplug::ChannelElement::rxTone() const {\n  return decode_ctcss_tone_table(getUInt16_le(Offset::rxTone()));\n}\nvoid\nRadioddityCodeplug::ChannelElement::setRXTone(const SelectiveCall &code) {\n  setUInt16_le(Offset::rxTone(), encode_ctcss_tone_table(code));\n}\nSelectiveCall\nRadioddityCodeplug::ChannelElement::txTone() const {\n  return decode_ctcss_tone_table(getUInt16_le(Offset::txTone()));\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXTone(const SelectiveCall &code) {\n  setUInt16_le(Offset::txTone(), encode_ctcss_tone_table(code));\n}\n\nunsigned\nRadioddityCodeplug::ChannelElement::txSignalingIndex() const {\n  return getUInt8(Offset::txSignaling());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXSignalingIndex(unsigned index) {\n  setUInt8(Offset::txSignaling(), index);\n}\nunsigned\nRadioddityCodeplug::ChannelElement::rxSignalingIndex() const {\n  return getUInt8(Offset::rxSignaling());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setRXSignalingIndex(unsigned index) {\n  setUInt8(Offset::rxSignaling(), index);\n}\n\nRadioddityCodeplug::ChannelElement::PrivacyGroup\nRadioddityCodeplug::ChannelElement::privacyGroup() const {\n  return (PrivacyGroup) getUInt8(Offset::privacyGroup());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setPrivacyGroup(PrivacyGroup grp) {\n  setUInt8(Offset::privacyGroup(), (unsigned)grp);\n}\n\nunsigned\nRadioddityCodeplug::ChannelElement::txColorCode() const {\n  return getUInt8(Offset::txColorCode());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTXColorCode(unsigned cc) {\n  setUInt8(Offset::txColorCode(), cc);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::hasGroupList() const {\n  return 0 != groupListIndex();\n}\nunsigned\nRadioddityCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupList());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setGroupListIndex(unsigned index) {\n  setUInt8(Offset::groupList(), index);\n}\n\nunsigned\nRadioddityCodeplug::ChannelElement::rxColorCode() const {\n  return getUInt8(Offset::rxColorCode());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setRXColorCode(unsigned cc) {\n  setUInt8(Offset::rxColorCode(), cc);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::hasEmergencySystem() const {\n  return 0 != emergencySystemIndex();\n}\nunsigned\nRadioddityCodeplug::ChannelElement::emergencySystemIndex() const {\n  return getUInt8(Offset::emergencySystem());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setEmergencySystemIndex(unsigned index) {\n  setUInt8(Offset::emergencySystem(), index);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::hasContact() const {\n  return 0!=contactIndex();\n}\nunsigned\nRadioddityCodeplug::ChannelElement::contactIndex() const {\n  return getUInt16_le(Offset::transmitContact());\n}\nvoid\nRadioddityCodeplug::ChannelElement::setContactIndex(unsigned index) {\n  setUInt16_le(Offset::transmitContact(), index);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::dataCallConfirm() const {\n  return getBit(Offset::dataCallConfirm());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableDataCallConfirm(bool enable) {\n  setBit(Offset::dataCallConfirm(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::emergencyAlarmACK() const {\n  return getBit(Offset::emergencyAlarmACK());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableEmergencyAlarmACK(bool enable) {\n  setBit(Offset::emergencyAlarmACK(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::privateCallConfirm() const {\n  return getBit(Offset::privateCallConfirm());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enablePrivateCallConfirm(bool enable) {\n  setBit(Offset::privateCallConfirm(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::privacyEnabled() const {\n  return getBit(Offset::privacyEnabled());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enablePrivacy(bool enable) {\n  setBit(Offset::privacyEnabled(), enable);\n}\n\nDMRChannel::TimeSlot\nRadioddityCodeplug::ChannelElement::timeSlot() const {\n  return (getBit(Offset::timeSlot()) ? DMRChannel::TimeSlot::TS2 : DMRChannel::TimeSlot::TS1);\n}\nvoid\nRadioddityCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  setBit(Offset::timeSlot(), DMRChannel::TimeSlot::TS2 == ts);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::dualCapacityDirectMode() const {\n  return getBit(Offset::dualCapacityDirectMode());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableDualCapacityDirectMode(bool enable) {\n  setBit(Offset::dualCapacityDirectMode(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::nonSTEFrequency() const {\n  return getBit(Offset::nonSTEFrequency());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableNonSTEFrequency(bool enable) {\n  setBit(Offset::nonSTEFrequency(), enable);\n}\n\nFMChannel::Bandwidth\nRadioddityCodeplug::ChannelElement::bandwidth() const {\n  return (getBit(Offset::bandwidth()) ? FMChannel::Bandwidth::Wide : FMChannel::Bandwidth::Narrow);\n}\nvoid\nRadioddityCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  setBit(Offset::bandwidth(), FMChannel::Bandwidth::Wide == bw);\n}\n\nbool\nRadioddityCodeplug::ChannelElement::rxOnly() const {\n  return getBit(Offset::rxOnly());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableRXOnly(bool enable) {\n  setBit(Offset::rxOnly(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::talkaround() const {\n  return getBit(Offset::talkaround());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableTalkaround(bool enable) {\n  setBit(Offset::talkaround(), enable);\n}\nbool\nRadioddityCodeplug::ChannelElement::vox() const {\n  return getBit(Offset::vox());\n}\nvoid\nRadioddityCodeplug::ChannelElement::enableVOX(bool enable) {\n  setBit(Offset::vox(), enable);\n}\n\nChannel::Power\nRadioddityCodeplug::ChannelElement::power() const {\n  return (getBit(Offset::power()) ? Channel::Power::High : Channel::Power::Low);\n}\nvoid\nRadioddityCodeplug::ChannelElement::setPower(Channel::Power pwr) {\n  switch (pwr) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    clearBit(Offset::power());\n    break;\n  case Channel::Power::Mid:\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setBit(Offset::power());\n    break;\n  }\n}\n\nChannel *\nRadioddityCodeplug::ChannelElement::toChannelObj(Codeplug::Context &ctx, const ErrorStack& err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  Channel *ch = nullptr;\n  if (MODE_ANALOG == mode()) {\n    FMChannel *ach = new FMChannel(); ch = ach;\n    switch (admitCriterion()) {\n    case ADMIT_ALWAYS: ach->setAdmit(FMChannel::Admit::Always); break;\n    case ADMIT_CH_FREE: ach->setAdmit(FMChannel::Admit::Free); break;\n    default: ach->setAdmit(FMChannel::Admit::Always); break;\n    }\n    ach->setBandwidth(bandwidth());\n    ach->setRXTone(rxTone());\n    ach->setTXTone(txTone());\n    ach->setSquelchDefault(); // There is no per-channel squelch setting\n    ach->extended()->enableTalkaround(talkaround());\n  } else {\n    DMRChannel *dch = new DMRChannel(); ch = dch;\n    switch (admitCriterion()) {\n    case ADMIT_ALWAYS: dch->setAdmit(DMRChannel::Admit::Always); break;\n    case ADMIT_CH_FREE: dch->setAdmit(DMRChannel::Admit::Free); break;\n    case ADMIT_COLOR: dch->setAdmit(DMRChannel::Admit::ColorCode); break;\n    default: dch->setAdmit(DMRChannel::Admit::Always); break;\n    }\n    dch->setTimeSlot(timeSlot());\n    dch->setColorCode(txColorCode());\n    dch->extended()->enableDataConfirm(dataCallConfirm());\n    dch->extended()->enablePrivateCallConfirm(privateCallConfirm());\n    dch->extended()->enableDCDM(dualCapacityDirectMode());\n    dch->extended()->enableTalkaround(talkaround());\n  }\n\n  // Apply common settings\n  ch->setName(name());\n  ch->setRXFrequency(Frequency::fromHz(rxFrequency()));\n  ch->setTXFrequency(Frequency::fromHz(txFrequency()));\n  ch->setPower(power());\n  ch->setTimeout(txTimeOut());\n  ch->setRXOnly(rxOnly());\n  if (vox())\n    ch->setVOXDefault();\n  else\n    ch->disableVOX();\n\n  // done.\n  return ch;\n}\n\nbool\nRadioddityCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack& err) const {\n  Q_UNUSED(err)\n\n  // Link common\n  if (hasScanList() && ctx.has<ScanList>(scanListIndex()))\n    c->setScanList(ctx.get<ScanList>(scanListIndex()));\n  // Link digital channel\n  if (c->is<DMRChannel>()) {\n    DMRChannel *dc = c->as<DMRChannel>();\n    if (hasGroupList() && ctx.has<RXGroupList>(groupListIndex()))\n      dc->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n    if (hasContact() && ctx.has<DMRContact>(contactIndex()))\n      dc->setContact(ctx.get<DMRContact>(contactIndex()));\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack& err) {\n  clear();\n\n  setName(c->name());\n  setRXFrequency(c->rxFrequency().inHz());\n  setTXFrequency(c->txFrequency().inHz());\n  if (c->defaultPower())\n    setPower(ctx.config()->settings()->power());\n  else\n    setPower(c->power());\n  if (c->defaultTimeout())\n    setTXTimeOut(ctx.config()->settings()->tot());\n  else\n    setTXTimeOut(c->timeout());\n  enableRXOnly(c->rxOnly());\n\n  // Enable vox\n  bool defaultVOXEnabled = (c->defaultVOX() && ctx.config()->settings()->audio()->voxEnabled());\n  bool channelVOXEnabled = (! (c->voxDisabled()||c->defaultVOX()));\n  enableVOX(defaultVOXEnabled || channelVOXEnabled);\n\n  if (c->scanList())\n    setScanListIndex(ctx.index(c->scanList()));\n\n  if (c->is<FMChannel>()) {\n    const FMChannel *ac = c->as<const FMChannel>();\n    setMode(MODE_ANALOG);\n    switch (ac->admit()) {\n    case FMChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break;\n    case FMChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break;\n    default: setAdmitCriterion(ADMIT_ALWAYS);\n    }\n    setBandwidth(ac->bandwidth());\n    setRXTone(ac->rxTone());\n    setTXTone(ac->txTone());\n    // no per channel squelch setting\n    enableTalkaround(ac->extended()->talkaround());\n  } else if (c->is<DMRChannel>()) {\n    const DMRChannel *dc = c->as<const DMRChannel>();\n    setMode(MODE_DIGITAL);\n    switch (dc->admit()) {\n    case DMRChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break;\n    case DMRChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break;\n    case DMRChannel::Admit::ColorCode: setAdmitCriterion(ADMIT_COLOR); break;\n    }\n    setTimeSlot(dc->timeSlot());\n    setRXColorCode(dc->colorCode());\n    setTXColorCode(dc->colorCode());\n    if (dc->groupList())\n      setGroupListIndex(ctx.index(dc->groupList()));\n    if (dc->contact())\n      setContactIndex(ctx.index(dc->contact()));\n    enableTalkaround(dc->extended()->talkaround());\n    enableDataCallConfirm(dc->extended()->dataConfirm());\n    enablePrivateCallConfirm(dc->extended()->privateCallConfirm());\n    enableDualCapacityDirectMode(dc->extended()->dcdm());\n  } else {\n    errMsg(err) << \"Cannot encode channel of type '\" << c->metaObject()->className()\n                << \"': Not supported by the radio.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ChannelBankElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ChannelBankElement::ChannelBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ChannelBankElement::~ChannelBankElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ChannelBankElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nRadioddityCodeplug::ChannelBankElement::isEnabled(unsigned idx) const {\n  unsigned byte = Offset::bitmask() + idx/8, bit = idx%8;\n  return getBit(byte, bit);\n}\nvoid\nRadioddityCodeplug::ChannelBankElement::enable(unsigned idx, bool enabled) {\n  unsigned byte = Offset::bitmask() + idx/8, bit = idx%8;\n  return setBit(byte, bit, enabled);\n}\n\nuint8_t *\nRadioddityCodeplug::ChannelBankElement::get(unsigned idx) const {\n  return (_data+Offset::channels())+idx*ChannelElement::size();\n}\n\nRadioddityCodeplug::ChannelElement\nRadioddityCodeplug::ChannelBankElement::channel(unsigned int n) {\n  return ChannelElement((_data+Offset::channels())+n*ChannelElement::size());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of RadioddityCodeplug::VFOChannelElement\n * ******************************************************************************************** */\nRadioddityCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, unsigned size)\n  : ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr)\n  : ChannelElement(ptr)\n{\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::VFOChannelElement::clear() {\n  ChannelElement::clear();\n  setStepSize(12.5);\n  setOffsetMode(OffsetMode::Off);\n  setTXOffset(10.0);\n}\n\nQString\nRadioddityCodeplug::VFOChannelElement::name() const {\n  return QString();\n}\nvoid\nRadioddityCodeplug::VFOChannelElement::setName(const QString &name) {\n  Q_UNUSED(name);\n  ChannelElement::setName(\"\");\n}\n\ndouble\nRadioddityCodeplug::VFOChannelElement::stepSize() const {\n  switch (StepSize(getUInt4(Offset::stepSize()))) {\n  case StepSize::SS2_5kHz: return 2.5;\n  case StepSize::SS5kHz: return 5;\n  case StepSize::SS6_25kHz: return 6.25;\n  case StepSize::SS10kHz: return 10.0;\n  case StepSize::SS12_5kHz: return 12.5;\n  case StepSize::SS20kHz: return 20;\n  case StepSize::SS30kHz: return 30;\n  case StepSize::SS50kHz: return 50;\n  }\n  return 12.5;\n}\n\nvoid\nRadioddityCodeplug::VFOChannelElement::setStepSize(double kHz) {\n  if (2.5 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS2_5kHz);\n  else if (5.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS5kHz);\n  else if (6.25 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS6_25kHz);\n  else if (10.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS10kHz);\n  else if (12.5 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS12_5kHz);\n  else if (20.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS20kHz);\n  else if (30.0 >= kHz)\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS30kHz);\n  else\n    setUInt4(Offset::stepSize(), (unsigned)StepSize::SS50kHz);\n}\n\nRadioddityCodeplug::VFOChannelElement::OffsetMode\nRadioddityCodeplug::VFOChannelElement::offsetMode() const {\n  return (OffsetMode)getUInt2(Offset::offsetMode());\n}\nvoid\nRadioddityCodeplug::VFOChannelElement::setOffsetMode(OffsetMode mode) {\n  setUInt2(Offset::offsetMode(), (unsigned)mode);\n}\ndouble\nRadioddityCodeplug::VFOChannelElement::txOffset() const {\n  return ((double)getBCD4_le(Offset::txOffset()))/100;\n}\nvoid\nRadioddityCodeplug::VFOChannelElement::setTXOffset(double f) {\n  setBCD4_le(Offset::txOffset(), (f*100));\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ContactElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ContactElement::ContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ContactElement::~ContactElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ContactElement::clear() {\n  setName(\"\");\n  setNumber(0);\n  setType(DMRContact::GroupCall);\n  enableRing(0);\n  setRingStyle(0);\n  setUInt8(0x017, 0x00);\n}\n\nbool\nRadioddityCodeplug::ContactElement::isValid() const {\n  return (! name().isEmpty());\n}\n\nQString\nRadioddityCodeplug::ContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::ContactElement::setName(const QString name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\nunsigned\nRadioddityCodeplug::ContactElement::number() const {\n  return getBCD8_be(Offset::number());\n}\nvoid\nRadioddityCodeplug::ContactElement::setNumber(unsigned id) {\n  setBCD8_be(Offset::number(), id);\n}\n\nDMRContact::Type\nRadioddityCodeplug::ContactElement::type() const {\n  switch (getUInt8(Offset::type())) {\n  case 0: return DMRContact::GroupCall;\n  case 1: return DMRContact::PrivateCall;\n  case 2: return DMRContact::AllCall;\n  default: break;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nRadioddityCodeplug::ContactElement::setType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::GroupCall: setUInt8(Offset::type(), 0); break;\n  case DMRContact::PrivateCall: setUInt8(Offset::type(), 1); break;\n  case DMRContact::AllCall: setUInt8(Offset::type(), 2); break;\n  }\n}\n\nbool\nRadioddityCodeplug::ContactElement::ring() const {\n  return 0x00 != getUInt8(Offset::ring());\n}\nvoid\nRadioddityCodeplug::ContactElement::enableRing(bool enable) {\n  setUInt8(Offset::ring(), enable ? 0x01 : 0x00);\n}\n\nunsigned\nRadioddityCodeplug::ContactElement::ringStyle() const {\n  return getUInt8(Offset::ringStyle());\n}\nvoid\nRadioddityCodeplug::ContactElement::setRingStyle(unsigned style) {\n  style = std::min(style, Limit::ringStyle());\n  setUInt8(Offset::ringStyle(), style);\n}\n\nDMRContact *\nRadioddityCodeplug::ContactElement::toContactObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n  if (! isValid()) {\n    errMsg(err) << \"Cannot create contact from an invalid element.\";\n    return nullptr;\n  }\n  return new DMRContact(type(), name(), number(), ring());\n}\n\nbool\nRadioddityCodeplug::ContactElement::fromContactObj(const DMRContact *cont, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  setName(cont->name());\n  setNumber(cont->number());\n  setType(cont->type());\n  if (cont->ring()) {\n    enableRing(true);\n    setRingStyle(1);\n  } else {\n    enableRing(false);\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::DTMFContactElement\n * ********************************************************************************************* */\nRadioddityCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::DTMFContactElement::DTMFContactElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::DTMFContactElement::~DTMFContactElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::DTMFContactElement::clear() {\n  memset(_data, 0xff, Limit::nameLength());\n}\nbool\nRadioddityCodeplug::DTMFContactElement::isValid() const {\n  return (! name().isEmpty());\n}\n\nQString\nRadioddityCodeplug::DTMFContactElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::DTMFContactElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\nQString\nRadioddityCodeplug::DTMFContactElement::number() const {\n  return readASCII(Offset::number(), Limit::numberLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::DTMFContactElement::setNumber(const QString &number) {\n  writeASCII(Offset::number(), number, Limit::numberLength(), 0xff);\n}\n\nDTMFContact *\nRadioddityCodeplug::DTMFContactElement::toContactObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n  if (! isValid()) {\n    errMsg(err) << \"Cannot create a DTMF contact from an invalid element.\";\n    return nullptr;\n  }\n  return new DTMFContact(name(), number());\n}\n\nbool\nRadioddityCodeplug::DTMFContactElement::fromContactObj(const DTMFContact *cont, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  setName(cont->name());\n  setNumber(cont->number());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ZoneElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ZoneElement::~ZoneElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ZoneElement::clear() {\n  memset(_data+Offset::name(), 0xff, Limit::nameLength());\n  memset(_data+Offset::channels(), 0x00, sizeof(uint16_t)*Limit::memberCount());\n}\nbool\nRadioddityCodeplug::ZoneElement::isValid() const {\n  return (! name().isEmpty());\n}\n\nQString\nRadioddityCodeplug::ZoneElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::ZoneElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\nbool\nRadioddityCodeplug::ZoneElement::hasMember(unsigned n) const {\n  if (n >= Limit::memberCount())\n    return false;\n  return (0 != member(n));\n}\nunsigned\nRadioddityCodeplug::ZoneElement::member(unsigned n) const {\n  if (n >= Limit::memberCount())\n    return 0;\n  return getUInt16_le(Offset::channels()+Offset::betweenChannels()*n);\n}\nvoid\nRadioddityCodeplug::ZoneElement::setMember(unsigned n, unsigned idx) {\n  if (n >= Limit::memberCount())\n    return;\n  setUInt16_le(Offset::channels()+Offset::betweenChannels()*n, idx);\n}\nvoid\nRadioddityCodeplug::ZoneElement::clearMember(unsigned n) {\n  setMember(n, 0);\n}\n\nZone *\nRadioddityCodeplug::ZoneElement::toZoneObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx)\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode an invalid zone.\";\n    return nullptr;\n  }\n  return new Zone(name());\n}\n\nbool\nRadioddityCodeplug::ZoneElement::linkZoneObj(Zone *zone, Context &ctx, const ErrorStack &err) const {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link invalid zone.\";\n    return false;\n  }\n\n  for (unsigned int i=0; (i<Limit::memberCount()) && hasMember(i); i++) {\n    if (ctx.has<Channel>(member(i))) {\n      zone->A()->add(ctx.get<Channel>(member(i)));\n    } else {\n      logWarn() << \"While linking zone '\" << zone->name() << \"': \" << i <<\"-th channel index \"\n                << member(i) << \" out of bounds.\";\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::ZoneElement::fromZoneObjA(const Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (zone->A()->count() && zone->B()->count())\n    setName(zone->name() + \" A\");\n  else\n    setName(zone->name());\n\n  for (unsigned int i=0; i<Limit::memberCount(); i++) {\n    if (i < (unsigned int)zone->A()->count())\n      setMember(i, ctx.index(zone->A()->get(i)));\n    else\n      clearMember(i);\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::ZoneElement::fromZoneObjB(const Zone *zone, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (zone->A()->count() && zone->B()->count())\n    setName(zone->name() + \" B\");\n  else\n    setName(zone->name());\n\n  for (unsigned int i=0; i<Limit::memberCount(); i++) {\n    if (i < (unsigned int)zone->B()->count())\n      setMember(i, ctx.index(zone->B()->get(i)));\n    else\n      clearMember(i);\n  }\n\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ZoneBankElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ZoneBankElement::ZoneBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ZoneBankElement::~ZoneBankElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ZoneBankElement::clear() {\n  memset(_data, 0, size());\n}\n\nbool\nRadioddityCodeplug::ZoneBankElement::isEnabled(unsigned idx) const {\n  unsigned byte=Offset::bitmap() + idx/8, bit = idx%8;\n  return getBit(byte, bit);\n}\nvoid\nRadioddityCodeplug::ZoneBankElement::enable(unsigned idx, bool enabled) {\n  unsigned byte=Offset::bitmap() + idx/8, bit = idx%8;\n  setBit(byte, bit, enabled);\n}\n\nuint8_t *\nRadioddityCodeplug::ZoneBankElement::get(unsigned idx) const {\n  return _data + Offset::zones() + idx*ZoneElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::GroupListElement\n * ********************************************************************************************* */\nRadioddityCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::GroupListElement::~GroupListElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::GroupListElement::clear() {\n  setName(\"\");\n  if ((Offset::members() + Offset::betweenMembers()*Limit::memberCount()) > _size) {\n    logFatal() << \"Cannot clear group list: Overflow.\";\n    return;\n  }\n  memset(_data+Offset::members(), 0, Offset::betweenMembers()*Limit::memberCount());\n}\n\nQString\nRadioddityCodeplug::GroupListElement::name() const {\n  return readASCII(Offset::name(), Limit::nameLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::GroupListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::nameLength(), 0xff);\n}\n\nbool\nRadioddityCodeplug::GroupListElement::hasMember(unsigned n) const {\n  if (n >= Limit::memberCount())\n    return false;\n  return 0 != member(n);\n}\nunsigned\nRadioddityCodeplug::GroupListElement::member(unsigned n) const {\n  return getUInt16_le(Offset::members() + sizeof(uint16_t)*n);\n}\nvoid\nRadioddityCodeplug::GroupListElement::setMember(unsigned n, unsigned idx) {\n  return setUInt16_le(Offset::members() + sizeof(uint16_t)*n, idx);\n}\nvoid\nRadioddityCodeplug::GroupListElement::clearMember(unsigned n) {\n  setMember(n,0);\n}\n\nRXGroupList *\nRadioddityCodeplug::GroupListElement::toRXGroupListObj(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  return new RXGroupList(name());\n}\n\nbool\nRadioddityCodeplug::GroupListElement::linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err) const {\n  for (unsigned int i=0; (i<Limit::memberCount()) && (i<ncnt); i++) {\n    if (ctx.has<DMRContact>(member(i))) {\n      lst->addContact(ctx.get<DMRContact>(member(i)));\n    } else {\n      errMsg(err) << \"Cannot link group list '\" << lst->name()\n                  << \"': Member index \" << member(i) << \" does not refer to a digital contact.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::GroupListElement::fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  setName(lst->name());\n\n  int j = 0;\n  // Iterate over all entries in the codeplug\n  for (unsigned int i=0; i<Limit::memberCount(); i++) {\n    if (lst->count() > j) {\n      // Skip non-group-call entries\n      while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) {\n        logWarn() << \"Contact '\" << lst->contact(i)->name() << \"' in group list '\" << lst->name()\n                  << \"' is not a group call. Skip entry.\";\n        j++;\n      }\n      setMember(i, ctx.index(lst->contact(j))); j++;\n    } else {\n      // Clear entry.\n      clearMember(i);\n    }\n  }\n\n  return false;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::GroupListBankElement\n * ********************************************************************************************* */\nRadioddityCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::GroupListBankElement::GroupListBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::GroupListBankElement::~GroupListBankElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::GroupListBankElement::clear() {\n  memset(_data, 0, Limit::groupListCount());\n}\n\nbool\nRadioddityCodeplug::GroupListBankElement::isEnabled(unsigned n) const {\n  return 0 != getUInt8(Offset::contactCounts() + n);\n}\nunsigned\nRadioddityCodeplug::GroupListBankElement::contactCount(unsigned n) const {\n  return getUInt8(Offset::contactCounts() + n) - 1;\n}\nvoid\nRadioddityCodeplug::GroupListBankElement::setContactCount(unsigned n, unsigned size) {\n  setUInt8(Offset::contactCounts() + n, size+1);\n}\nvoid\nRadioddityCodeplug::GroupListBankElement::disable(unsigned n) {\n  setUInt8(Offset::contactCounts() + n, 0);\n}\n\nuint8_t *\nRadioddityCodeplug::GroupListBankElement::get(unsigned n) const {\n  if ((Offset::groupLists() + (n+1)*GroupListElement::size())>_size) {\n    logFatal() << \"Cannot resolve group list at index \" << n << \": Overflow.\";\n    return nullptr;\n  }\n  return _data + Offset::groupLists() + n*GroupListElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ScanListElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ScanListElement::~ScanListElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ScanListElement::clear() {\n  setName(\"\");\n  setUInt8(0x000f, 0xff);\n  // Clear member\n  memset(_data+0x0010, 0x00, 2*32);\n  clearPrimary();\n  clearSecondary();\n  clearRevert();\n  setHoldTime(1000);\n  setPrioritySampleTime(2000);\n}\n\nQString\nRadioddityCodeplug::ScanListElement::name() const {\n  return readASCII(Offset::name(), Limit::name(), 0xff);\n}\nvoid\nRadioddityCodeplug::ScanListElement::setName(const QString &name) {\n  writeASCII(Offset::name(), name, Limit::name(), 0xff);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::channelMark() const {\n  return getBit(Offset::channelMark());\n}\nvoid\nRadioddityCodeplug::ScanListElement::enableChannelMark(bool enable) {\n  setBit(Offset::channelMark(), enable);\n}\n\nRadioddityCodeplug::ScanListElement::Mode\nRadioddityCodeplug::ScanListElement::mode() const {\n  return (Mode) getUInt2(Offset::mode());\n}\nvoid\nRadioddityCodeplug::ScanListElement::setMode(Mode mode) {\n  setUInt2(Offset::mode(), (unsigned)mode);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::talkback() const {\n  return getBit(Offset::talkback());\n}\nvoid\nRadioddityCodeplug::ScanListElement::enableTalkback(bool enable) {\n  setBit(Offset::talkback(), enable);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::hasMember(unsigned n) const {\n  return 0 != getUInt16_le(Offset::members()+Offset::betweenMembers()*n);\n}\nbool\nRadioddityCodeplug::ScanListElement::isSelected(unsigned n) const {\n  return 1 == getUInt16_le(Offset::members()+Offset::betweenMembers()*n);\n}\nunsigned\nRadioddityCodeplug::ScanListElement::member(unsigned n) const {\n  return getUInt16_le(Offset::members() + Offset::betweenMembers()*n)-1;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setMember(unsigned n, unsigned idx) {\n  setUInt16_le(Offset::members()+Offset::betweenMembers()*n, idx+1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::setSelected(unsigned n) {\n  setUInt16_le(Offset::members() + Offset::betweenMembers()*n, 1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::clearMember(unsigned n) {\n  setUInt16_le(Offset::members() + Offset::betweenMembers()*n, 0);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::hasPrimary() const {\n  return 0 != getUInt16_le(Offset::primary());\n}\nbool\nRadioddityCodeplug::ScanListElement::primaryIsSelected() const {\n  return 1 == getUInt16_le(Offset::primary());\n}\nunsigned\nRadioddityCodeplug::ScanListElement::primary() const {\n  return getUInt16_le(Offset::primary())-1;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setPrimary(unsigned idx) {\n  setUInt16_le(Offset::primary(), idx+1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::setPrimarySelected() {\n  setUInt16_le(Offset::primary(), 1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::clearPrimary() {\n  setUInt16_le(Offset::primary(), 0);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::hasSecondary() const {\n  return 0 != getUInt16_le(Offset::secondary());\n}\nbool\nRadioddityCodeplug::ScanListElement::secondaryIsSelected() const {\n  return 1 == getUInt16_le(Offset::secondary());\n}\nunsigned\nRadioddityCodeplug::ScanListElement::secondary() const {\n  return getUInt16_le(Offset::secondary())-1;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setSecondary(unsigned idx) {\n  setUInt16_le(Offset::secondary(), idx+1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::setSecondarySelected() {\n  setUInt16_le(Offset::secondary(), 1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::clearSecondary() {\n  setUInt16_le(Offset::secondary(), 0);\n}\n\nbool\nRadioddityCodeplug::ScanListElement::hasRevert() const {\n  return 0 != getUInt16_le(Offset::revert());\n}\nbool\nRadioddityCodeplug::ScanListElement::revertIsSelected() const {\n  return 1 == getUInt16_le(Offset::revert());\n}\nunsigned\nRadioddityCodeplug::ScanListElement::revert() const {\n  return getUInt16_le(Offset::revert())-1;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setRevert(unsigned idx) {\n  setUInt16_le(Offset::revert(), idx+1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::setRevertSelected() {\n  setUInt16_le(Offset::revert(), 1);\n}\nvoid\nRadioddityCodeplug::ScanListElement::clearRevert() {\n  setUInt16_le(Offset::revert(), 0);\n}\n\nunsigned\nRadioddityCodeplug::ScanListElement::holdTime() const {\n  return unsigned(getUInt8(Offset::holdTime()))*25;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setHoldTime(unsigned ms) {\n  setUInt8(Offset::holdTime(), ms/25);\n}\n\nunsigned\nRadioddityCodeplug::ScanListElement::prioritySampleTime() const {\n  return unsigned(getUInt8(Offset::primaryHoldTime()))*250;\n}\nvoid\nRadioddityCodeplug::ScanListElement::setPrioritySampleTime(unsigned ms) {\n  setUInt8(Offset::primaryHoldTime(), ms/250);\n}\n\nScanList *\nRadioddityCodeplug::ScanListElement::toScanListObj(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(ctx); Q_UNUSED(err)\n  return new ScanList(name());\n}\n\nbool\nRadioddityCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err) const {\n  if (primaryIsSelected()) {\n    lst->setPrimaryChannel(SelectedChannel::get());\n  } else if (hasPrimary()) {\n    if (! ctx.has<Channel>(primary())) {\n      errMsg(err) << \"Cannot link scan list '\" << lst->name()\n                  << \"', primary priority channel index \" << primary() << \" not defined.\";\n      return false;\n    }\n    lst->setPrimaryChannel(ctx.get<Channel>(primary()));\n  }\n\n  if (secondaryIsSelected()) {\n    lst->setSecondaryChannel(SelectedChannel::get());\n  } else if (hasSecondary()) {\n    if (! ctx.has<Channel>(secondary())) {\n      errMsg(err) << \"Cannot link scan list '\" << lst->name()\n                  << \"', secondary priority channel index \" << secondary() << \" not defined.\";\n      return false;\n    }\n    lst->setSecondaryChannel(ctx.get<Channel>(secondary()));\n  }\n\n  if (revertIsSelected()) {\n    lst->setRevertChannel(SelectedChannel::get());\n  } else if (hasRevert()) {\n    if (! ctx.has<Channel>(revert())) {\n      errMsg(err) << \"Cannot link scan list '\" << lst->name()\n                  << \"', revert channel index \" << revert() << \" not defined.\";\n      return false;\n    }\n    lst->setRevertChannel(ctx.get<Channel>(revert()));\n  }\n\n  for (unsigned int i=0; (i<Limit::members()) && hasMember(i); i++) {\n    if (isSelected(i))\n      lst->addChannel(SelectedChannel::get());\n    else if (hasMember(i)) {\n      if (! ctx.has<Channel>(member(i))) {\n        errMsg(err) << \"Cannot link scan list '\" << lst->name()\n                    << \"', \" << (i+1) << \"-th member index \" << member(i) << \" not defined.\";\n        return false;\n      }\n      lst->addChannel(ctx.get<Channel>(member(i)));\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::ScanListElement::fromScanListObj(const ScanList *lst, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  clear();\n  setName(lst->name());\n\n  if (lst->primaryChannel() && (SelectedChannel::get() == lst->primaryChannel()))\n    setPrimarySelected();\n  else if (lst->primaryChannel())\n    setPrimary(ctx.index(lst->primaryChannel()));\n\n  if (lst->secondaryChannel() && (SelectedChannel::get() == lst->secondaryChannel()))\n    setSecondarySelected();\n  else if (lst->secondaryChannel())\n    setSecondary(ctx.index(lst->secondaryChannel()));\n\n  if (lst->revertChannel() && (SelectedChannel::get() == lst->revertChannel()))\n    setRevertSelected();\n  else if (lst->revertChannel())\n    setRevert(ctx.index(lst->revertChannel()));\n\n  for (unsigned int i=0; i<Limit::members(); i++) {\n    if (i >= (unsigned int)lst->count())\n      clearMember(i);\n    else if (SelectedChannel::get() == lst->channel(i))\n      setSelected(i);\n    else\n      setMember(i, ctx.index(lst->channel(i)));\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ScanListBankElement\n * ********************************************************************************************* */\nRadioddityCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ScanListBankElement::ScanListBankElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::ScanListBankElement::~ScanListBankElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ScanListBankElement::clear() {\n  memset(_data, 0, Limit::scanListCount());\n}\n\nbool\nRadioddityCodeplug::ScanListBankElement::isEnabled(unsigned n) const {\n  return 0x00 != getUInt8(Offset::bytemap() + n);\n}\nvoid\nRadioddityCodeplug::ScanListBankElement::enable(unsigned n, bool enabled) {\n  if (enabled)\n    setUInt8(Offset::bytemap() + n, 0x01);\n  else\n    setUInt8(Offset::bytemap() + n, 0x00);\n}\n\nuint8_t *\nRadioddityCodeplug::ScanListBankElement::get(unsigned n) const {\n  return _data+Offset::scanLists() + n*ScanListElement::size();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::GeneralSettingsElement\n * ********************************************************************************************* */\nRadioddityCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pas...\n}\n\nRadioddityCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::GeneralSettingsElement::~GeneralSettingsElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::GeneralSettingsElement::clear() {\n  memset(_data+0x0000, 0xff, 8);\n  memset(_data+0x0008, 0x00, 4);\n  setUInt32_be(0x000c, 0);\n  setUInt8(0x0010, 0);\n\n  setPreambleDuration(Interval::fromMilliseconds(360));\n  setMonitorType(MonitorType::Silent);\n  setVOXSensitivity(Level::fromValue(3));\n  setLowBatteryWarnInterval(30);\n  setCallAlertDuration(120);\n  setLoneWorkerResponsePeriod(1);\n  setLoneWorkerReminderPeriod(10);\n  setGroupCallHangTime(Interval::fromMilliseconds(3000));\n  setPrivateCallHangTime(Interval::fromMilliseconds(3000));\n\n  enableDownChannelModeVFO(false);\n  enableUpChannelModeVFO(false);\n  enableResetTone(false);\n  enableUnknownNumberTone(false);\n  setARTSToneMode(ARTSTone::Once);\n\n  enableDigitalTalkPermitTone(false);\n  enableAnalogTalkPermitTone(false);\n  enableSelftestTone(true);\n  enableChannelFreeIndicationTone(false);\n  setBit(0x001b, 4, false);\n  disableAllTones(false);\n  enableBatsaveRX(true);\n  enableBatsavePreamble(true);\n\n  setUInt5(0x001c, 0, 0);\n  disableAllLEDs(false);\n  inhibitQuickKeyOverride(false);\n  setBit(0x001c, 7, true);\n\n  setUInt3(0x001d, 0, 0);\n  enableTXExitTone(false);\n  enableTXOnActiveChannel(true);\n  enableAnimation(false);\n  setScanMode(ScanMode::Time);\n\n  setRepeaterEndDelay(0);\n  setRepeaterSTE(0);\n\n  setUInt8(0x001f, 0);\n  clearProgPassword();\n}\n\nQString\nRadioddityCodeplug::GeneralSettingsElement::name() const {\n  return readASCII(0x0000, 8, 0xff);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setName(const QString &name) {\n  writeASCII(0x0000, name, 8, 0xff);\n}\n\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::radioID() const {\n  return getBCD8_be(0x0008);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setRadioID(unsigned id) {\n  setBCD8_be(0x0008, id);\n}\n\nInterval\nRadioddityCodeplug::GeneralSettingsElement::preambleDuration() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x0011)*60));\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setPreambleDuration(const Interval &dur) {\n  setUInt8(0x0011, dur.milliseconds()/60);\n}\n\nRadioddityCodeplug::GeneralSettingsElement::MonitorType\nRadioddityCodeplug::GeneralSettingsElement::monitorType() const {\n  return MonitorType(getUInt8(0x0012));\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setMonitorType(MonitorType type) {\n  setUInt8(0x0012, (unsigned)type);\n}\n\nLevel\nRadioddityCodeplug::GeneralSettingsElement::voxSensitivity() const {\n  return Level::fromValue(getUInt8(0x0013));\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setVOXSensitivity(Level value) {\n  setUInt8(0x0013, value.mapTo(Limit::vox()));\n}\n\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::lowBatteryWarnInterval() const {\n  return unsigned(getUInt8(0x0014))*5;\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setLowBatteryWarnInterval(unsigned sec) {\n  setUInt8(0x0014, sec/5);\n}\n\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::callAlertDuration() const {\n  return unsigned(getUInt8(0x0015))*5;\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setCallAlertDuration(unsigned sec) {\n  setUInt8(0x0015, sec/5);\n}\n\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::loneWorkerResponsePeriod() const {\n  return getUInt8(0x0016);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setLoneWorkerResponsePeriod(unsigned min) {\n  setUInt8(0x0016, min);\n}\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::loneWorkerReminderPeriod() const {\n  return getUInt8(0x0017);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setLoneWorkerReminderPeriod(unsigned sec) {\n  setUInt8(0x0017, sec);\n}\n\nInterval\nRadioddityCodeplug::GeneralSettingsElement::groupCallHangTime() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x0018))*500);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setGroupCallHangTime(const Interval &dur) {\n  setUInt8(0x0018, dur.milliseconds()/500);\n}\nInterval\nRadioddityCodeplug::GeneralSettingsElement::privateCallHangTime() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x0019))*500);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setPrivateCallHangTime(const Interval &dur) {\n  setUInt8(0x0019, dur.milliseconds()/500);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::downChannelModeVFO() const {\n  return getBit(0x001a, 0);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableDownChannelModeVFO(bool enable) {\n  setBit(0x001a, 0, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::upChannelModeVFO() const {\n  return getBit(0x001a, 1);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableUpChannelModeVFO(bool enable) {\n  setBit(0x001a, 1, enable);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::resetTone() const {\n  return getBit(0x001a, 2);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableResetTone(bool enable) {\n  setBit(0x001a, 2, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::unknownNumberTone() const {\n  return getBit(0x001a, 3);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableUnknownNumberTone(bool enable) {\n  setBit(0x001a, 3, enable);\n}\n\nRadioddityCodeplug::GeneralSettingsElement::ARTSTone\nRadioddityCodeplug::GeneralSettingsElement::artsToneMode() const {\n  return ARTSTone(getUInt4(0x001a, 4));\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setARTSToneMode(ARTSTone mode) {\n  setUInt4(0x001a, 4, (unsigned) mode);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::digitalTalkPermitTone() const {\n  return getBit(0x001b, 0);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableDigitalTalkPermitTone(bool enable) {\n  setBit(0x001b, 0, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::analogTalkPermitTone() const {\n  return getBit(0x001b, 1);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableAnalogTalkPermitTone(bool enable) {\n  setBit(0x001b, 1, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::selftestTone() const {\n  return getBit(0x001b, 2);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableSelftestTone(bool enable) {\n  setBit(0x001b, 2, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::channelFreeIndicationTone() const {\n  return getBit(0x001b, 3);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableChannelFreeIndicationTone(bool enable) {\n  setBit(0x001b, 3, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::allTonesDisabled() const {\n  return getBit(0x001b, 5);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::disableAllTones(bool disable) {\n  setBit(0x001b, 5, disable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::batsaveRX() const {\n  return getBit(0x001b, 6);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableBatsaveRX(bool enable) {\n  setBit(0x001b, 6, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::batsavePreamble() const {\n  return getBit(0x001b, 7);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableBatsavePreamble(bool enable) {\n  setBit(0x001b, 7, enable);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::allLEDsDisabled() const {\n  return getBit(0x001c, 5);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::disableAllLEDs(bool disable) {\n  setBit(0x001c, 5, disable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::quickKeyOverrideInhibited() const {\n  return getBit(0x001c, 6);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::inhibitQuickKeyOverride(bool inhibit) {\n  setBit(0x001c, 6, inhibit);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::txExitTone() const {\n  return getBit(0x001d, 3);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableTXExitTone(bool enable) {\n  setBit(0x001d, 3, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::txOnActiveChannel() const {\n  return getBit(0x001d, 4);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableTXOnActiveChannel(bool enable) {\n  setBit(0x001d, 4, enable);\n}\nbool\nRadioddityCodeplug::GeneralSettingsElement::animation() const {\n  return getBit(0x001d, 5);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::enableAnimation(bool enable) {\n  setBit(0x001d, 5, enable);\n}\nRadioddityCodeplug::GeneralSettingsElement::ScanMode\nRadioddityCodeplug::GeneralSettingsElement::scanMode() const {\n  return ScanMode(getUInt2(0x001d, 6));\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setScanMode(ScanMode mode) {\n  setUInt2(0x001d, 6, unsigned(mode));\n}\n\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::repeaterEndDelay() const {\n  return getUInt4(0x001e, 0);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setRepeaterEndDelay(unsigned delay) {\n  setUInt4(0x001e, 0, delay);\n}\nunsigned\nRadioddityCodeplug::GeneralSettingsElement::repeaterSTE() const {\n  return getUInt4(0x001e, 4);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setRepeaterSTE(unsigned ste) {\n  setUInt4(0x001e, 4, ste);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::hasProgPassword() const {\n  return (0xff != _data[0x0020]) && (0x00 != _data[0x0020]);\n}\nQString\nRadioddityCodeplug::GeneralSettingsElement::progPassword() const {\n  return readASCII(0x0020, 8, 0xff);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::setProgPassword(const QString &pwd) {\n  writeASCII(0x0020, pwd, 8, 0xff);\n}\nvoid\nRadioddityCodeplug::GeneralSettingsElement::clearProgPassword() {\n  memset(_data+0x0020, 0xff, 8);\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::fromConfig(Context &ctx, const ErrorStack &err) {\n  if (! ctx.config()->settings()->defaultIdRef()->isNull()) {\n    setName(ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()->name());\n    setRadioID(ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()->number());\n  } else if (ctx.count<DMRRadioID>()) {\n    setName(ctx.get<DMRRadioID>(0)->name());\n    setRadioID(ctx.get<DMRRadioID>(0)->number());\n  } else {\n    errMsg(err) << \"Cannot encode radioddity codeplug: No radio ID defined.\";\n    return false;\n  }\n\n  setVOXSensitivity(ctx.config()->settings()->audio()->vox());\n  enableAnimation(BootSettings::BootDisplay::Logo == ctx.config()->settings()->boot()->bootDisplay());\n  setPreambleDuration(ctx.config()->settings()->dmr()->preamble());\n  setGroupCallHangTime(ctx.config()->settings()->dmr()->groupCallHangTime());\n  setPrivateCallHangTime(ctx.config()->settings()->dmr()->privateCallHangTime());\n  // There is no global squelch settings either\n  // Apply tone settings\n  disableAllTones(ctx.config()->settings()->tone()->silent());\n  enableDigitalTalkPermitTone(ctx.config()->settings()->tone()->talkPermit().testFlag(Channel::Type::DMR));\n  enableAnalogTalkPermitTone(ctx.config()->settings()->tone()->talkPermit().testFlag(Channel::Type::FM));\n  enableTXExitTone(\n    ctx.config()->settings()->tone()->callEnd().testFlags(\n      Channel::Type::DMR | Channel::Type::FM));\n  enableChannelFreeIndicationTone(\n    ctx.config()->settings()->tone()->channelIdle().testAnyFlags(\n      Channel::Type::DMR|Channel::Type::FM));\n  enableResetTone(ctx.config()->settings()->tone()->callResetEnabled());\n\n  // Handle Radioddity extension\n  if (RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension()) {\n    setMonitorType(ext->monitorType());\n    setLowBatteryWarnInterval(ext->tone()->lowBatteryWarnInterval().seconds());\n    setCallAlertDuration(ext->tone()->callAlertDuration().seconds());\n    setLoneWorkerResponsePeriod(ext->loneWorkerResponseTime().minutes());\n    setLoneWorkerReminderPeriod(ext->loneWorkerReminderPeriod().seconds());\n    enableDownChannelModeVFO(ext->downChannelModeVFO());\n    enableUpChannelModeVFO(ext->upChannelModeVFO());\n    enableUnknownNumberTone(ext->tone()->unknownNumberTone());\n    setARTSToneMode(ext->tone()->artsToneMode());\n    enableSelftestTone(ext->tone()->selftestTone());\n    enableBatsaveRX(ext->powerSaveMode());\n    enableBatsavePreamble(ext->wakeupPreamble());\n    disableAllLEDs(ext->allLEDsDisabled());\n    inhibitQuickKeyOverride(ext->quickKeyOverrideInhibited());\n    enableTXOnActiveChannel(ext->txOnActiveChannel());\n    setScanMode(ext->scanMode());\n    setRepeaterEndDelay(ext->repeaterEndDelay().seconds());\n    setRepeaterSTE(ext->repeaterSTE().seconds());\n    if (ext->boot()->progPassword().isEmpty())\n      clearProgPassword();\n    else\n      setProgPassword(ext->boot()->progPassword());\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::GeneralSettingsElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (ctx.config()->settings()->defaultIdRef()->isNull()) {\n    int idx = ctx.config()->radioIDs()->add(new DMRRadioID(name(), radioID()));\n    ctx.config()->settings()->defaultIdRef()->set(\n      ctx.config()->radioIDs()->get(idx)->as<DMRRadioID>());\n  } else {\n    ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()->setName(name());\n    ctx.config()->settings()->defaultIdRef()->as<DMRRadioID>()->setNumber(radioID());\n  }\n  ctx.config()->settings()->audio()->setVox(voxSensitivity());\n  if (animation())\n    ctx.config()->settings()->boot()->setBootDisplay(BootSettings::BootDisplay::Logo);\n  ctx.config()->settings()->dmr()->setPreamble(preambleDuration());\n  ctx.config()->settings()->dmr()->setGroupCallHangTime(groupCallHangTime());\n  ctx.config()->settings()->dmr()->setPrivateCallHangTime(privateCallHangTime());\n\n  // Apply audio settings.\n  // there is no global squelch settings either, so set it to 1\n  ctx.config()->settings()->audio()->setSquelch(Level::fromValue(1));\n\n  // Apply tone settings\n  ctx.config()->settings()->tone()->enableSilent(allTonesDisabled());\n  ctx.config()->settings()->tone()->setTalkPermit(\n    (digitalTalkPermitTone() ? Channel::Type::DMR : Channel::Type::None) |\n    (analogTalkPermitTone() ? Channel::Type::FM : Channel::Type::None) );\n  ctx.config()->settings()->tone()->setCallEnd(\n    txExitTone() ? (Channel::Type::FM|Channel::Type::DMR) : Channel::Type::None);\n  ctx.config()->settings()->tone()->setChannelIdle(\n    channelFreeIndicationTone() ? (Channel::Type::FM|Channel::Type::DMR) : Channel::Type::None);\n  ctx.config()->settings()->tone()->enableCallReset(resetTone());\n\n  // Allocate Radioddity extension if needed\n  RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension();\n  if (nullptr == ext) {\n    ext = new RadiodditySettingsExtension();\n    ctx.config()->settings()->setRadioddityExtension(ext);\n  }\n  // Update settings extension\n  ext->setMonitorType(monitorType());\n  ext->tone()->setLowBatteryWarnInterval(Interval::fromSeconds(lowBatteryWarnInterval()));\n  ext->tone()->setCallAlertDuration(Interval::fromSeconds(callAlertDuration()));\n  ext->setLoneWorkerResponseTime(Interval::fromMinutes(loneWorkerResponsePeriod()));\n  ext->setLoneWorkerReminderPeriod(Interval::fromSeconds(loneWorkerReminderPeriod()));\n  ext->enableDownChannelModeVFO(downChannelModeVFO());\n  ext->enableUpChannelModeVFO(upChannelModeVFO());\n  ext->tone()->enableUnknownNumberTone(unknownNumberTone());\n  ext->tone()->setARTSToneMode(artsToneMode());\n  ext->tone()->enableSelftestTone(selftestTone());\n  ext->enablePowerSaveMode(batsaveRX());\n  ext->enableWakeupPreamble(batsavePreamble());\n  ext->disableAllLEDs(allLEDsDisabled());\n  ext->inhibitQuickKeyOverride(quickKeyOverrideInhibited());\n  ext->enableTXOnActiveChannel(txOnActiveChannel());\n  ext->setScanMode(scanMode());\n  ext->setRepeaterEndDelay(Interval::fromSeconds(repeaterEndDelay()));\n  ext->setRepeaterSTE(Interval::fromSeconds(repeaterSTE()));\n  if (hasProgPassword())\n    ext->boot()->setProgPassword(progPassword());\n  else\n    ext->boot()->setProgPassword(\"\");\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::ButtonSettingsElement\n * ********************************************************************************************* */\nuint8_t\nRadioddityCodeplug::ButtonSettingsElement::KeyFunction::encode(RadioddityButtonSettingsExtension::Function func) {\n  switch (func) {\n  case RadioddityButtonSettingsExtension::Function::None: return None;\n  case RadioddityButtonSettingsExtension::Function::ToggleAllAlertTones: return ToggleAllAlertTones;\n  case RadioddityButtonSettingsExtension::Function::EmergencyOn: return EmergencyOn;\n  case RadioddityButtonSettingsExtension::Function::EmergencyOff: return EmergencyOff;\n  case RadioddityButtonSettingsExtension::Function::ToggleMonitor: return ToggleMonitor;\n  case RadioddityButtonSettingsExtension::Function::RadioDisable: return NuiaceDelete;\n  case RadioddityButtonSettingsExtension::Function::OneTouch1: return OneTouch1;\n  case RadioddityButtonSettingsExtension::Function::OneTouch2: return OneTouch2;\n  case RadioddityButtonSettingsExtension::Function::OneTouch3: return OneTouch3;\n  case RadioddityButtonSettingsExtension::Function::OneTouch4: return OneTouch4;\n  case RadioddityButtonSettingsExtension::Function::OneTouch5: return OneTouch5;\n  case RadioddityButtonSettingsExtension::Function::OneTouch6: return OneTouch6;\n  case RadioddityButtonSettingsExtension::Function::ToggleTalkaround: return ToggleRepeatTalkaround;\n  case RadioddityButtonSettingsExtension::Function::ToggleScan: return ToggleScan;\n  case RadioddityButtonSettingsExtension::Function::ToggleEncryption: return TogglePrivacy;\n  case RadioddityButtonSettingsExtension::Function::ToggleVox: return ToggleVox;\n  case RadioddityButtonSettingsExtension::Function::ZoneSelect: return ZoneSelect;\n  case RadioddityButtonSettingsExtension::Function::BatteryIndicator: return BatteryIndicator;\n  case RadioddityButtonSettingsExtension::Function::ToggleLoneWorker: return ToggleLoneWorker;\n  case RadioddityButtonSettingsExtension::Function::PhoneExit: return PhoneExit;\n  case RadioddityButtonSettingsExtension::Function::ToggleFlashLight: return ToggleFlashLight;\n  case RadioddityButtonSettingsExtension::Function::ToggleFMRadio: return ToggleFMRadio;\n  default: break;\n  }\n  return Action::None;\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::KeyFunction::decode(uint8_t action) {\n  switch ((Action) action) {\n  case None: return RadioddityButtonSettingsExtension::Function::None;\n  case ToggleAllAlertTones: return RadioddityButtonSettingsExtension::Function::ToggleAllAlertTones;\n  case EmergencyOn: return RadioddityButtonSettingsExtension::Function::EmergencyOn;\n  case EmergencyOff: return RadioddityButtonSettingsExtension::Function::EmergencyOff;\n  case ToggleMonitor: return RadioddityButtonSettingsExtension::Function::ToggleMonitor;\n  case NuiaceDelete: return RadioddityButtonSettingsExtension::Function::RadioDisable;\n  case OneTouch1: return RadioddityButtonSettingsExtension::Function::OneTouch1;\n  case OneTouch2: return RadioddityButtonSettingsExtension::Function::OneTouch2;\n  case OneTouch3: return RadioddityButtonSettingsExtension::Function::OneTouch3;\n  case OneTouch4: return RadioddityButtonSettingsExtension::Function::OneTouch4;\n  case OneTouch5: return RadioddityButtonSettingsExtension::Function::OneTouch5;\n  case OneTouch6: return RadioddityButtonSettingsExtension::Function::OneTouch6;\n  case ToggleRepeatTalkaround: return RadioddityButtonSettingsExtension::Function::ToggleTalkaround;\n  case ToggleScan: return RadioddityButtonSettingsExtension::Function::ToggleScan;\n  case TogglePrivacy: return RadioddityButtonSettingsExtension::Function::ToggleEncryption;\n  case ToggleVox: return RadioddityButtonSettingsExtension::Function::ToggleVox;\n  case ZoneSelect: return RadioddityButtonSettingsExtension::Function::ZoneSelect;\n  case BatteryIndicator: return RadioddityButtonSettingsExtension::Function::BatteryIndicator;\n  case ToggleLoneWorker: return RadioddityButtonSettingsExtension::Function::ToggleLoneWorker;\n  case PhoneExit: return RadioddityButtonSettingsExtension::Function::PhoneExit;\n  case ToggleFlashLight: return RadioddityButtonSettingsExtension::Function::ToggleFlashLight;\n  case ToggleFMRadio: return RadioddityButtonSettingsExtension::Function::ToggleFMRadio;\n  }\n\n  return RadioddityButtonSettingsExtension::Function::None;\n}\n\n\nRadioddityCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x0020)\n{\n  // pass...\n}\n\nRadioddityCodeplug::ButtonSettingsElement::~ButtonSettingsElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::ButtonSettingsElement::clear() {\n  setUInt8(0x0000, 0x01);\n  setLongPressDuration(Interval::fromMilliseconds(1500));\n  setSK1ShortPress(RadioddityButtonSettingsExtension::Function::ZoneSelect);\n  setSK1LongPress(RadioddityButtonSettingsExtension::Function::ToggleFMRadio);\n  setSK2ShortPress(RadioddityButtonSettingsExtension::Function::ToggleMonitor);\n  setSK2LongPress(RadioddityButtonSettingsExtension::Function::ToggleFlashLight);\n  setTKShortPress(RadioddityButtonSettingsExtension::Function::BatteryIndicator);\n  setTKLongPress(RadioddityButtonSettingsExtension::Function::ToggleVox);\n  memset(_data+0x0008, 0xff, 6*4);\n}\n\nInterval\nRadioddityCodeplug::ButtonSettingsElement::longPressDuration() const {\n  return Interval::fromMilliseconds(getUInt8(Offset::longPressDuration())*250);\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setLongPressDuration(Interval ms) {\n  setUInt8(Offset::longPressDuration(), ms.milliseconds()/250);\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::sk1ShortPress() const {\n  return KeyFunction::decode(getUInt8(Offset::sk1ShortPress()));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setSK1ShortPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::sk1ShortPress(), KeyFunction::encode(action));\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::sk1LongPress() const {\n  return KeyFunction::decode(getUInt8(0x0003));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setSK1LongPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::sk1LongPress(), KeyFunction::encode(action));\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::sk2ShortPress() const {\n  return KeyFunction::decode(getUInt8(Offset::sk2ShortPress()));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setSK2ShortPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::sk2ShortPress(), KeyFunction::encode(action));\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::sk2LongPress() const {\n  return KeyFunction::decode(getUInt8(Offset::sk2LongPress()));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setSK2LongPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::sk2LongPress(), KeyFunction::encode(action));\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::tkShortPress() const {\n  return KeyFunction::decode(getUInt8(Offset::tkShortPress()));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setTKShortPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::tkShortPress(), KeyFunction::encode(action));\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityCodeplug::ButtonSettingsElement::tkLongPress() const {\n  return KeyFunction::decode(getUInt8(Offset::tkLongPress()));\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setTKLongPress(RadioddityButtonSettingsExtension::Function action) {\n  setUInt8(Offset::tkLongPress(), KeyFunction::encode(action));\n}\n\nRadioddityCodeplug::ButtonSettingsElement::OneTouchAction\nRadioddityCodeplug::ButtonSettingsElement::oneTouchAction(unsigned n) const {\n  return OneTouchAction(getUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0));\n}\nunsigned\nRadioddityCodeplug::ButtonSettingsElement::oneTouchContact(unsigned n) const {\n  return getUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1);\n}\nunsigned\nRadioddityCodeplug::ButtonSettingsElement::oneTouchMessage(unsigned n) const {\n  return getUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3);\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::disableOneTouch(unsigned n) {\n  setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::None);\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setOneTouchDigitalCall(unsigned n, unsigned index) {\n  setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::DigitalCall);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, index);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, 0);\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setOneTouchDigitalMessage(unsigned n, unsigned index) {\n  setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::DigitalMessage);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, 0);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, index);\n}\nvoid\nRadioddityCodeplug::ButtonSettingsElement::setOneTouchAnalogCall(unsigned n) {\n  setUInt8(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 0, (unsigned)OneTouchAction::AnalogCall);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 1, 0);\n  setUInt16_be(Offset::oneTouchActions() + n*Offset::betweenOneTouchActions() + 3, 0);\n}\n\nbool\nRadioddityCodeplug::ButtonSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (! ctx.config()->settings()->radioddityExtension())\n    return true;\n  RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension();\n\n  setLongPressDuration(ext->buttons()->longPressDuration());\n  setSK1ShortPress(ext->buttons()->funcKey1Short());\n  setSK1LongPress(ext->buttons()->funcKey1Long());\n  setSK2ShortPress(ext->buttons()->funcKey2Short());\n  setSK2LongPress(ext->buttons()->funcKey2Long());\n  setTKShortPress(ext->buttons()->funcKey3Short());\n  setTKLongPress(ext->buttons()->funcKey3Long());\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::ButtonSettingsElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (! ctx.config()->settings()->radioddityExtension())\n    ctx.config()->settings()->setRadioddityExtension(new RadiodditySettingsExtension());\n  RadiodditySettingsExtension *ext = ctx.config()->settings()->radioddityExtension();\n\n  ext->buttons()->setLongPressDuration(longPressDuration());\n  ext->buttons()->setFuncKey1Short(sk1ShortPress());\n  ext->buttons()->setFuncKey1Long(sk1LongPress());\n  ext->buttons()->setFuncKey2Short(sk2ShortPress());\n  ext->buttons()->setFuncKey2Long(sk2LongPress());\n  ext->buttons()->setFuncKey3Short(tkShortPress());\n  ext->buttons()->setFuncKey3Long(tkLongPress());\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::MenuSettingsElement\n * ********************************************************************************************* */\nRadioddityCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x0008)\n{\n  // pass...\n}\n\nRadioddityCodeplug::MenuSettingsElement::~MenuSettingsElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::MenuSettingsElement::clear() {\n  setMenuHangTime(10);\n\n  enableMessage(true);\n  enableScanStart(true);\n  enableCallAlert(true);\n  enableEditContact(true);\n  enableManualDial(true);\n  enableRadioCheck(true);\n  enableRemoteMonitor(true);\n\n  enableRadioEnable(true);\n  enableRadioDisable(true);\n  enableProgPassword(true);\n  enableTalkaround(true);\n  enableTone(true);\n  enablePower(true);\n  enableBacklight(true);\n  enableIntroScreen(true);\n\n  enableKeypadLock(true);\n  enableLEDIndicator(true);\n  enableSquelch(true);\n  enablePrivacy(true);\n  enableVOX(true);\n  enablePasswordLock(true);\n  enableMissedCalls(true);\n  enableAnsweredCalls(true);\n\n  enableOutgoingCalls(true);\n  enableChannelDisplay(true);\n  enableDualWatch(true);\n  setBit(0x0004, 3, 0); setBit(0x0004, 4, 0); setBit(0x0004, 5, 1); setBit(0x0004, 6, 1);\n  setBit(0x0004, 7, 1);\n\n  setUInt8(0x0005, 0xff);\n\n  setKeypadLockTime(0);\n  setBacklightTime(15);\n  setUInt2(0x0006, 4, 0);\n  setChannelDisplayMode(ChannelDisplayMode::Name);\n\n  setUInt4(0x0007, 0, 0);\n  setBit(0x0007, 4, 1);\n  enableKeyTone(true);\n  setDualWatchMode(DualWatchMode::DualDual);\n}\n\nunsigned\nRadioddityCodeplug::MenuSettingsElement::menuHangTime() const {\n  return getUInt8(0x0000);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::setMenuHangTime(unsigned sec) {\n  sec = std::min(10u, sec);\n  setUInt8(0x0000, sec);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::message() const {\n  return getBit(0x0001, 0);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableMessage(bool enable) {\n  setBit(0x0001, 0, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::scanStart() const {\n  return getBit(0x0001, 1);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableScanStart(bool enable) {\n  setBit(0x0001, 1, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::editScanList() const {\n  return getBit(0x0001, 2);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableEditScanList(bool enable) {\n  setBit(0x0001, 2, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::callAlert() const {\n  return getBit(0x0001, 3);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableCallAlert(bool enable) {\n  setBit(0x0001, 3, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::editContact() const {\n  return getBit(0x0001, 4);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableEditContact(bool enable) {\n  setBit(0x0001, 4, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::manualDial() const {\n  return getBit(0x0001, 5);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableManualDial(bool enable) {\n  setBit(0x0001, 5, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::radioCheck() const {\n  return getBit(0x0001, 6);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableRadioCheck(bool enable) {\n  setBit(0x0001, 6, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::remoteMonitor() const {\n  return getBit(0x0001, 7);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableRemoteMonitor(bool enable) {\n  setBit(0x0001, 7, enable);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::radioEnable() const {\n  return getBit(0x0002, 0);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableRadioEnable(bool enable) {\n  setBit(0x0002, 0, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::radioDisable() const {\n  return getBit(0x0002, 1);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableRadioDisable(bool enable) {\n  setBit(0x0002, 1, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::progPassword() const {\n  return getBit(0x0002, 2);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableProgPassword(bool enable) {\n  setBit(0x0002, 2, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::talkaround() const {\n  return getBit(0x0002, 3);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableTalkaround(bool enable) {\n  setBit(0x0002, 3, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::tone() const {\n  return getBit(0x0002, 4);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableTone(bool enable) {\n  setBit(0x0002, 4, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::power() const {\n  return getBit(0x0002, 5);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enablePower(bool enable) {\n  setBit(0x0002, 5, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::backlight() const {\n  return getBit(0x0002, 6);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableBacklight(bool enable) {\n  setBit(0x0002, 6, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::introScreen() const {\n  return getBit(0x0002, 7);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableIntroScreen(bool enable) {\n  setBit(0x0002, 7, enable);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::keypadLock() const {\n  return getBit(0x0003, 0);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableKeypadLock(bool enable) {\n  setBit(0x0003, 0, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::ledIndicator() const {\n  return getBit(0x0003, 1);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableLEDIndicator(bool enable) {\n  setBit(0x0003, 1, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::squelch() const {\n  return getBit(0x0003, 2);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableSquelch(bool enable) {\n  setBit(0x0003, 2, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::privacy() const {\n  return getBit(0x0003, 3);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enablePrivacy(bool enable) {\n  setBit(0x0003, 3, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::vox() const {\n  return getBit(0x0003, 4);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableVOX(bool enable) {\n  setBit(0x0003, 4, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::passwordLock() const {\n  return getBit(0x0003, 5);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enablePasswordLock(bool enable) {\n  setBit(0x0003, 5, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::missedCalls() const {\n  return getBit(0x0003, 6);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableMissedCalls(bool enable) {\n  setBit(0x0003, 6, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::answeredCalls() const {\n  return getBit(0x0003, 7);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableAnsweredCalls(bool enable) {\n  setBit(0x0003, 7, enable);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::outgoingCalls() const {\n  return getBit(0x0004, 0);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableOutgoingCalls(bool enable) {\n  setBit(0x0004, 0, enable);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::channelDisplay() const {\n  return getBit(0x0004, 1);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableChannelDisplay(bool enable) {\n  setBit(0x0004, 1, enable);\n}\nbool\nRadioddityCodeplug::MenuSettingsElement::dualWatch() const {\n  return getBit(0x0004, 2);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableDualWatch(bool enable) {\n  setBit(0x0004, 2, enable);\n}\n\nunsigned\nRadioddityCodeplug::MenuSettingsElement::keypadLockTime() const {\n  return getUInt2(0x0006, 0)*5;\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::setKeypadLockTime(unsigned sec) {\n  setUInt2(0x0006, 0, sec/5);\n}\n\nunsigned\nRadioddityCodeplug::MenuSettingsElement::backlightTime() const {\n  return getUInt2(0x0006, 2)*5;\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::setBacklightTime(unsigned sec) {\n  setUInt2(0x0006, 2, sec/5);\n}\n\nRadioddityCodeplug::MenuSettingsElement::ChannelDisplayMode\nRadioddityCodeplug::MenuSettingsElement::channelDisplayMode() const {\n  return (ChannelDisplayMode)getUInt2(0x0006, 6);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::setChannelDisplayMode(ChannelDisplayMode mode) {\n  setUInt2(0x0006, 6, (unsigned)mode);\n}\n\nbool\nRadioddityCodeplug::MenuSettingsElement::keyTone() const {\n  return getBit(0x0007, 5);\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::enableKeyTone(bool enable) {\n  setBit(0x0007, 5, enable);\n}\n\nRadioddityCodeplug::MenuSettingsElement::DualWatchMode\nRadioddityCodeplug::MenuSettingsElement::dualWatchMode() const {\n  return DualWatchMode(getUInt2(0x0007, 6));\n}\nvoid\nRadioddityCodeplug::MenuSettingsElement::setDualWatchMode(DualWatchMode mode) {\n  setUInt2(0x0007, 6, (unsigned)mode);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::BootSettingsElement\n * ********************************************************************************************* */\nRadioddityCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x20)\n{\n  // pass...\n}\n\nRadioddityCodeplug::BootSettingsElement::~BootSettingsElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::BootSettingsElement::clear() {\n  setBootDisplay(BootSettings::BootDisplay::Logo);\n  enableBootPassword(false);\n  setBCD8_be(0x0002, 0);\n  setUInt8(0x0007, 0);\n  memset(_data+0x0008, 0, 24);\n}\n\n\nBootSettings::BootDisplay\nRadioddityCodeplug::BootSettingsElement::bootDisplay() const {\n  return (1 == getUInt8(Offset::bootText()))\n    ? BootSettings::BootDisplay::Text\n    : BootSettings::BootDisplay::Logo;\n}\n\nvoid\nRadioddityCodeplug::BootSettingsElement::setBootDisplay(BootSettings::BootDisplay mode) {\n  switch (mode) {\n    case BootSettings::BootDisplay::Text: setUInt8(Offset::bootText(), 1); break;\n    default: setUInt8(Offset::bootText(), 0); break;\n  }\n}\n\nbool\nRadioddityCodeplug::BootSettingsElement::bootPasswordEnabled() const {\n  return (1 == getUInt8(Offset::bootPasswordEnable()));\n}\nvoid\nRadioddityCodeplug::BootSettingsElement::enableBootPassword(bool enable) {\n  setUInt8(Offset::bootPasswordEnable(), (enable ? 1 : 0));\n}\n\n\nQString\nRadioddityCodeplug::BootSettingsElement::bootPassword() const {\n  return QString::number(getBCD8_be(Offset::bootPassword()));\n}\n\nbool\nRadioddityCodeplug::BootSettingsElement::setBootPassword(const QString &passwd) {\n  if (passwd.isEmpty()) {\n    enableBootPassword(false);\n    return true;\n  }\n  static QRegularExpression valid(R\"(^[0-9]{0,8}$)\");\n  if (! valid.match(passwd).hasMatch()) {\n    return false;\n  }\n  setBCD8_be(Offset::bootPassword(), passwd.toUInt());\n  return true;\n}\n\n\nbool\nRadioddityCodeplug::BootSettingsElement::encode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  setBootDisplay(ctx.config()->settings()->boot()->bootDisplay());\n  enableBootPassword(ctx.config()->settings()->boot()->bootPasswordEnabled());\n  setBootPassword(ctx.config()->settings()->boot()->bootPassword());\n  return true;\n}\n\nbool\nRadioddityCodeplug::BootSettingsElement::decode(Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err);\n  ctx.config()->settings()->boot()->setBootDisplay(bootDisplay());\n  ctx.config()->settings()->boot()->enableBootPassword(bootPasswordEnabled() && (!bootPassword().isEmpty()));\n  ctx.config()->settings()->boot()->setBootPassword(bootPassword());\n  return true;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::BootTextElement\n * ********************************************************************************************* */\nRadioddityCodeplug::BootTextElement::BootTextElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::BootTextElement::BootTextElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::BootTextElement::~BootTextElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::BootTextElement::clear() {\n  setLine1(\"\");\n  setLine2(\"\");\n}\n\nQString\nRadioddityCodeplug::BootTextElement::line1() const {\n  return readASCII(Offset::line1(), Limit::lineLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::BootTextElement::setLine1(const QString &text) {\n  writeASCII(Offset::line1(), text, Limit::lineLength(), 0xff);\n}\n\nQString\nRadioddityCodeplug::BootTextElement::line2() const {\n  return readASCII(Offset::line2(), Limit::lineLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::BootTextElement::setLine2(const QString &text) {\n  writeASCII(Offset::line2(), text, Limit::lineLength(), 0xff);\n}\n\nbool\nRadioddityCodeplug::BootTextElement::fromConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  setLine1(ctx.config()->settings()->boot()->message1());\n  setLine2(ctx.config()->settings()->boot()->message2());\n  return true;\n}\n\nbool\nRadioddityCodeplug::BootTextElement::updateConfig(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  ctx.config()->settings()->boot()->setMessage1(line1());\n  ctx.config()->settings()->boot()->setMessage2(line2());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::MessageBankElement\n * ********************************************************************************************* */\nRadioddityCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr)\n  : Element(ptr, 0x1248)\n{\n  // pass...\n}\n\nRadioddityCodeplug::MessageBankElement::~MessageBankElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::MessageBankElement::clear() {\n  setUInt8(Offset::messageConut(), 0); // set count to 0\n  memset(_data+0x0001, 0x00, 7); // Fill unused\n  memset(_data+Offset::messageLengths(), 0x00, Limit::messages()); // Set message lengths to 0\n  memset(_data+0x0028, 0x00, 32); // Fill unused\n  memset(_data+Offset::messages(), 0xff, Limit::messages()*Limit::messageLength()); // Clear all messages\n}\n\nunsigned\nRadioddityCodeplug::MessageBankElement::numMessages() const {\n  return getUInt8(Offset::messageConut());\n}\nQString\nRadioddityCodeplug::MessageBankElement::message(unsigned n) const {\n  if (n >= numMessages())\n    return QString();\n  return readASCII(Offset::messages()+n*Offset::betweenMessages(), Limit::messageLength(), 0xff);\n}\nvoid\nRadioddityCodeplug::MessageBankElement::appendMessage(const QString msg) {\n  unsigned idx = numMessages();\n  if (idx >= Limit::messages())\n    return;\n  unsigned int len = std::min((unsigned int)msg.size(), Limit::messageLength());\n  // increment counter\n  setUInt8(Offset::messageConut(), idx+1);\n  // store length\n  setUInt8(Offset::messageLengths()+idx, len);\n  // store string\n  writeASCII(Offset::messages()+Offset::betweenMessages()*idx, msg, Limit::messageLength(), 0xff);\n}\n\nbool\nRadioddityCodeplug::MessageBankElement::encode(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  clear();\n  unsigned int count = std::min(\n        Limit::messages(), (unsigned int)ctx.config()->smsExtension()->smsTemplates()->count());\n  for (unsigned int i=0; i<count; i++)\n    appendMessage(ctx.config()->smsExtension()->smsTemplates()->message(i)->message());\n  return true;\n}\n\nbool\nRadioddityCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  for (unsigned int i=0; i<numMessages(); i++) {\n    SMSTemplate *sms = new SMSTemplate();\n    sms->setName(QString(\"Message %1\").arg(i+1));\n    sms->setMessage(message(i));\n    ctx.config()->smsExtension()->smsTemplates()->add(sms);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug::EncryptionElement\n * ********************************************************************************************* */\nRadioddityCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nRadioddityCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRadioddityCodeplug::EncryptionElement::~EncryptionElement() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::EncryptionElement::clear() {\n  setPrivacyType(PrivacyType::None);\n  for (unsigned int i=0; i<Limit::keyCount(); i++) {\n    clearBasicKey(i);\n  }\n}\n\nRadioddityCodeplug::EncryptionElement::PrivacyType\nRadioddityCodeplug::EncryptionElement::privacyType() const {\n  return PrivacyType(getUInt8(Offset::privacyType()));\n}\nvoid\nRadioddityCodeplug::EncryptionElement::setPrivacyType(PrivacyType type) {\n  setUInt8(Offset::privacyType(), (uint8_t)type);\n}\n\nbool\nRadioddityCodeplug::EncryptionElement::isBasicKeySet(unsigned n) const {\n  if (n >= Limit::keyCount())\n    return false;\n  unsigned byte=n/8, bit =n%8;\n  return getBit(Offset::bitmap()+byte, bit);\n}\nQByteArray\nRadioddityCodeplug::EncryptionElement::basicKey(unsigned n) const {\n  if (n >= Limit::keyCount())\n    return QByteArray();\n  return QByteArray((const char *)_data+Offset::keys()+Offset::key()*n, Limit::keySize());\n}\nvoid\nRadioddityCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) {\n  if ((n >= Limit::keyCount()) || (Limit::keySize() != key.size()))\n    return;\n  unsigned byte=n/8, bit =n%8;\n  // Store key (twice?)\n  memcpy(_data+Offset::keys() + Offset::key()*n, key.data(), Limit::keySize());\n  memcpy(_data+Offset::keys() + Offset::key()*n + Limit::keySize(), key.data(), Limit::keySize());\n  // Update bitmap\n  setBit(Offset::bitmap()+byte, bit);\n  setPrivacyType(PrivacyType::Basic);\n}\nvoid\nRadioddityCodeplug::EncryptionElement::clearBasicKey(unsigned n) {\n  if (n >= Limit::keyCount())\n    return;\n  unsigned byte=n/8, bit =n%8;\n  memset(_data+Offset::keys() + Offset::key()*n, 0xff, Limit::keySize());\n  memset(_data+Offset::keys() + Offset::key()*n + Limit::keySize(), 0xff, Limit::keySize());\n  clearBit(Offset::bitmap()+byte, bit);\n}\n\nbool\nRadioddityCodeplug::EncryptionElement::fromCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err) {\n  clear();\n\n  if ((unsigned int)ext->encryptionKeys()->count() > Limit::keyCount()) {\n    errMsg(err) << \"Cannot encode encryption extension. Can only encode \"\n                << Limit::keyCount() << \" keys.\";\n    return false;\n  }\n\n  for (int i=0; i<ext->encryptionKeys()->count(); i++) {\n    if (! ext->encryptionKeys()->get(i)->is<BasicEncryptionKey>()) {\n      errMsg(err) << \"Can only encode basic encryption keys.\";\n      return false;\n    }\n    BasicEncryptionKey *key = ext->encryptionKeys()->get(i)->as<BasicEncryptionKey>();\n    if (key->key().size() != Limit::keySize()) {\n      errMsg(err) << \"Can only encode \" << 8*Limit::keySize() << \"bit basic encryption keys.\";\n      return false;\n    }\n    setBasicKey(i, key->key());\n    ctx.add(key, i+1);\n  }\n\n  return true;\n}\n\nbool RadioddityCodeplug::EncryptionElement::updateCommercialExt(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  if (PrivacyType::None == privacyType())\n    return false;\n\n  CommercialExtension *ext = ctx.config()->commercialExtension();\n  for (unsigned int i=0; i<Limit::keyCount(); i++) {\n    if (! isBasicKeySet(i))\n      continue;\n    // Assemble key\n    BasicEncryptionKey *key = new BasicEncryptionKey();\n    key->setName(QString(\"Basic Key %1\").arg(i+1));\n    key->fromHex(basicKey(i).toHex());\n    // add key to extension\n    ext->encryptionKeys()->add(key);\n    // register key index\n    ctx.add(key, i+1);\n  }\n\n  return ext;\n}\n\nbool\nRadioddityCodeplug::EncryptionElement::linkCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ext); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Keys do not need any linking step\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityCodeplug\n * ********************************************************************************************* */\nRadioddityCodeplug::RadioddityCodeplug(QObject *parent)\n  : Codeplug(parent)\n{\n  // pass...\n}\n\nRadioddityCodeplug::~RadioddityCodeplug() {\n  // pass...\n}\n\nvoid\nRadioddityCodeplug::clear() {\n  // Clear general config\n  clearGeneralSettings();\n  // Clear button settings\n  clearButtonSettings();\n  // Clear messages\n  clearMessages();\n  // Clear contacts\n  clearContacts();\n  // clear DTMF contacts\n  clearDTMFContacts();\n  // clear boot settings\n  clearBootSettings();\n  // clear menu settings\n  clearMenuSettings();\n  // clear boot text\n  clearBootSettings();\n  // clear VFO settings\n  clearVFOSettings();\n  // clear zones\n  clearZones();\n  // clear scan lists\n  clearScanLists();\n  // clear group lists\n  clearGroupLists();\n  // clear encryption keys\n  clearEncryption();\n}\n\nbool\nRadioddityCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n  // All indices as 1-based. That is, the first channel gets index 1.\n\n  // Map radio IDs\n  for (int i=0; i<config->radioIDs()->count(); i++) {\n    if (config->radioIDs()->get(i)->is<DMRRadioID>())\n      ctx.add(config->radioIDs()->get(i)->as<DMRRadioID>(), i+1);\n  }\n\n  // Map digital and DTMF contacts\n  for (int i=0, d=0, a=0; i<config->contacts()->count(); i++) {\n    Contact *contact = config->contacts()->contact(i);\n    if (contact->is<DMRContact>()) {\n      ctx.add(contact->as<DMRContact>(), d+1); d++;\n    } else if (contact->is<DTMFContact>()) {\n      ctx.add(contact->as<DTMFContact>(), a+1); a++;\n    } else {\n      logInfo() << \"Cannot index contact '\" << contact->name()\n                << \"'. Contact type '\" << contact->metaObject()->className()\n                << \"' not supported by or implemented for Radioddity devices.\";\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i+1);\n\n  // Map channels\n  for (int i=0, c=1; i<config->channelList()->count(); i++) {\n    Channel *channel = config->channelList()->channel(i);\n    if (channel->is<FMChannel>() || channel->is<DMRChannel>()) {\n      ctx.add(channel, c); c++;\n    } else {\n      logInfo() << \"Cannot index channel '\" << channel->name()\n                << \"'. Channel type '\" << channel->metaObject()->className()\n                << \"' not supported by or implemented for Radioddity devices.\";\n    }\n  }\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i+1);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i+1);\n\n  // Map DMR APRS systems\n  for (int i=0,a=0,d=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<DMRAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<DMRAPRSSystem>(), d+1); d++;\n    } else if (config->posSystems()->system(i)->is<FMAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<FMAPRSSystem>(), a+1); a++;\n    }\n  }\n\n  // Map roaming\n  for (int i=0; i<config->roamingZones()->count(); i++)\n    ctx.add(config->roamingZones()->zone(i), i+1);\n\n  return true;\n}\n\nConfig *\nRadioddityCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot pre-process Radioddity codeplug.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(intermediate, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(intermediate, err)) {\n    errMsg(err) << \"Cannot split zone for Radioddity codeplug.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nRadioddityCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  // Check if default DMR id is set.\n  if (config->settings()->defaultIdRef()->isNull()) {\n    errMsg(err) << \"No default radio ID specified.\";\n    return false;\n  }\n\n  // Create index<->object table.\n  Context ctx(config);\n  if (! index(config, ctx, err)) {\n    errMsg(err) << \"Cannot index configuration objects.\";\n    return false;\n  }\n\n  return this->encodeElements(flags, ctx);\n}\n\nbool\nRadioddityCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  // General config\n  if (! this->encodeGeneralSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode general settings.\";\n    return false;\n  }\n\n  if (! this->encodeButtonSettings(ctx, flags, err)) {\n    errMsg(err) << \"Cannot encode button settings.\";\n    return false;\n  }\n\n  if (! this->encodeMessages(ctx, flags, err)) {\n    errMsg(err) << \"Cannot encode preset messages.\";\n    return false;\n  }\n\n  // Define Contacts\n  if (! this->encodeContacts(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  if (! this->encodeDTMFContacts(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode DTMF contacts.\";\n    return false;\n  }\n\n  if (! this->encodeChannels(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode channels\";\n    return false;\n  }\n\n  if (! this->encodeBootSettings(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode boot text.\";\n    return false;\n  }\n\n  if (! this->encodeZones(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  if (! this->encodeScanLists(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode scan lists.\";\n    return false;\n  }\n\n  if (! this->encodeGroupLists(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n\n  if (! this->encodeEncryption(flags, ctx, err)) {\n    errMsg(err) << \"Cannot encode encryption keys.\";\n    return true;\n  }\n\n  return true;\n}\n\n\nbool\nRadioddityCodeplug::decode(Config *config, const ErrorStack &err) {\n  // Clear config object\n  config->clear();\n\n  // Create index<->object table.\n  Context ctx(config);\n\n  return this->decodeElements(ctx, err);\n}\n\nbool\nRadioddityCodeplug::postprocess(Config *config, const ErrorStack &err) const {\n  if (! Codeplug::postprocess(config, err)) {\n    errMsg(err) << \"Cannot post-process Radioddy codeplug.\";\n    return false;\n  }\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(config, err)) {\n    errMsg(err) << \"Cannot merg zones in decoded Radioddity codeplug.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nRadioddityCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! this->decodeGeneralSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings.\";\n    return false;\n  }\n\n  if (! this->decodeButtonSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode button settings.\";\n    return false;\n  }\n\n  if (! this->decodeMessages(ctx, err)) {\n    errMsg(err) << \"Cannot decode preset messages.\";\n    return false;\n  }\n\n  if (! this->createContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create contacts.\";\n    return false;\n  }\n\n  if (! this->createDTMFContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create DTMF contacts\";\n    return false;\n  }\n\n  if (! this->createChannels(ctx, err)) {\n    errMsg(err) << \"Cannot create channels.\";\n    return false;\n  }\n\n  if (! this->decodeBootSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode boot text.\";\n    return false;\n  }\n\n  if (! this->createEncryption(ctx, err)) {\n    errMsg(err) << \"Cannot decode encryption keys.\";\n    return false;\n  }\n\n  if (! this->createZones(ctx, err)) {\n    errMsg(err) << \"Cannot create zones.\";\n    return false;\n  }\n\n  if (! this->createScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot create scan lists.\";\n    return false;\n  }\n\n  if (! this->createGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot create group lists.\";\n    return false;\n  }\n\n  if (! this->linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  if (! this->linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  if (! this->linkScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot link scan lists.\";\n    return false;\n  }\n\n  if (! this->linkGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n\n  if (! this->linkEncryption(ctx, err)) {\n    errMsg(err) << \"Cannot link encryption keys.\";\n    return false;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/radioddity_codeplug.hh",
    "content": "#ifndef RADIODDITYCODEPLUG_HH\n#define RADIODDITYCODEPLUG_HH\n\n#include \"codeplug.hh\"\n#include \"signaling.hh\"\n#include \"channel.hh\"\n#include \"contact.hh\"\n#include \"radioddity_extensions.hh\"\n#include \"ranges.hh\"\n#include \"bootsettings.hh\"\n\n\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\n\n/** Base class of all Radioddity codeplugs. This class implements the majority of all codeplug\n * elements present in all Radioddity codeplugs (also some derivatives like OpenGD77). This eases\n * the support of several Radioddity radios, as only the differences in the codeplug to this base\n * class must be implemented.\n *\n * @ingroup radioddity */\nclass RadioddityCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements the base for all Radioddity channel encodings.\n   *\n   * Memory layout of encoded channel:\n   * @verbinclude radioddity_channel.txt */\n  class ChannelElement: public Codeplug::Element\n  {\n  public:\n    /** Possible channel types. */\n    enum Mode {\n      MODE_ANALOG = 0,   ///< Analog channel, aka FM.\n      MODE_DIGITAL = 1   ///< Digital channel, aka DMR.\n    };\n\n    /** Possible admit criteria. */\n    enum Admit {\n      ADMIT_ALWAYS = 0,  ///< Allow always.\n      ADMIT_CH_FREE = 1, ///< Allow TX on channel free.\n      ADMIT_COLOR = 2    ///< Allow TX on matching color-code.\n    };\n\n    /** Possible privacy groups, not used in ham radio. */\n    enum PrivacyGroup {\n      PRIVGR_NONE = 0,     ///< No privacy group, default.\n      PRIVGR_53474C39 = 1  ///< Privacy group 53474C39 (wtf?).\n    };\n\n  protected:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructs a channel from the given memory. */\n    explicit ChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelElement();\n\n    /** The size of the channel. */\n    static constexpr unsigned int size() { return 0x0038; }\n\n    /** Resets the channel. */\n    virtual void clear();\n\n    /** Returns the name of the channel. */\n    virtual QString name() const;\n    /** Sets the name of the channel. */\n    virtual void setName(const QString &n);\n\n    /** Returns the RX frequency of the channel. */\n    virtual uint32_t rxFrequency() const;\n    /** Sets the RX frequency of the channel. */\n    virtual void setRXFrequency(uint32_t freq);\n    /** Returns the TX frequency of the channel. */\n    virtual uint32_t txFrequency() const;\n    /** Sets the TX frequency of the channel. */\n    virtual void setTXFrequency(uint32_t freq);\n\n    /** Returns the channel mode. */\n    virtual Mode mode() const;\n    /** Sets the channel mode. */\n    virtual void setMode(Mode mode);\n\n    /** Returns the TX timeout in seconds. A value of 0 means disabled. */\n    virtual Interval txTimeOut() const;\n    /** Sets the TX timeout in seconds. Setting it to 0 disables the timeout. */\n    virtual void setTXTimeOut(const Interval &tot);\n    /** Returns the transmit time-out re-key delay in seconds. */\n    virtual unsigned txTimeOutRekeyDelay() const;\n    /** Sets the transmit time-out re-key delay in seconds. */\n    virtual void setTXTimeOutRekeyDelay(unsigned delay);\n\n    /** Returns the admit criterion. */\n    virtual Admit admitCriterion() const;\n    /** Sets the admit criterion. */\n    virtual void setAdmitCriterion(Admit admit);\n\n    /** Returns @c true if a scan list is set. */\n    virtual bool hasScanList() const;\n    /** Returns the scan list index (+1). */\n    virtual unsigned scanListIndex() const;\n    /** Sets the scan list index (+1). */\n    virtual void setScanListIndex(unsigned index);\n\n    /** Returns the RX subtone. */\n    virtual SelectiveCall rxTone() const;\n    /** Sets the RX subtone. */\n    virtual void setRXTone(const SelectiveCall &code);\n    /** Returns the TX subtone. */\n    virtual SelectiveCall txTone() const;\n    /** Sets the TX subtone. */\n    virtual void setTXTone(const SelectiveCall &code);\n\n    /** Returns TX signaling index (+1). */\n    virtual unsigned txSignalingIndex() const;\n    /** Sets TX signaling index (+1). */\n    virtual void setTXSignalingIndex(unsigned index);\n    /** Returns RX signaling index (+1). */\n    virtual unsigned rxSignalingIndex() const;\n    /** Sets RX signaling index (+1). */\n    virtual void setRXSignalingIndex(unsigned index);\n\n    /** Returns the privacy group. */\n    virtual PrivacyGroup privacyGroup() const;\n    /** Sets the privacy group. */\n    virtual void setPrivacyGroup(PrivacyGroup grp);\n\n    /** Returns the TX color code. */\n    virtual unsigned txColorCode() const;\n    /** Sets the TX color code. */\n    virtual void setTXColorCode(unsigned cc);\n\n    /** Returns @c true if a group list is set. */\n    virtual bool hasGroupList() const;\n    /** Returns the group-list index (+1). */\n    virtual unsigned groupListIndex() const;\n    /** Sets the group-list index (+1). */\n    virtual void setGroupListIndex(unsigned index);\n\n    /** Returns the RX color code. */\n    virtual unsigned rxColorCode() const;\n    /** Sets the RX color code. */\n    virtual void setRXColorCode(unsigned cc);\n\n    /** Returns @c true if an emergency system is set. */\n    virtual bool hasEmergencySystem() const;\n    /** Returns the emergency system index (+1). */\n    virtual unsigned emergencySystemIndex() const;\n    /** Sets the emergency system index (+1). */\n    virtual void setEmergencySystemIndex(unsigned index);\n\n    /** Returns @c true if a TX contact is set. */\n    virtual bool hasContact() const;\n    /** Returns the transmit contact index (+1). */\n    virtual unsigned contactIndex() const;\n    /** Sets the transmit contact index (+1). */\n    virtual void setContactIndex(unsigned index);\n\n    /** Returns @c true if data-call-confirm is enabled. */\n    virtual bool dataCallConfirm() const;\n    /** Enables/disables data-call-confirm. */\n    virtual void enableDataCallConfirm(bool enable);\n    /** Returns @c true if emergency alarm ACK is enabled. */\n    virtual bool emergencyAlarmACK() const;\n    /** Enables/disables emergency alarm ACK. */\n    virtual void enableEmergencyAlarmACK(bool enable);\n    /** Returns @c true if private-call-confirm is enabled. */\n    virtual bool privateCallConfirm() const;\n    /** Enables/disables private-call-confirm. */\n    virtual void enablePrivateCallConfirm(bool enable);\n    /** Returns @c true if privacy is enabled. */\n    virtual bool privacyEnabled() const;\n    /** Enables/disables privacy. */\n    virtual void enablePrivacy(bool enable);\n\n    /** Returns the time slot of the channel. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot of the channel. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns @c true if the dual-capacity direct mode is enabled. */\n    virtual bool dualCapacityDirectMode() const;\n    /** Enables/disables the dual-capacity direct mode. */\n    virtual void enableDualCapacityDirectMode(bool enable);\n    /** Returns @c true if non-STE is frequency (?!). */\n    virtual bool nonSTEFrequency() const;\n    /** Enables/disables non-STE is frequency (?!). */\n    virtual void enableNonSTEFrequency(bool enable);\n\n    /** Returns the bandwidth. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the bandwidth. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns @c true if RX only is enabled. */\n    virtual bool rxOnly() const;\n    /** Enables/disables RX only. */\n    virtual void enableRXOnly(bool enable);\n    /** Returns @c true if talkaround is enabled. */\n    virtual bool talkaround() const;\n    /** Enables/disables talkaround. */\n    virtual void enableTalkaround(bool enable);\n    /** Returns @c true if VOX is enabled. */\n    virtual bool vox() const;\n    /** Enables/disables VOX. */\n    virtual void enableVOX(bool enable);\n\n    /** Returns the power setting of the channel. */\n    virtual Channel::Power power() const;\n    /** Sets the power setting of the channel. */\n    virtual void setPower(Channel::Power pwr);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      /** The maximum length of the name. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the channel element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int rxFrequency() { return 0x0010; }\n      static constexpr unsigned int txFrequency() { return 0x0014; }\n      static constexpr unsigned int mode() { return 0x0018; }\n      static constexpr unsigned int txTimeout() { return 0x001b; }\n      static constexpr unsigned int txTimeoutRekeyDelay() { return 0x001c; }\n      static constexpr unsigned int admitCriterion() { return 0x001d; }\n      static constexpr unsigned int scanList() { return 0x001f; }\n      static constexpr unsigned int rxTone() { return 0x0020; }\n      static constexpr unsigned int txTone() { return 0x0022; }\n      static constexpr unsigned int txSignaling() { return 0x0025; }\n      static constexpr unsigned int rxSignaling() { return 0x0027; }\n      static constexpr unsigned int privacyGroup() { return 0x0029; }\n      static constexpr unsigned int txColorCode() { return 0x002a; }\n      static constexpr unsigned int groupList() { return 0x002b; }\n      static constexpr unsigned int rxColorCode() { return 0x002c; }\n      static constexpr unsigned int emergencySystem() { return 0x002d; }\n      static constexpr unsigned int transmitContact() { return 0x002e; }\n      static constexpr Bit dataCallConfirm() { return {0x0030, 7}; }\n      static constexpr Bit emergencyAlarmACK() { return {0x0030, 6}; }\n      static constexpr Bit privateCallConfirm() { return {0x0031, 0}; }\n      static constexpr Bit privacyEnabled() { return {0x0031, 4}; }\n      static constexpr Bit timeSlot() { return {0x0031, 6}; }\n      static constexpr Bit dualCapacityDirectMode() { return {0x0032, 0}; }\n      static constexpr Bit nonSTEFrequency() { return {0x0032, 5}; }\n      static constexpr Bit bandwidth() { return {0x0033, 1}; }\n      static constexpr Bit rxOnly() { return {0x0033, 2}; }\n      static constexpr Bit talkaround() { return {0x0033, 3}; }\n      static constexpr Bit vox() { return {0x0033, 6}; }\n      static constexpr Bit power() { return {0x0033, 7}; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the base for channel banks in Radioddity codeplugs.\n   *\n   * Memory layout of a channel bank:\n   * @verbinclude radioddity_channelbank.txt */\n  class ChannelBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelBankElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelBankElement();\n\n    /** The size of the channel bank. */\n    static constexpr unsigned int size() { return 0x1c10; }\n\n    /** Clears the bank. */\n    void clear();\n\n    /** Returns @c true if the channel is enabled. */\n    virtual bool isEnabled(unsigned idx) const ;\n    /** Enable/disable a channel in the bank. */\n    virtual void enable(unsigned idx, bool enabled);\n    /** Returns a pointer to the channel at the given index. */\n    virtual uint8_t *get(unsigned idx) const;\n    /** Returns the n-th channel. */\n    ChannelElement channel(unsigned int n);\n\n  public:\n    /** Some limits for the channel bank. */\n    struct Limit {\n      /** The maximum number of channels. */\n      static constexpr unsigned int channelCount() { return 128; }\n    };\n\n  protected:\n    /** Some internal offset within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bitmask() { return 0x0000; }\n      static constexpr unsigned int channels() { return 0x0010; }\n      /// @endcond\n    };\n  };\n\n  /** VFO Channel representation within the binary codeplug.\n   *\n   * Each channel requires 0x38b:\n   * @verbinclude radioddity_vfochannel.txt */\n  class VFOChannelElement: public ChannelElement\n  {\n  public:\n    /** Possible offset frequency modes. */\n    enum class OffsetMode {\n      Off = 0,       ///< Disables transmit frequency offset.\n      Positive = 1,  ///< Transmit offset frequency is positive (TX above RX).\n      Negative = 2   ///< Transmit offset frequency is negative (TX below RX).\n    };\n\n    /** Possible tuning step sizes. */\n    enum class StepSize {\n      SS2_5kHz = 0,  ///< 2.5kHz\n      SS5kHz = 1,    ///< 5kHz\n      SS6_25kHz = 2, ///< 6.25kHz\n      SS10kHz = 3,   ///< 10kHz\n      SS12_5kHz = 4, ///< 12.5kHz\n      SS20kHz = 5,   ///< 20kHz\n      SS30kHz = 6,   ///< 30kHz\n      SS50kHz = 7    ///< 50kHz\n    };\n\n  protected:\n    /** Hidden constructor. */\n    VFOChannelElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit VFOChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** The VFO channel has no name. */\n    QString name() const;\n    /** The VFO channel has no name. */\n    void setName(const QString &name);\n\n    /** Returns the tuning step-size in kHz. */\n    virtual double stepSize() const;\n    /** Sets the tuning step-size in kHz. */\n    virtual void setStepSize(double kHz);\n\n    /** Returns the transmit frequency offset mode. */\n    virtual OffsetMode offsetMode() const;\n    /** Returns the transmit frequency offset. */\n    virtual double txOffset() const;\n    /** Sets the transmit frequency offset in MHz. */\n    virtual void setTXOffset(double f);\n    /** Sets the transmit frequency offset mode. */\n    virtual void setOffsetMode(OffsetMode mode);\n\n  protected:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public ChannelElement::Offset {\n      static constexpr Bit stepSize()                         { return {0x0036, 4} ; }\n      static constexpr Bit offsetMode()                       { return {0x0036, 2} ; }\n      static constexpr unsigned int txOffset()                { return 0x0034; }\n    };\n    /// @endcond\n  };\n\n\n  /** Implements the base for digital contacts in Radioddity codeplugs.\n   *\n   * Memory layout of a digital contact:\n   * @verbinclude radioddity_contact.txt */\n  class ContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ContactElement();\n\n    /** The size of the contact element. */\n    static constexpr unsigned int size() { return 0x0018; }\n\n    /** Resets the contact. */\n    void clear();\n    /** Returns @c true if the contact is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString name);\n\n    /** Returns the DMR number of the contact. */\n    virtual unsigned number() const;\n    /** Sets the DMR number of the contact. */\n    virtual void setNumber(unsigned id);\n\n    /** Returns the call type. */\n    virtual DMRContact::Type type() const;\n    /** Sets the call type. */\n    virtual void setType(DMRContact::Type type);\n\n    /** Returns @c true if the ring tone is enabled for this contact. */\n    virtual bool ring() const;\n    /** Enables/disables ring tone for this contact. */\n    virtual void enableRing(bool enable);\n\n    /** Returns the ring tone style for this contact [0-10]. */\n    virtual unsigned ringStyle() const;\n    /** Sets the ring tone style for this contact [0-10]. */\n    virtual void setRingStyle(unsigned style);\n\n    /** Constructs a @c DigitalContact instance from this codeplug contact. */\n    virtual DMRContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug contact from the given @c DigitalContact. */\n    virtual bool fromContactObj(const DMRContact *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the contact. */\n    struct Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** Number of possible ring-styles [0,10]. */\n      static constexpr unsigned int ringStyle() { return 10; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int number() { return 0x0010; }\n      static constexpr unsigned int type() { return 0x0014; }\n      static constexpr unsigned int ring() { return 0x0015; }\n      static constexpr unsigned int ringStyle() { return 0x0016; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements a base DTMF (analog) contact for Radioddity codeplugs.\n   *\n   * Memory layout of the DTMF contact:\n   * @verbinclude radioddity_dtmfcontact.txt\n   */\n  class DTMFContactElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    DTMFContactElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit DTMFContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~DTMFContactElement();\n\n    /** The size of the contact element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the contact. */\n    void clear();\n    /** Returns @c true if the contact is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &name);\n\n    /** Returns the number of the contact. */\n    virtual QString number() const;\n    /** Sets the number of the contact. */\n    virtual void setNumber(const QString &number);\n\n    /** Constructs a @c DTMFContact instance from this codeplug contact. */\n    virtual DTMFContact *toContactObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug contact from the given @c DTMFContact. */\n    virtual bool fromContactObj(const DTMFContact *obj, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      /** The maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** The maximum number length. */\n      static constexpr unsigned int numberLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name() { return 0x0000; }\n      static constexpr unsigned int number() { return 0x0010; }\n      /// @endcond\n    };\n  };\n\n  /** Represents a zone within Radioddity codeplugs.\n   *\n   * Memory layout of the zone:\n   * @verbinclude radioddity_zone.txt\n   */\n  class ZoneElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ZoneElement(uint8_t *ptr);\n    virtual ~ZoneElement();\n\n    /** The size of the zone element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the zone. */\n    void clear();\n    /** Returns @c true if the zone is valid. */\n    bool isValid() const;\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if a member is stored at the given index.\n     * That is, if the index is not 0. */\n    virtual bool hasMember(unsigned n) const;\n    /** Returns the n-th member index (+1). */\n    virtual unsigned member(unsigned n) const;\n    /** Sets the n-th member index (+1). */\n    virtual void setMember(unsigned n, unsigned idx);\n    /** Clears the n-th member index. */\n    virtual void clearMember(unsigned n);\n\n    /** Constructs a generic @c Zone object from this codeplug zone. */\n    virtual Zone *toZoneObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed @c Zone object to the rest of the configuration. That is\n     * linking to the referred channels. */\n    virtual bool linkZoneObj(Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Resets this codeplug zone representation from the given generic @c Zone object. */\n    virtual bool fromZoneObjA(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Resets this codeplug zone representation from the given generic @c Zone object. */\n    virtual bool fromZoneObjB(const Zone *zone, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for zone elements. */\n    struct Limit {\n      /** The maximum length of the zone name. */\n      static constexpr unsigned int nameLength() { return 16; }\n      /** The maximum number of members. */\n      static constexpr unsigned int memberCount() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()  { return 0x0000; }\n      static constexpr unsigned int channels() { return 0x0010; }\n      static constexpr unsigned int betweenChannels() { return 0x0002; }\n      /// @endcond\n    };\n\n  };\n\n  /** Implements the base class for all zone banks of Radioddity codeplugs.\n   *\n   * Memory layout of the zone table/bank:\n   * @verbinclude radioddity_zonebank.txt\n   */\n  class ZoneBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ZoneBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ZoneBankElement(uint8_t *ptr);\n    /** Destructor. */\n    ~ZoneBankElement();\n\n    /** The size of the zone element. */\n    static constexpr unsigned int size() { return 0x2f00; }\n\n    /** Resets the bank. */\n    void clear();\n\n    /** Returns @c true if the channel is enabled. */\n    virtual bool isEnabled(unsigned idx) const ;\n    /** Enable/disable a channel in the bank. */\n    virtual void enable(unsigned idx, bool enabled);\n\n    /** Returns a pointer to the channel at the given index. */\n    virtual uint8_t *get(unsigned idx) const;\n    /** Returns the n-th zone. */\n    ZoneElement zone(unsigned int n);\n\n  public:\n    /** Some limits for the zone bank. */\n    struct Limit {\n      /** The maximum number of zones in this bank. */\n      static constexpr unsigned int zoneCount() { return 250; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bitmap() { return 0x0000; }\n      static constexpr unsigned int zones()  { return 0x0020; }\n      /// @endcond\n    };\n  };\n\n  /** Represents a base class for all group lists within Radioddity codeplugs.\n   *\n   * Memory layout of the RX group list:\n   * @verbinclude radioddity_grouplist.txt\n   */\n  class GroupListElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit GroupListElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GroupListElement();\n\n    /** Size of the group list element. */\n    static constexpr unsigned int size() { return 0x0030; }\n\n    /** Resets the group list. */\n    void clear();\n\n    /** Returns the name of the group list. */\n    virtual QString name() const;\n    /** Sets the name of the group list. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if the group list has an n-th member.\n     * That is if the n-th index is not 0. */\n    virtual bool hasMember(unsigned n) const;\n    /** Returns the n-th member index (+1). */\n    virtual unsigned member(unsigned n) const;\n    /** Sets the n-th member index (+1). */\n    virtual void setMember(unsigned n, unsigned idx);\n    /** Clears the n-th member index. */\n    virtual void clearMember(unsigned n);\n\n    /** Constructs a @c RXGroupList object from the codeplug representation. */\n    virtual RXGroupList *toRXGroupListObj(Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Links a previously constructed @c RXGroupList to the rest of the generic configuration. */\n    virtual bool linkRXGroupListObj(unsigned int ncnt, RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack()) const;\n    /** Reset this codeplug representation from a @c RXGroupList object. */\n    virtual bool fromRXGroupListObj(const RXGroupList *lst, Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for group lists. */\n    struct Limit {\n      static constexpr unsigned int nameLength()  { return 16; }   ///< Maximum name length.\n      static constexpr unsigned int memberCount() { return 16; }  ///< Maximum number of entries.\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int name()           { return 0x0000; }\n      static constexpr unsigned int members()        { return 0x0010; }\n      static constexpr unsigned int betweenMembers() { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n  /** Implements a base class of group list memory banks for all Radioddity codeplugs.\n   *\n   * Memory layout of the group list table:\n   * @verbinclude radioddity_grouplistbank.txt\n   */\n  class GroupListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GroupListBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit GroupListBankElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GroupListBankElement();\n\n    /** The size of the group list bank element. */\n    static constexpr unsigned int size() { return 0x0c80; }\n\n    /** Resets the bank. */\n    void clear();\n\n    /** Returns @c true if the n-th group list is enabled. */\n    virtual bool isEnabled(unsigned n) const;\n    /** Returns the number of contacts in the n-th group list. */\n    virtual unsigned contactCount(unsigned n) const;\n    /** Sets the number of contacts in the n-th group list.\n     * This also enables the n-th group list. */\n    virtual void setContactCount(unsigned n, unsigned size);\n    /** Disables the n-th group list. */\n    virtual void disable(unsigned n);\n\n    /** Returns a pointer to the n-th group list. */\n    virtual uint8_t *get(unsigned n) const;\n\n  public:\n    /** Some limits for the group list bank. */\n    struct Limit {\n      static constexpr unsigned int groupListCount() { return 64; }   ///< Maximum number of group lists.\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int contactCounts() { return 0x0000; }\n      static constexpr unsigned int groupLists() { return 0x0080; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the base class for scan lists of all Radioddity codeplugs.\n   *\n   * Memory layout of the scan list.\n   * @verbinclude radioddity_scanlist.txt\n   */\n  class ScanListElement: public Element\n  {\n  public:\n    /** Possible priority channel types. */\n    enum Mode {\n      PL_NONPRI = 0,              ///< Only non-priority channels.\n      PL_DISABLE = 1,             ///< Disable priority channels.\n      PL_PRI = 2,                 ///< Only priority channels.\n      PL_PRI_NONPRI = 3           ///< Priority and non-priority channels.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    ScanListElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ScanListElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ScanListElement();\n\n    /** The size of the scan list. */\n    static constexpr unsigned int size() { return 0x0058; }\n\n    /** Resets the scan list. */\n    void clear();\n\n    /** Returns the name of the scan list. */\n    virtual QString name() const;\n    /** Sets the name of the scan list. */\n    virtual void setName(const QString &name);\n\n    /** Returns @c true if channel mark is enabled. */\n    virtual bool channelMark() const;\n    /** Enables/disables channel mark. */\n    virtual void enableChannelMark(bool enable);\n\n    /** Returns the scan mode. */\n    virtual Mode mode() const;\n    /** Sets the scan mode. */\n    virtual void setMode(Mode mode);\n\n    /** Returns @c true if talk back is enabled. */\n    virtual bool talkback() const;\n    /** Enables/disables talk back. */\n    virtual void enableTalkback(bool enable);\n\n    /** Returns @c true if the n-th member is set. */\n    virtual bool hasMember(unsigned n) const;\n    /** Returns @c true if the n-th member is selected channel. */\n    virtual bool isSelected(unsigned n) const;\n    /** Returns the n-th member index. */\n    virtual unsigned member(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMember(unsigned n, unsigned idx);\n    /** Sets the n-th member to be the selected channel. */\n    virtual void setSelected(unsigned n);\n    /** Clears the n-th member. */\n    virtual void clearMember(unsigned n);\n\n    /** Returns @c true if the primary priority channel is set. */\n    virtual bool hasPrimary() const;\n    /** Returns @c true if the primary priority channel is the selected channel. */\n    virtual bool primaryIsSelected() const;\n    /** Return the channel index for the primary priority channel. */\n    virtual unsigned primary() const;\n    /** Sets the primary priority channel index. */\n    virtual void setPrimary(unsigned idx);\n    /** Sets the primary priority channel to be the selected channel. */\n    virtual void setPrimarySelected();\n    /** Clears the primary priority channel. */\n    virtual void clearPrimary();\n\n    /** Returns @c true if the secondary priority channel is set. */\n    virtual bool hasSecondary() const;\n    /** Returns @c true if the secondary priority channel is the selected channel. */\n    virtual bool secondaryIsSelected() const;\n    /** Return the channel index for the secondary priority channel. */\n    virtual unsigned secondary() const;\n    /** Sets the secondary priority channel index. */\n    virtual void setSecondary(unsigned idx);\n    /** Sets the secondary priority channel to be the selected channel. */\n    virtual void setSecondarySelected();\n    /** Clears the secondary priority channel. */\n    virtual void clearSecondary();\n\n    /** Returns @c true if the revert channel is set, if @c false the radio will transmit on the\n     * last active channel during scan. */\n    virtual bool hasRevert() const;\n    /** Returns @c true if the revert channel is the selected channel. */\n    virtual bool revertIsSelected() const;\n    /** Return the channel index for the revert channel. */\n    virtual unsigned revert() const;\n    /** Sets the revert channel index. */\n    virtual void setRevert(unsigned idx);\n    /** Sets the revert channel to be the selected one. */\n    virtual void setRevertSelected();\n    /** Clears the revert channel, sets it to last active. */\n    virtual void clearRevert();\n\n    /** Returns the hold time in ms. */\n    virtual unsigned holdTime() const;\n    /** Sets the hold time in ms. */\n    virtual void setHoldTime(unsigned ms);\n\n    /** Returns the priority sample time in ms. */\n    virtual unsigned prioritySampleTime() const;\n    /** Sets the priority sample time in ms. */\n    virtual void setPrioritySampleTime(unsigned ms);\n\n    /** Constructs a @c ScanList object from this codeplug representation. */\n    virtual ScanList *toScanListObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed @c ScanList object to the rest of the generic configuration. */\n    virtual bool linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug representation from the given @c ScanList object. */\n    virtual bool fromScanListObj(const ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the scan list. */\n    struct Limit: public Element::Limit {\n      /// Maximum name length.\n      static constexpr unsigned int name()                    { return 15; }\n      /// Maximum number of members.\n      static constexpr unsigned int members()                 { return 32; }\n    };\n\n  protected:\n    /// @cond DO_NOT_DOCUMENT\n    struct Offset: public Element::Offset {\n      static constexpr unsigned int name()                    { return 0x0000; }\n      static constexpr Bit channelMark()                      { return {0x000f, 4}; }\n      static constexpr Bit mode()                             { return {0x000f, 6}; }\n      static constexpr Bit talkback()                         { return {0x000f, 7}; }\n      static constexpr unsigned int members()                 { return 0x0010; }\n      static constexpr unsigned int betweenMembers()          { return 0x0002; }\n      static constexpr unsigned int primary()                 { return 0x0050; }\n      static constexpr unsigned int secondary()               { return 0x0052; }\n      static constexpr unsigned int revert()                  { return 0x0054; }\n      static constexpr unsigned int holdTime()                { return 0x0056; }\n      static constexpr unsigned int primaryHoldTime()         { return 0x0057; }\n    };\n    /// @endcond\n  };\n\n  /** Implements the base class of scan lists banks for all Radioddity codeplugs.\n   *\n   * Memory layout of the scan list table.\n   * @verbinclude radioddity_scanlistbank.txt\n   */\n  class ScanListBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    ScanListBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ScanListBankElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ScanListBankElement();\n\n    /** The size of the scan list bank. */\n    static constexpr unsigned int size() { return 0x56f0; }\n\n    /** Resets the scan list bank. */\n    void clear();\n\n    /** Returns @c true if the n-th scan list is enabled. */\n    virtual bool isEnabled(unsigned n) const;\n    /** Enable/disable n-th scan list. */\n    virtual void enable(unsigned n, bool enabled);\n    /** Returns a pointer to the n-th scan list. */\n    virtual uint8_t *get(unsigned n) const;\n\n  public:\n    /** Some limits for the scan list bank. */\n    struct Limit {\n      static constexpr unsigned int scanListCount() { return 250; } ///< Maximum number of scan lists.\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bytemap()   { return 0x0000; }\n      static constexpr unsigned int scanLists() { return 0x0040; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the base class of general settings for all Radioddity codeplugs.\n   *\n   * Memory layout of the general settings\n   * @verbinclude radioddity_generalsettings.txt\n   */\n  class GeneralSettingsElement: public Element\n  {\n  public:\n    /** Use monitor type from extension. */\n    typedef RadiodditySettingsExtension::MonitorType MonitorType;\n    /** Use ARTS tone mode from extension. */\n    typedef RadioddityToneSettingsExtension::ARTSTone ARTSTone;\n    /** Use scan mode from extension. */\n    typedef RadiodditySettingsExtension::ScanMode ScanMode;\n\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit GeneralSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GeneralSettingsElement();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0028; }\n\n    /** Resets the general settings. */\n    void clear();\n\n    /** Returns the radio name. */\n    virtual QString name() const;\n    /** Sets the radio name. */\n    virtual void setName(const QString &name);\n\n    /** Returns the DMR radio ID. */\n    virtual unsigned radioID() const;\n    /** Sets the DMR radio ID. */\n    virtual void setRadioID(unsigned id);\n\n    /** Returns the preamble duration in ms. */\n    virtual Interval preambleDuration() const;\n    /** Sets the preamble duration in ms. */\n    virtual void setPreambleDuration(const Interval &dur);\n\n    /** Returns the monitor type. */\n    virtual MonitorType monitorType() const;\n    /** Sets the monitor type. */\n    virtual void setMonitorType(MonitorType type);\n\n    /** Returns the VOX sensitivity [1-10], 0=disabled. */\n    virtual Level voxSensitivity() const;\n    /** Sets the VOX sensitivity. */\n    virtual void setVOXSensitivity(Level value);\n\n    /** Returns the low-battery warn interval in seconds. */\n    virtual unsigned lowBatteryWarnInterval() const;\n    /** Sets the low-battery warn interval in seconds. */\n    virtual void setLowBatteryWarnInterval(unsigned sec);\n\n    /** Returns the call-alert duration in seconds. */\n    virtual unsigned callAlertDuration() const;\n    /** Sets the call-allert duration in seconds. */\n    virtual void setCallAlertDuration(unsigned sec);\n\n    /** Returns the lone-worker response period in minutes. */\n    virtual unsigned loneWorkerResponsePeriod() const;\n    /** Sets the lone-worker response period in minutes. */\n    virtual void setLoneWorkerResponsePeriod(unsigned min);\n    /** Returns the lone-worker reminder period in seconds. */\n    virtual unsigned loneWorkerReminderPeriod() const;\n    /** Sets the lone-worker reminder period in seconds. */\n    virtual void setLoneWorkerReminderPeriod(unsigned sec);\n\n    /** Returns the group call hang time in ms. */\n    virtual Interval groupCallHangTime() const;\n    /** Sets the group call hang time in ms. */\n    virtual void setGroupCallHangTime(const Interval &dur);\n    /** Returns the private call hang time in ms. */\n    virtual Interval privateCallHangTime() const;\n    /** Sets the private call hang time in ms. */\n    virtual void setPrivateCallHangTime(const Interval &dur);\n\n    /** Returns @c true if the down-channel mode is VFO. */\n    virtual bool downChannelModeVFO() const;\n    /** Enables/disables down-channel mode is VFO. */\n    virtual void enableDownChannelModeVFO(bool enable);\n    /** Returns @c true if the up-channel mode is VFO. */\n    virtual bool upChannelModeVFO() const;\n    /** Enables/disables up-channel mode is VFO. */\n    virtual void enableUpChannelModeVFO(bool enable);\n    /** Returns @c true if the reset tone is enabled. */\n    virtual bool resetTone() const;\n    /** Enables/disables reset tone. */\n    virtual void enableResetTone(bool enable);\n    /** Returns @c true if the unknown number tone is enabled. */\n    virtual bool unknownNumberTone() const;\n    /** Enables/disables reset tone. */\n    virtual void enableUnknownNumberTone(bool enable);\n    /** Returns the ARTS tone mode. */\n    virtual ARTSTone artsToneMode() const;\n    /** Sets the ARTS tone mode. */\n    virtual void setARTSToneMode(ARTSTone mode);\n\n    /** Returns @c true if the digital channel talk permit tone is enabled. */\n    virtual bool digitalTalkPermitTone() const;\n    /** Enables/disables digital channel talk permit tone. */\n    virtual void enableDigitalTalkPermitTone(bool enable);\n    /** Returns @c true if the analog channel talk permit tone is enabled. */\n    virtual bool analogTalkPermitTone() const;\n    /** Enables/disables analog channel talk permit tone. */\n    virtual void enableAnalogTalkPermitTone(bool enable);\n    /** Returns @c true if the reset tone is enabled. */\n    virtual bool selftestTone() const;\n    /** Enables/disables reset tone. */\n    virtual void enableSelftestTone(bool enable);\n    /** Returns @c true if the channel free indication tone is enabled. */\n    virtual bool channelFreeIndicationTone() const;\n    /** Enables/disables channel free indication tone. */\n    virtual void enableChannelFreeIndicationTone(bool enable);\n    /** Returns @c true if all tones are disabled. */\n    virtual bool allTonesDisabled() const;\n    /** Disables/enables all tones. */\n    virtual void disableAllTones(bool disable);\n    /** Returns @c true if reception is disabled for battery saving. */\n    virtual bool batsaveRX() const;\n    /** Enables/disables battery saving by disabling RX. */\n    virtual void enableBatsaveRX(bool enable);\n    /** Returns @c true if preable is disabled for battery saving. */\n    virtual bool batsavePreamble() const;\n    /** Enables/disables battery saving by disabling preamble. */\n    virtual void enableBatsavePreamble(bool enable);\n\n    /** Returns @c true if all LEDs are disabled. */\n    virtual bool allLEDsDisabled() const;\n    /** Disables/enables all LEDs. */\n    virtual void disableAllLEDs(bool disable);\n    /** Returns true if quick-key override is inhibited. */\n    virtual bool quickKeyOverrideInhibited() const;\n    /** Inhibits quick-key override. */\n    virtual void inhibitQuickKeyOverride(bool inhibit);\n\n    /** Returns @c true if the TX exit tone is enabled. */\n    virtual bool txExitTone() const;\n    /** Enables/disables TX exit tone. */\n    virtual void enableTXExitTone(bool enable);\n    /** Returns @c true if the radio transmits on the active channel on double monitor. */\n    virtual bool txOnActiveChannel() const;\n    /** Enables/disables transmission on active channel on double monitor. */\n    virtual void enableTXOnActiveChannel(bool enable);\n    /** Returns @c true if animation is enabled. */\n    virtual bool animation() const;\n    /** Enables/disables animation. */\n    virtual void enableAnimation(bool enable);\n    /** Returns the scan mode. */\n    virtual ScanMode scanMode() const;\n    /** Sets the scan mode. */\n    virtual void setScanMode(ScanMode mode);\n\n    /** Returns the repeater end delay in [0-10]. */\n    virtual unsigned repeaterEndDelay() const;\n    /** Sets the repeater end delay in [0-10]. */\n    virtual void setRepeaterEndDelay(unsigned delay);\n    /** Returns the repeater STE in [0-10]. */\n    virtual unsigned repeaterSTE() const;\n    /** Sets the repeater STE in [0-10]. */\n    virtual void setRepeaterSTE(unsigned ste);\n\n    /** Returns @c true if a programming password is set. */\n    virtual bool hasProgPassword() const;\n    /** Returns the programming password. */\n    virtual QString progPassword() const;\n    /** Sets the programming password. */\n    virtual void setProgPassword(const QString &pwd);\n    /** Resets the programming password. */\n    virtual void clearProgPassword();\n\n    /** Encodes the general setting from the given config. */\n    virtual bool fromConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Updates the given config from this settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: Element::Limit {\n      // VOX sensitivity settings.\n      static constexpr Range<unsigned int> vox() { return {1,10}; }\n    };\n  };\n\n  /** Implements the base class of button settings for all Radioddity codeplugs.\n   *\n   * Encoding of button settings (size 0x20b):\n   * @verbinclude radioddity_buttonsettings.txt */\n  class ButtonSettingsElement: public Element\n  {\n  public:\n    /** Encoding/decoding of function key actions. */\n    struct KeyFunction {\n    public:\n      /** Encodes the given function. */\n      static uint8_t encode(RadioddityButtonSettingsExtension::Function func);\n      /** Decodes the action. */\n      static RadioddityButtonSettingsExtension::Function decode(uint8_t action);\n\n    protected:\n      /** Possible function key actions. */\n      enum Action {\n        None                   = 0x00,  ///< Disables button.\n        ToggleAllAlertTones    = 0x01,\n        EmergencyOn            = 0x02,\n        EmergencyOff           = 0x03,\n        ToggleMonitor          = 0x05,  ///< Toggle monitor on channel.\n        NuiaceDelete           = 0x06,\n        OneTouch1              = 0x07,  ///< Performs the first of 6 user-programmable actions (call, message).\n        OneTouch2              = 0x08,  ///< Performs the second of 6 user-programmable actions (call, message).\n        OneTouch3              = 0x09,  ///< Performs the third of 6 user-programmable actions (call, message).\n        OneTouch4              = 0x0a,  ///< Performs the fourth of 6 user-programmable actions (call, message).\n        OneTouch5              = 0x0b,  ///< Performs the fifth of 6 user-programmable actions (call, message).\n        OneTouch6              = 0x0c,  ///< Performs the sixt of 6 user-programmable actions (call, message).\n        ToggleRepeatTalkaround = 0x0d,\n        ToggleScan             = 0x0e,\n        TogglePrivacy          = 0x10,\n        ToggleVox              = 0x11,\n        ZoneSelect             = 0x12,\n        BatteryIndicator       = 0x13,\n        ToggleLoneWorker       = 0x14,\n        PhoneExit              = 0x16,\n        ToggleFlashLight       = 0x1a,\n        ToggleFMRadio          = 0x1b\n      };\n    };\n\n    /** Possible one-touch actions. */\n    enum class OneTouchAction {\n      None                   = 0x00,  ///< Disabled.\n      DigitalCall            = 0x10,  ///< Calls a digital contact.\n      DigitalMessage         = 0x11,  ///< Sends a SMS.\n      AnalogCall             = 0x20   ///< Calls an analog contact.\n    };\n\n  protected:\n    /** Hidden constructor */\n    ButtonSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit ButtonSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ButtonSettingsElement();\n\n    /** Clears the button settings. */\n    void clear();\n\n    /** Returns the long-press duration in ms. */\n    virtual Interval longPressDuration() const;\n    /** Sets the long-press duration in ms. */\n    virtual void setLongPressDuration(Interval ms);\n\n    /** Returns the side-key 1 short-press action. */\n    virtual RadioddityButtonSettingsExtension::Function sk1ShortPress() const;\n    /** Sets the side-key 1 short-press action. */\n    virtual void setSK1ShortPress(RadioddityButtonSettingsExtension::Function action);\n    /** Returns the side-key 1 long-press action. */\n    virtual RadioddityButtonSettingsExtension::Function sk1LongPress() const;\n    /** Sets the side-key 1 long-press action. */\n    virtual void setSK1LongPress(RadioddityButtonSettingsExtension::Function action);\n\n    /** Returns the side-key 2 short-press action. */\n    virtual RadioddityButtonSettingsExtension::Function sk2ShortPress() const;\n    /** Sets the side-key 2 short-press action. */\n    virtual void setSK2ShortPress(RadioddityButtonSettingsExtension::Function action);\n    /** Returns the side-key 2 long-press action. */\n    virtual RadioddityButtonSettingsExtension::Function sk2LongPress() const;\n    /** Sets the side-key 2 long-press action. */\n    virtual void setSK2LongPress(RadioddityButtonSettingsExtension::Function action);\n\n    /** Returns the top-key short-press action. */\n    virtual RadioddityButtonSettingsExtension::Function tkShortPress() const;\n    /** Sets the top-key short-press action. */\n    virtual void setTKShortPress(RadioddityButtonSettingsExtension::Function action);\n    /** Returns the top-key long-press action. */\n    virtual RadioddityButtonSettingsExtension::Function tkLongPress() const;\n    /** Sets the top-key long-press action. */\n    virtual void setTKLongPress(RadioddityButtonSettingsExtension::Function action);\n\n    /** Returns the n-th one-touch action. */\n    virtual OneTouchAction oneTouchAction(unsigned n) const;\n    /** Returns the n-th one-touch contact index (if action is @c OneTouchAction::DigitalCall). */\n    virtual unsigned oneTouchContact(unsigned n) const;\n    /** Returns the n-th one-touch message index (if action is @c OneTouchAction::DigitalMessage). */\n    virtual unsigned oneTouchMessage(unsigned n) const;\n    /** Disables the n-th one-touch action. */\n    virtual void disableOneTouch(unsigned n);\n    /** Configures n-th one-touch action as a digital call to contact index. */\n    virtual void setOneTouchDigitalCall(unsigned n, unsigned index);\n    /** Configures n-th one-touch action as a digital message using given index. */\n    virtual void setOneTouchDigitalMessage(unsigned n, unsigned index);\n    /** Configures n-th one-touch action as a analog call. */\n    virtual void setOneTouchAnalogCall(unsigned n);\n\n    /** Encodes the button settings (if set). */\n    bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Decodes the button settings. */\n    bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limits {\n      /** Range of valid long-press durations. */\n      static constexpr TimeRange longPressDuration() {\n        return TimeRange{Interval::fromMilliseconds(0), Interval::fromMilliseconds(255*250)};\n      }\n      /** Number of one-touch actions. */\n      static constexpr unsigned int oneTouchActions() { return 6; }\n    };\n\n  protected:\n    /** Internal used offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int longPressDuration()      { return 0x0001; }\n      static constexpr unsigned int sk1ShortPress()          { return 0x0002; }\n      static constexpr unsigned int sk1LongPress()           { return 0x0003; }\n      static constexpr unsigned int sk2ShortPress()          { return 0x0004; }\n      static constexpr unsigned int sk2LongPress()           { return 0x0005; }\n      static constexpr unsigned int tkShortPress()           { return 0x0006; }\n      static constexpr unsigned int tkLongPress()            { return 0x0007; }\n      static constexpr unsigned int oneTouchActions()        { return 0x0008; }\n      static constexpr unsigned int betweenOneTouchActions() { return 0x0004; }\n      /// @endcond\n    };\n  };\n\n  /** Implements the base class of menu settings for all Radioddity codeplugs.\n   *\n   * Encoding of Menu settings (size 0x08b):\n   * @verbinclude radioddity_menusettings.txt */\n  class MenuSettingsElement: public Element\n  {\n  public:\n    /** Possible channel display modes. */\n    enum class ChannelDisplayMode {\n      Number    = 0,  ///< Show channel number.\n      Name      = 1,  ///< Show channel name.\n      Frequency = 2   ///< Show channel frequency.\n    };\n\n    /** Possible dual-watch modes. */\n    enum class DualWatchMode {\n      DualDual = 1,\n      DualSingle = 2\n    };\n\n  protected:\n    /** Hidden constructor. */\n    MenuSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit MenuSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~MenuSettingsElement();\n\n    /** Resets the menu settings. */\n    void clear();\n\n    /** Returns the menu hang-time in seconds. */\n    virtual unsigned menuHangTime() const;\n    /** Sets the menu hang time in seconds. */\n    virtual void setMenuHangTime(unsigned sec);\n\n    /** Returns @c true if the message menu is shown. */\n    virtual bool message() const;\n    /** Enables/disables the message menu. */\n    virtual void enableMessage(bool enable);\n    /** Returns @c true if the scan-start menu is shown. */\n    virtual bool scanStart() const;\n    /** Enables/disables the scan-start menu. */\n    virtual void enableScanStart(bool enable);\n    /** Returns @c true if the edit scan-list menu is shown. */\n    virtual bool editScanList() const;\n    /** Enables/disables the edit scan-list menu. */\n    virtual void enableEditScanList(bool enable);\n    /** Returns @c true if the call-alert menu is shown. */\n    virtual bool callAlert() const;\n    /** Enables/disables the call-alert menu. */\n    virtual void enableCallAlert(bool enable);\n    /** Returns @c true if the edit-contact menu is shown. */\n    virtual bool editContact() const;\n    /** Enables/disables the edit-contact menu. */\n    virtual void enableEditContact(bool enable);\n    /** Returns @c true if the manual-dial menu is shown. */\n    virtual bool manualDial() const;\n    /** Enables/disables the manual-dial menu. */\n    virtual void enableManualDial(bool enable);\n    /** Returns @c true if the radio-check menu is shown. */\n    virtual bool radioCheck() const;\n    /** Enables/disables the radioCheck menu. */\n    virtual void enableRadioCheck(bool enable);\n    /** Returns @c true if the remote-monitor menu is shown. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables the message menu. */\n    virtual void enableRemoteMonitor(bool enable);\n\n    /** Returns @c true if the radio-enable menu is shown. */\n    virtual bool radioEnable() const;\n    /** Enables/disables the radio-enable menu. */\n    virtual void enableRadioEnable(bool enable);\n    /** Returns @c true if the radio-disable menu is shown. */\n    virtual bool radioDisable() const;\n    /** Enables/disables the radio-disable menu. */\n    virtual void enableRadioDisable(bool enable);\n    /** Returns @c true if the programming-password menu is shown. */\n    virtual bool progPassword() const;\n    /** Enables/disables the programming-password menu. */\n    virtual void enableProgPassword(bool enable);\n    /** Returns @c true if the talkaround menu is shown. */\n    virtual bool talkaround() const;\n    /** Enables/disables the talkaround menu. */\n    virtual void enableTalkaround(bool enable);\n    /** Returns @c true if the tone menu is shown. */\n    virtual bool tone() const;\n    /** Enables/disables the tone menu. */\n    virtual void enableTone(bool enable);\n    /** Returns @c true if the power menu is shown. */\n    virtual bool power() const;\n    /** Enables/disables the power menu. */\n    virtual void enablePower(bool enable);\n    /** Returns @c true if the backlight menu is shown. */\n    virtual bool backlight() const;\n    /** Enables/disables the backlight menu. */\n    virtual void enableBacklight(bool enable);\n    /** Returns @c true if the intro-screen menu is shown. */\n    virtual bool introScreen() const;\n    /** Enables/disables the message menu. */\n    virtual void enableIntroScreen(bool enable);\n\n    /** Returns @c true if the keypad-lock menu is shown. */\n    virtual bool keypadLock() const;\n    /** Enables/disables the keypad-lock menu. */\n    virtual void enableKeypadLock(bool enable);\n    /** Returns @c true if the LED-indicator menu is shown. */\n    virtual bool ledIndicator() const;\n    /** Enables/disables the LED-indicator menu. */\n    virtual void enableLEDIndicator(bool enable);\n    /** Returns @c true if the squelch menu is shown. */\n    virtual bool squelch() const;\n    /** Enables/disables the squelch menu. */\n    virtual void enableSquelch(bool enable);\n    /** Returns @c true if the privacy menu is shown. */\n    virtual bool privacy() const;\n    /** Enables/disables the privacy menu. */\n    virtual void enablePrivacy(bool enable);\n    /** Returns @c true if the VOX menu is shown. */\n    virtual bool vox() const;\n    /** Enables/disables the VOX menu. */\n    virtual void enableVOX(bool enable);\n    /** Returns @c true if the password-lock menu is shown. */\n    virtual bool passwordLock() const;\n    /** Enables/disables the password-lock menu. */\n    virtual void enablePasswordLock(bool enable);\n    /** Returns @c true if the missed-calls menu is shown. */\n    virtual bool missedCalls() const;\n    /** Enables/disables the missed-calls menu. */\n    virtual void enableMissedCalls(bool enable);\n    /** Returns @c true if the answered-calls menu is shown. */\n    virtual bool answeredCalls() const;\n    /** Enables/disables the answered-calls menu. */\n    virtual void enableAnsweredCalls(bool enable);\n\n    /** Returns @c true if the outgoing-calls menu is shown. */\n    virtual bool outgoingCalls() const;\n    /** Enables/disables the outgoing-calls menu. */\n    virtual void enableOutgoingCalls(bool enable);\n    /** Returns @c true if the channel display-mode menu is shown. */\n    virtual bool channelDisplay() const;\n    /** Enables/disables the channel display mode menu. */\n    virtual void enableChannelDisplay(bool enable);\n    /** Returns @c true if the dual-watch menu is shown. */\n    virtual bool dualWatch() const;\n    /** Enables/disables the dual-watch menu. */\n    virtual void enableDualWatch(bool enable);\n\n    /** Returns the keypad lock time in seconds. */\n    virtual unsigned keypadLockTime() const;\n    /** Sets the keypad lock time in seconds. */\n    virtual void setKeypadLockTime(unsigned sec);\n\n    /** Returns the backlight time in seconds. */\n    virtual unsigned backlightTime() const;\n    /** Sets the backlight time in seconds. */\n    virtual void setBacklightTime(unsigned sec);\n\n    /** Returns the channel display mode. */\n    virtual ChannelDisplayMode channelDisplayMode() const;\n    /** Sets the channel display mode. */\n    virtual void setChannelDisplayMode(ChannelDisplayMode mode);\n\n    /** Returns @c true if the keytone is enabled. */\n    virtual bool keyTone() const;\n    /** Enables/disables the keytone. */\n    virtual void enableKeyTone(bool enable);\n\n    /** Returns the dual-watch mode. */\n    virtual DualWatchMode dualWatchMode() const;\n    /** Sets the dual-watch mode. */\n    virtual void setDualWatchMode(DualWatchMode mode);\n  };\n\n  /** Implements the base class of boot settings for all Radioddity codeplugs.\n   *\n   * Encoding of boot settings (size 0x20b):\n   * @verbinclude radioddity_bootsettings.txt */\n  class BootSettingsElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BootSettingsElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit BootSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~BootSettingsElement();\n\n    /** Resets the settings. */\n    void clear();\n\n    /** Returns the boot display mode. */\n    virtual BootSettings::BootDisplay bootDisplay() const;\n    /** Sets the boot display mode. */\n    virtual void setBootDisplay(BootSettings::BootDisplay mode);\n\n    /** Returns @c true if the boot password is enabled. */\n    virtual bool bootPasswordEnabled() const;\n    /** Enables/disables the boot password. */\n    virtual void enableBootPassword(bool enable);\n    /** Returns the boot password (6 digit). */\n    virtual QString bootPassword() const;\n    /** Sets the boot password (6 digit). */\n    virtual bool setBootPassword(const QString &passwd);\n\n    /** Decodes boot settings and updates config. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Encodes boot settings from config. */\n    virtual bool encode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: Element::Limit {\n      // Maximum password length.\n      static constexpr unsigned passwordLength() { return 8; }\n    };\n\n  protected:\n    /** Internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int bootText() { return 0x0000; }\n      static constexpr unsigned int bootPasswordEnable() { return 0x0001; }\n      static constexpr unsigned int bootPassword() { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the base class of boot messages for all Radioddity codeplugs.\n   *\n   * Encoding of boot messages (size 0x20b):\n   * @verbinclude radioddity_boottext.txt */\n  class BootTextElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BootTextElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit BootTextElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~BootTextElement();\n\n    /** The size of the boot text element. */\n    static constexpr unsigned int size() { return 0x0020; }\n\n    /** Resets the intro text. */\n    void clear();\n\n    /** Returns the first line. */\n    virtual QString line1() const;\n    /** Sets the first line. */\n    virtual void setLine1(const QString &text);\n    /** Returns the Second line. */\n    virtual QString line2() const;\n    /** Sets the second line. */\n    virtual void setLine2(const QString &text);\n\n    /** Encodes boot text settings from configuration. */\n    virtual bool fromConfig(Context &ctx, const ErrorStack &err = ErrorStack());\n    /** Updates the configuration with the boot text settings. */\n    virtual bool updateConfig(Context &ctx, const ErrorStack &err = ErrorStack());\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      static constexpr unsigned int lineLength() { return 16; }  ///< The maximum length of the boot text line.\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int line1() { return 0x0000;}\n      static constexpr unsigned int line2() { return 0x0010;}\n      /// @endcond\n    };\n  };\n\n  /** Implements the base class of a message bank for all Radioddity message banks.\n   *\n   * Encoding of messages (size: 0x1248b):\n   * @verbinclude radioddity_messagebank.txt */\n  class MessageBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageBankElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit MessageBankElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~MessageBankElement();\n\n    /** Returns the size of the message bank. */\n    static constexpr unsigned int size() { return 0x1248; }\n    /** Resets all messages. */\n    void clear();\n\n    /** Returns the number of messages. */\n    virtual unsigned numMessages() const;\n    /** Returns the n-th message. */\n    virtual QString message(unsigned n) const;\n    /** Appends a message to the list. */\n    virtual void appendMessage(const QString msg);\n\n    /** Encodes all preset messages. */\n    virtual bool encode(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n    /** Decodes all preset messages. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int messages()      { return 32; }   ///< Maximum number of messages.\n      static constexpr unsigned int messageLength() { return 144; }  ///< Maximum length of each message.\n    };\n\n  protected:\n    /** Some internal used offset. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int messageConut()    { return 0x0000; }\n      static constexpr unsigned int messageLengths()  { return 0x0008; }\n      static constexpr unsigned int messages()        { return 0x0048; }\n      static constexpr unsigned int betweenMessages() { return Limit::messageLength(); }\n      /// @endcond\n    };\n  };\n\n  /** Represents all encryption keys and settings within the codeplug on the device.\n   *\n   * Memory representation of encryption settings:\n   * @verbinclude radioddity_privacy.txt */\n  class EncryptionElement: public Codeplug::Element\n  {\n  public:\n    /** Encodes possible privacy types. For now, only none (encryption disabled) and basic are\n     * supported. */\n    enum class PrivacyType {\n      None,   ///< No encryption at all.\n      Basic   ///< Use basic DMR encryption.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    EncryptionElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit EncryptionElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~EncryptionElement();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0088; }\n\n    void clear();\n\n    /** Returns the privacy type set. */\n    virtual PrivacyType privacyType() const;\n    /** Sets the privacy type. */\n    virtual void setPrivacyType(PrivacyType type);\n\n    /** Returns @c true if the n-th \"basic\" key (32bit) is set.\n     * That is, if it is not filled with 0xff. */\n    virtual bool isBasicKeySet(unsigned n) const;\n    /** Returns the n-th \"basic\" key (32bit). */\n    virtual QByteArray basicKey(unsigned n) const;\n    /** Sets the n-th \"basic\" key (32bit). */\n    virtual void setBasicKey(unsigned n, const QByteArray &key);\n    /** Resets the n-th basic key. */\n    virtual void clearBasicKey(unsigned n);\n\n    /** Encodes given encryption extension. */\n    virtual bool fromCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Constructs the encryption extension. */\n    virtual bool updateCommercialExt(Context &ctx, const ErrorStack &err=ErrorStack());\n    /** Links the given encryption extension. */\n    virtual bool linkCommercialExt(CommercialExtension *ext, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits for this element. */\n    struct Limit {\n      /** The maximum number of keys. */\n      static constexpr unsigned int keyCount() { return 16; }\n      /** The required key size. */\n      static constexpr unsigned int keySize() { return 4; }\n    };\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int privacyType() { return 0x0000; }\n      static constexpr unsigned int bitmap() { return 0x0002; }\n      static constexpr unsigned int keys() { return 0x0008; }         ///< Offset of the first key.\n      static constexpr unsigned int key() { return 0x0008;}           ///< Offset between keys.\n      /// @endcond\n    };\n  };\n\nprotected:\n  /** Hidden constructor, use a device specific class to instantiate. */\n  explicit RadioddityCodeplug(QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~RadioddityCodeplug();\n\n  /** Clears and resets the complete codeplug to some default values. */\n  virtual void clear();\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n  bool postprocess(Config *config, const ErrorStack &err) const;\n\n  Config *preprocess(Config *config, const ErrorStack &err) const;\n  bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Decodes the binary codeplug and stores its content in the given generic configuration using\n   * the given context. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes the given generic configuration as a binary codeplug using the given context. */\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Clears the general settings in the codeplug. */\n  virtual void clearGeneralSettings() = 0;\n  /** Updates the general settings from the given configuration. */\n  virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Updates the given configuration from the general settings. */\n  virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the button settings. */\n  virtual void clearButtonSettings() = 0;\n  /** Encodes button settings. */\n  virtual bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the button settings. */\n  virtual bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the messages. */\n  virtual void clearMessages() = 0;\n  /** Encodes preset messages. */\n  virtual bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes preset messages. */\n  virtual bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all contacts in the codeplug. */\n  virtual void clearContacts() = 0;\n  /** Encodes all digital contacts in the configuration into the codeplug. */\n  virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a digital contact to the configuration for each one in the codeplug. */\n  virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all DTMF contacts in the codeplug. */\n  virtual void clearDTMFContacts() = 0;\n  /** Encodes all DTMF contacts. */\n  virtual bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds all DTMF contacts to the configuration. */\n  virtual bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clear all channels. */\n  virtual void clearChannels() = 0;\n  /** Encode all channels. */\n  virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds all defined channels to the configuration. */\n  virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all channels. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears menu settings. */\n  virtual void clearMenuSettings() = 0;\n\n  /** Clears boot text. */\n  virtual void clearBootSettings() = 0;\n  /** Encodes boot text. */\n  virtual bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Updates the given configuration from the boot text settings. */\n  virtual bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the VFO settings. */\n  virtual void clearVFOSettings() = 0;\n\n  /** Clears all zones. */\n  virtual void clearZones() = 0;\n  /** Encodes zones. */\n  virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds zones to the configuration. */\n  virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all zones within the configuration. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all scan lists. */\n  virtual void clearScanLists() = 0;\n  /** Encodes all scan lists. */\n  virtual bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Creates all scan lists. */\n  virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all scan lists. */\n  virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all group lists. */\n  virtual void clearGroupLists() = 0;\n  /** Encodes all group lists. */\n  virtual bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Creates all group lists. */\n  virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all group lists. */\n  virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all encryption keys. */\n  virtual void clearEncryption() = 0;\n  /** Encodes all encryption keys defined. */\n  virtual bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Creates all encryption keys. */\n  virtual bool createEncryption(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all encryption keys. */\n  virtual bool linkEncryption(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n};\n\n#endif // RADIODDITYCODEPLUG_HH\n"
  },
  {
    "path": "lib/radioddity_extensions.cc",
    "content": "#include \"radioddity_extensions.hh\"\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityButtonSettingsExtension\n * ********************************************************************************************* */\nRadioddityButtonSettingsExtension::RadioddityButtonSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _longPressDuration(Interval::fromMilliseconds(1000)),\n    _funcKey1Short(Function::ZoneSelect), _funcKey1Long(Function::ToggleFMRadio),\n    _funcKey2Short(Function::ToggleMonitor), _funcKey2Long(Function::ToggleFlashLight),\n    _funcKey3Short(Function::BatteryIndicator), _funcKey3Long(Function::ToggleVox)\n{\n  // pass...\n}\n\nConfigItem *\nRadioddityButtonSettingsExtension::clone() const {\n  ConfigItem *clone = new RadioddityButtonSettingsExtension();\n  if (! clone->copy(*this)) {\n    delete clone;\n    return nullptr;\n  }\n  return clone;\n}\n\nInterval\nRadioddityButtonSettingsExtension::longPressDuration() const {\n  return _longPressDuration;\n}\nvoid\nRadioddityButtonSettingsExtension::setLongPressDuration(Interval interval) {\n  if (interval == _longPressDuration)\n    return;\n  _longPressDuration = interval;\n  emit modified(this);\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey1Short() const {\n  return _funcKey1Short;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey1Short(Function func) {\n  if (func == _funcKey1Short)\n    return;\n  _funcKey1Short = func;\n  emit modified(this);\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey1Long() const {\n  return _funcKey1Long;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey1Long(Function func) {\n  if (func == _funcKey1Long)\n    return;\n  _funcKey1Long = func;\n  emit modified(this);\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey2Short() const {\n  return _funcKey2Short;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey2Short(Function func) {\n  if (func == _funcKey2Short)\n    return;\n  _funcKey2Short = func;\n  emit modified(this);\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey2Long() const {\n  return _funcKey2Long;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey2Long(Function func) {\n  if (func == _funcKey2Long)\n    return;\n  _funcKey2Long = func;\n  emit modified(this);\n}\n\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey3Short() const {\n  return _funcKey3Short;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey3Short(Function func) {\n  if (func == _funcKey3Short)\n    return;\n  _funcKey3Short = func;\n  emit modified(this);\n}\nRadioddityButtonSettingsExtension::Function\nRadioddityButtonSettingsExtension::funcKey3Long() const {\n  return _funcKey3Long;\n}\nvoid\nRadioddityButtonSettingsExtension::setFuncKey3Long(Function func) {\n  if (func == _funcKey3Long)\n    return;\n  _funcKey3Long = func;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityToneSettingsExtension\n * ********************************************************************************************* */\nRadioddityToneSettingsExtension::RadioddityToneSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _lowBatteryWarn(true), _lowBatteryWarnInterval(Interval::fromSeconds(30)),\n  _lowBatteryWarnVolume(5), _callAlertDuration(Interval::fromSeconds(120)),\n  _unknownNumberTone(false), _artsToneMode(ARTSTone::Once), _selftestTone(true)\n{\n  // pass...\n}\n\nConfigItem *\nRadioddityToneSettingsExtension::clone() const {\n  RadioddityToneSettingsExtension *ext = new RadioddityToneSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nbool\nRadioddityToneSettingsExtension::lowBatteryWarn() const {\n  return _lowBatteryWarn;\n}\nvoid\nRadioddityToneSettingsExtension::enableLowBatteryWarn(bool enable) {\n  if (enable == _lowBatteryWarn)\n    return;\n  _lowBatteryWarn = enable;\n  emit modified(this);\n}\n\nInterval\nRadioddityToneSettingsExtension::lowBatteryWarnInterval() const {\n  return _lowBatteryWarnInterval;\n}\nvoid\nRadioddityToneSettingsExtension::setLowBatteryWarnInterval(Interval sec) {\n  if (_lowBatteryWarnInterval == sec)\n    return;\n  _lowBatteryWarnInterval = sec;\n  emit modified(this);\n}\n\nunsigned int\nRadioddityToneSettingsExtension::lowBatteryWarnVolume() const {\n  return _lowBatteryWarnVolume;\n}\nvoid\nRadioddityToneSettingsExtension::setLowBatteryWarnVolume(unsigned int volume) {\n  volume = std::min(10U, std::max(1U, volume));\n  if (volume == _lowBatteryWarnVolume)\n    return;\n  _lowBatteryWarnVolume = volume;\n  emit modified(this);\n}\n\nInterval\nRadioddityToneSettingsExtension::callAlertDuration() const {\n  return _callAlertDuration;\n}\nvoid\nRadioddityToneSettingsExtension::setCallAlertDuration(Interval sec) {\n  if (_callAlertDuration == sec)\n    return;\n  _callAlertDuration = sec;\n  emit modified(this);\n}\n\nbool\nRadioddityToneSettingsExtension::unknownNumberTone() const {\n  return _unknownNumberTone;\n}\nvoid\nRadioddityToneSettingsExtension::enableUnknownNumberTone(bool enable) {\n  if (_unknownNumberTone == enable)\n    return;\n  _unknownNumberTone = enable;\n  emit modified(this);\n}\n\nRadioddityToneSettingsExtension::ARTSTone\nRadioddityToneSettingsExtension::artsToneMode() const {\n  return _artsToneMode;\n}\nvoid\nRadioddityToneSettingsExtension::setARTSToneMode(ARTSTone mode) {\n  if (_artsToneMode == mode)\n    return;\n  _artsToneMode = mode;\n  emit modified(this);\n}\n\nbool\nRadioddityToneSettingsExtension::selftestTone() const {\n  return _selftestTone;\n}\nvoid\nRadioddityToneSettingsExtension::enableSelftestTone(bool enable) {\n  if (_selftestTone == enable)\n    return;\n  _selftestTone = enable;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioddityBootSettingsExtension\n * ********************************************************************************************* */\nRadioddityBootSettingsExtension::RadioddityBootSettingsExtension(QObject *parent)\n  : ConfigItem(parent), _progPasswd()\n{\n  // pass...\n}\n\nConfigItem *\nRadioddityBootSettingsExtension::clone() const {\n  auto *ext = new RadioddityBootSettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nconst QString &\nRadioddityBootSettingsExtension::progPassword() const {\n  return _progPasswd;\n}\nvoid\nRadioddityBootSettingsExtension::setProgPassword(const QString &pwd) {\n  if (_progPasswd == pwd)\n    return;\n  _progPasswd = pwd;\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadiodditySettingsExtension\n * ********************************************************************************************* */\nRadiodditySettingsExtension::RadiodditySettingsExtension(QObject *parent)\n  : ConfigExtension(parent), _monitorType(MonitorType::Silent),\n    _loneWorkerResponseTime(Interval::fromMinutes(1)),\n    _loneWorkerReminderPeriod(Interval::fromSeconds(10)),\n    _downChannelModeVFO(false),\n    _upChannelModeVFO(false), _powerSaveMode(true), _wakeupPreamble(true),\n    _powerSaveDelay(Interval::fromSeconds(10)),\n    _disableAllLEDs(false), _quickKeyOverrideInhibited(false), _txOnActiveChannel(true),\n    _scanMode(ScanMode::Time), _repeaterEndDelay(), _repeaterSTE(), _txInterrupt(false),\n    _language(Language::English),\n    _buttonSettings(new RadioddityButtonSettingsExtension(this)),\n    _toneSettings(new RadioddityToneSettingsExtension(this)),\n    _bootSettings(new RadioddityBootSettingsExtension(this))\n{\n  // pass...\n}\n\nConfigItem *\nRadiodditySettingsExtension::clone() const {\n  RadiodditySettingsExtension *ext = new RadiodditySettingsExtension();\n  if (! ext->copy(*this)) {\n    ext->deleteLater();\n    return nullptr;\n  }\n  return ext;\n}\n\nRadiodditySettingsExtension::MonitorType\nRadiodditySettingsExtension::monitorType() const {\n  return _monitorType;\n}\nvoid\nRadiodditySettingsExtension::setMonitorType(MonitorType type) {\n  if (_monitorType == type)\n    return;\n  _monitorType = type;\n  emit modified(this);\n}\n\nInterval\nRadiodditySettingsExtension::loneWorkerResponseTime() const {\n  return _loneWorkerResponseTime;\n}\nvoid\nRadiodditySettingsExtension::setLoneWorkerResponseTime(Interval min) {\n  if (_loneWorkerResponseTime == min)\n    return;\n  _loneWorkerResponseTime = min;\n  emit modified(this);\n}\n\nInterval\nRadiodditySettingsExtension::loneWorkerReminderPeriod() const {\n  return _loneWorkerReminderPeriod;\n}\nvoid\nRadiodditySettingsExtension::setLoneWorkerReminderPeriod(Interval sec) {\n  if (_loneWorkerReminderPeriod == sec)\n    return;\n  _loneWorkerReminderPeriod = sec;\n  emit modified(this);\n}\n\n\nbool\nRadiodditySettingsExtension::downChannelModeVFO() const {\n  return _downChannelModeVFO;\n}\nvoid\nRadiodditySettingsExtension::enableDownChannelModeVFO(bool enable) {\n  if (_downChannelModeVFO == enable)\n    return;\n  _downChannelModeVFO = enable;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::upChannelModeVFO() const {\n  return _upChannelModeVFO;\n}\nvoid\nRadiodditySettingsExtension::enableUpChannelModeVFO(bool enable) {\n  if (_upChannelModeVFO == enable)\n    return;\n  _upChannelModeVFO = enable;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::powerSaveMode() const {\n  return _powerSaveMode;\n}\nvoid\nRadiodditySettingsExtension::enablePowerSaveMode(bool enable) {\n  if (_powerSaveMode == enable)\n    return;\n  _powerSaveMode = enable;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::wakeupPreamble() const {\n  return _wakeupPreamble;\n}\nvoid\nRadiodditySettingsExtension::enableWakeupPreamble(bool enable) {\n  if (_wakeupPreamble == enable)\n    return;\n  _wakeupPreamble = enable;\n  emit modified(this);\n}\n\nInterval\nRadiodditySettingsExtension::powerSaveDelay() const {\n  return _powerSaveDelay;\n}\nvoid\nRadiodditySettingsExtension::setPowerSaveDelay(Interval interv) {\n  if (interv == _powerSaveDelay)\n    return;\n  _powerSaveDelay = interv;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::allLEDsDisabled() const {\n  return _disableAllLEDs;\n}\nvoid\nRadiodditySettingsExtension::disableAllLEDs(bool disable) {\n  if (_disableAllLEDs == disable)\n    return;\n  _disableAllLEDs = disable;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::quickKeyOverrideInhibited() const {\n  return _quickKeyOverrideInhibited;\n}\nvoid\nRadiodditySettingsExtension::inhibitQuickKeyOverride(bool inhibit) {\n  if (_quickKeyOverrideInhibited == inhibit)\n    return;\n  _quickKeyOverrideInhibited = inhibit;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::txOnActiveChannel() const {\n  return _txOnActiveChannel;\n}\nvoid\nRadiodditySettingsExtension::enableTXOnActiveChannel(bool enable) {\n  if (_txOnActiveChannel == enable)\n    return;\n  _txOnActiveChannel = enable;\n  emit modified(this);\n}\n\nRadiodditySettingsExtension::ScanMode\nRadiodditySettingsExtension::scanMode() const {\n  return _scanMode;\n}\nvoid\nRadiodditySettingsExtension::setScanMode(ScanMode mode) {\n  if (_scanMode == mode)\n    return;\n  _scanMode = mode;\n  emit modified(this);\n}\n\nInterval\nRadiodditySettingsExtension::repeaterEndDelay() const {\n  return _repeaterEndDelay;\n}\nvoid\nRadiodditySettingsExtension::setRepeaterEndDelay(Interval delay) {\n  if (_repeaterEndDelay == delay)\n    return;\n  _repeaterEndDelay = delay;\n  emit modified(this);\n}\n\nInterval\nRadiodditySettingsExtension::repeaterSTE() const {\n  return _repeaterSTE;\n}\nvoid\nRadiodditySettingsExtension::setRepeaterSTE(Interval ste) {\n  if (_repeaterSTE == ste)\n    return;\n  _repeaterSTE = ste;\n  emit modified(this);\n}\n\nbool\nRadiodditySettingsExtension::txInterrupt() const {\n  return _txInterrupt;\n}\nvoid\nRadiodditySettingsExtension::enableTXInterrupt(bool enable) {\n  if (enable == _txInterrupt)\n    return;\n  _txInterrupt = enable;\n  emit modified(this);\n}\n\nRadiodditySettingsExtension::Language\nRadiodditySettingsExtension::language() const {\n  return _language;\n}\nvoid\nRadiodditySettingsExtension::setLanguage(Language lang) {\n  if (lang == _language)\n    return;\n  _language = lang;\n  emit modified(this);\n}\n\nRadioddityButtonSettingsExtension *\nRadiodditySettingsExtension::buttons() const {\n  return _buttonSettings;\n}\n\nRadioddityToneSettingsExtension *\nRadiodditySettingsExtension::tone() const {\n  return _toneSettings;\n}\n\nRadioddityBootSettingsExtension *\nRadiodditySettingsExtension::boot() const {\n  return _bootSettings;\n}\n"
  },
  {
    "path": "lib/radioddity_extensions.hh",
    "content": "#ifndef RADIODDITYEXTENSIONS_HH\n#define RADIODDITYEXTENSIONS_HH\n\n#include \"configobject.hh\"\n#include \"interval.hh\"\n#include \"level.hh\"\n\n\n/** Represents the button settings extension for all radioddity devices.\n * This object is part of the RadiodditySettingsExtension instance. */\nclass RadioddityButtonSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The long-press duration. */\n  Q_PROPERTY(Interval longPressDuration READ longPressDuration WRITE setLongPressDuration)\n  /** The short-press action for the programmable function key 1 (SK1, P1). */\n  Q_PROPERTY(Function funcKey1Short READ funcKey1Short WRITE setFuncKey1Short)\n  /** The long-press action for the programmable function key 1 (SK1, P1). */\n  Q_PROPERTY(Function funcKey1Long READ funcKey1Long WRITE setFuncKey1Long)\n  /** The short-press action for the programmable function key 2 (SK2, P2). */\n  Q_PROPERTY(Function funcKey2Short READ funcKey2Short WRITE setFuncKey2Short)\n  /** The long-press action for the programmable function key 2 (SK2, P2). */\n  Q_PROPERTY(Function funcKey2Long READ funcKey2Long WRITE setFuncKey2Long)\n  /** The short-press action for the programmable function key 3 (TK). */\n  Q_PROPERTY(Function funcKey3Short READ funcKey3Short WRITE setFuncKey3Short)\n  /** The long-press action for the programmable function key 3 (TK). */\n  Q_PROPERTY(Function funcKey3Long READ funcKey3Long WRITE setFuncKey3Long)\n\npublic:\n  /** Possible function key actions. Not all functions are present on all devices. */\n  enum class Function {\n    None, ToggleAllAlertTones, EmergencyOn, EmergencyOff, ToggleMonitor, OneTouch1,\n    OneTouch2, OneTouch3, OneTouch4, OneTouch5, OneTouch6, ToggleTalkaround, ToggleScan,\n    ToggleEncryption, ToggleVox, ZoneSelect, BatteryIndicator, ToggleLoneWorker, PhoneExit,\n    ToggleFlashLight, ToggleFMRadio, RadioEnable, RadioCheck, RadioDisable, PowerLevel, TBST,\n    CallSwell\n  };\n  Q_ENUM(Function)\n\npublic:\n  /** Default constructor. */\n  explicit RadioddityButtonSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the long-press duration, usually specified in ms. */\n  Interval longPressDuration() const;\n  /** Sets the long-press duration, usually specified in ms. */\n  void setLongPressDuration(Interval interval);\n\n  /** Returns the short-press function of the programmable function key 1 (SK1, P1). */\n  Function funcKey1Short() const;\n  /** Sets the short-press function of the programmable function key 1 (SK1, P1). */\n  void setFuncKey1Short(Function func);\n  /** Returns the long-press function of the programmable function key 1 (SK1, P1). */\n  Function funcKey1Long() const;\n  /** Sets the long-press function of the programmable function key 1 (SK1, P1). */\n  void setFuncKey1Long(Function func);\n\n  /** Returns the short-press function of the programmable function key 2 (SK2, P2). */\n  Function funcKey2Short() const;\n  /** Sets the short-press function of the programmable function key 2 (SK2, P2). */\n  void setFuncKey2Short(Function func);\n  /** Returns the long-press function of the programmable function key 2 (SK2, P2). */\n  Function funcKey2Long() const;\n  /** Sets the long-press function of the programmable function key 2 (SK2, P2). */\n  void setFuncKey2Long(Function func);\n\n  /** Returns the short-press function of the programmable function key 3 (TK). */\n  Function funcKey3Short() const;\n  /** Sets the short-press function of the programmable function key 3 (TK). */\n  void setFuncKey3Short(Function func);\n  /** Returns the long-press function of the programmable function key 3 (TK). */\n  Function funcKey3Long() const;\n  /** Sets the long-press function of the programmable function key 3 (TK). */\n  void setFuncKey3Long(Function func);\n\nprotected:\n  /** The long-press duration. */\n  Interval _longPressDuration;\n  /** The short-press action for the programmable function key 1 (SK1, P1). */\n  Function _funcKey1Short;\n  /** The long-press action for the programmable function key 1 (SK1, P1). */\n  Function _funcKey1Long;\n  /** The short-press action for the programmable function key 2 (SK2, P2). */\n  Function _funcKey2Short;\n  /** The long-press action for the programmable function key 2 (SK2, P2). */\n  Function _funcKey2Long;\n  /** The short-press action for the programmable function key 3 (TK). */\n  Function _funcKey3Short;\n  /** The long-press action for the programmable function key 3 (TK). */\n  Function _funcKey3Long;\n};\n\n\n/** Tone settings for Radioddity devices. */\nclass RadioddityToneSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** If @c true, the low battery warning is enabled. (GD-73 only) */\n  Q_PROPERTY(bool lowBatteryWarn READ lowBatteryWarn WRITE enableLowBatteryWarn)\n  /** The low-battery warn interval in seconds. */\n  Q_PROPERTY(Interval lowBatteryWarnInterval READ lowBatteryWarnInterval WRITE setLowBatteryWarnInterval)\n  /** Returns the low-battery warning volume [1,10]. (GD-73 only)*/\n  Q_PROPERTY(bool lowBatteryWarnVolume READ lowBatteryWarnVolume WRITE setLowBatteryWarnVolume)\n  /** The call-alert duration in seconds. */\n  Q_PROPERTY(Interval callAlertDuration READ callAlertDuration WRITE setCallAlertDuration)\n  /** @c true, the unknown number tone is enabled. */\n  Q_PROPERTY(bool unknownNumberTone READ unknownNumberTone WRITE enableUnknownNumberTone)\n  /** The ARTS tone mode. */\n  Q_PROPERTY(ARTSTone artsToneMode READ artsToneMode WRITE setARTSToneMode)\n  /** If @c true, the self-test tone is enabled. */\n  Q_PROPERTY(bool selftestTone READ selftestTone WRITE enableSelftestTone)\n\npublic:\n  /** Possible ARTS tone settings. */\n  enum class ARTSTone {\n    Disabled = 0,        ///< ARTS tone is disabled.\n    Once     = 4,        ///< ARTS tone once.\n    Always   = 8         ///< ARTS tone always.\n  };\n  Q_ENUM(ARTSTone)\n\npublic:\n  /** Default constructor. */\n  explicit RadioddityToneSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if a low battery charge is indicated by a warning. */\n  bool lowBatteryWarn() const;\n  /** Enables/disables low-battery warning. */\n  void enableLowBatteryWarn(bool enable);\n  /** Returns the low-battery warn interval in seconds. */\n  Interval lowBatteryWarnInterval() const;\n  /** Sets the low-battery warn interval in seconds. */\n  void setLowBatteryWarnInterval(Interval sec);\n  /** Returns the volume of the low-battery warning tone [1,10]. */\n  unsigned int lowBatteryWarnVolume() const;\n  /** Sets the volume of the low-battery warning tone [1,10]. */\n  void setLowBatteryWarnVolume(unsigned int);\n\n  /** Returns the call-alert duration in seconds. */\n  Interval callAlertDuration() const;\n  /** Sets the call-allert duration in seconds. */\n  void setCallAlertDuration(Interval sec);\n\n  /** Returns @c true if the unknown number tone is enabled. */\n  bool unknownNumberTone() const;\n  /** Enables/disables reset tone. */\n  void enableUnknownNumberTone(bool enable);\n\n  /** Returns the ARTS tone mode. */\n  ARTSTone artsToneMode() const;\n  /** Sets the ARTS tone mode. */\n  void setARTSToneMode(ARTSTone mode);\n\n  /** Returns @c true if the self-test tone is enabled. */\n  bool selftestTone() const;\n  /** Enables/disables self-test tone. */\n  void enableSelftestTone(bool enable);\n\nprotected:\n  /** If @c true, a low-battery charge is indicated by a warning. */\n  bool _lowBatteryWarn;\n  /** Holds the low-battery warn interval in seconds. */\n  Interval _lowBatteryWarnInterval;\n  /** Holds the volume of the low-battery warning tone. */\n  unsigned int _lowBatteryWarnVolume;\n  /** Holds the call alert duration in seconds. */\n  Interval _callAlertDuration;\n  /** If @c true, the unknown number tone is enabled. */\n  bool _unknownNumberTone;\n  /** Holds the ARTS tone mode. */\n  ARTSTone _artsToneMode;\n  /** If @c true, the self-test tone is enabled. */\n  bool _selftestTone;\n};\n\n/** Represents the boot settings for Radioddity devices.\n * This settings extension is part of the RadiodditySettingsExtension. */\nclass RadioddityBootSettingsExtension: public ConfigItem\n{\n  Q_OBJECT\n\n  /** The programming password, disabled if empty. */\n  Q_PROPERTY(QString progPassword READ progPassword WRITE setProgPassword)\n\npublic:\n  /** Default constructor. */\n  explicit RadioddityBootSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the programming password. */\n  const QString &progPassword() const;\n  /** Sets the programming password. */\n  void setProgPassword(const QString &pwd);\n\nprotected:\n  /** Holds the programming password, disabled if empty. */\n  QString _progPasswd;\n};\n\n\n/** Represents the general settings extension for Radioddity devices.\n * @ingroup radioddity */\nclass RadiodditySettingsExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The monitor type. */\n  Q_PROPERTY(MonitorType monitorType READ monitorType WRITE setMonitorType)\n  /** The lone-worker response time in minutes. */\n  Q_PROPERTY(Interval loneWorkerResponseTime READ loneWorkerResponseTime WRITE setLoneWorkerResponseTime)\n  /** The lonw-worker reminder period in seconds. */\n  Q_PROPERTY(Interval loneWorkerReminderPeriod READ loneWorkerReminderPeriod WRITE setLoneWorkerReminderPeriod)\n  /** If @c true the down-channel mode is VFO. */\n  Q_PROPERTY(bool downChannelModeVFO READ downChannelModeVFO WRITE enableDownChannelModeVFO)\n  /** If @c true the up-channel mode is VFO. */\n  Q_PROPERTY(bool upChannelModeVFO READ upChannelModeVFO WRITE enableUpChannelModeVFO)\n  /** If @c true, the power save mode is enabled. */\n  Q_PROPERTY(bool powerSaveMode READ powerSaveMode WRITE enablePowerSaveMode)\n  Q_CLASSINFO(\"powerSaveModeDescription\", \"Puts the radio into sleep-mode when idle.\")\n  Q_CLASSINFO(\"powerSaveModeLongDescription\",\n              \"When enabled, the radio enters a sleep mode when idle. That is, when on receive and \"\n              \"there is no activity on the current channel. However, the radio may need some time \"\n              \"to wake up from this mode. Hence, the 'wakeupPreamble' need to be enabled by all \"\n              \"radios in the network to provide this wake-up delay.\")\n  /** If @c true, a wakeup preamble is sent. */\n  Q_PROPERTY(bool wakeupPreamble READ wakeupPreamble WRITE enableWakeupPreamble)\n  /** The delay, before the idle radio enters power save mode (if enabled). */\n  Q_PROPERTY(Interval powerSaveDelay READ powerSaveDelay WRITE setPowerSaveDelay)\n  /** If @c true, all LEDs are disabled. */\n  Q_PROPERTY(bool allLEDsDisabled READ allLEDsDisabled WRITE disableAllLEDs)\n  /** If @c true, the quick-key override is inhibited. */\n  Q_PROPERTY(bool quickKeyOverrideInhibited READ quickKeyOverrideInhibited WRITE inhibitQuickKeyOverride)\n  /** If @c true, the radio will transmit on the active channel when double-wait is enabled. */\n  Q_PROPERTY(bool txOnActiveChannel READ txOnActiveChannel WRITE enableTXOnActiveChannel)\n  /** The scan mode. */\n  Q_PROPERTY(ScanMode scanMode READ scanMode WRITE setScanMode)\n  /** The repeater end delay in seconds. */\n  Q_PROPERTY(Interval repeaterEndDelay READ repeaterEndDelay WRITE setRepeaterEndDelay)\n  /** The repeater STE in seconds. */\n  Q_PROPERTY(Interval repeaterSTE READ repeaterSTE WRITE setRepeaterSTE)\n  /** Returns @c true, if the TX interrupt is enabled. */\n  Q_PROPERTY(bool txInterrupt READ txInterrupt WRITE enableTXInterrupt)\n  /** UI language. */\n  Q_PROPERTY(Language language READ language WRITE setLanguage)\n\n  /** The button settings. */\n  Q_PROPERTY(RadioddityButtonSettingsExtension *buttons READ buttons)\n  /** The tone settings. */\n  Q_PROPERTY(RadioddityToneSettingsExtension *tone READ tone)\n  /** The boot settings. */\n  Q_PROPERTY(RadioddityBootSettingsExtension *boot READ boot)\n\npublic:\n  /** Possible monitor types. */\n  enum class MonitorType {\n    Open = 0,            ///< Monitoring by opening the squelch.\n    Silent = 1           ///< Silent monitoring.\n  };\n  Q_ENUM(MonitorType)\n\n  /** Possible scan modes. */\n  enum class ScanMode {\n    Time    = 0,\n    Carrier = 1,\n    Search  = 2\n  };\n  Q_ENUM(ScanMode)\n\n  /** Possible UI languages. */\n  enum class Language {\n    Chinese, English\n  };\n  Q_ENUM(Language)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit RadiodditySettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the monitor type. */\n  MonitorType monitorType() const;\n  /** Sets the monitor type. */\n  void setMonitorType(MonitorType type);\n\n  /** Returns the lone-worker response time in minutes. */\n  Interval loneWorkerResponseTime() const;\n  /** Sets the lone-worker response time in minutes. */\n  void setLoneWorkerResponseTime(Interval min);\n  /** Returns the lone-worker reminder period in seconds. */\n  Interval loneWorkerReminderPeriod() const;\n  /** Sets the lone-worker reminder period in seconds. */\n  void setLoneWorkerReminderPeriod(Interval sec);\n\n  /** Returns @c true if the down-channel mode is VFO. */\n  bool downChannelModeVFO() const;\n  /** Enables/disables down-channel mode is VFO. */\n  void enableDownChannelModeVFO(bool enable);\n  /** Returns @c true if the up-channel mode is VFO. */\n  bool upChannelModeVFO() const;\n  /** Enables/disables up-channel mode is VFO. */\n  void enableUpChannelModeVFO(bool enable);\n\n  /** Returns @c true if the power save mode is enabled. */\n  bool powerSaveMode() const;\n  /** Enables the power save mode. */\n  void enablePowerSaveMode(bool enable);\n  /** Returns @c true if the wake-up preamble is sent. */\n  bool wakeupPreamble() const;\n  /** Enables transmission of wakeup preamble. */\n  void enableWakeupPreamble(bool enable);\n  /** Returns the delay, before an idle radio enters power save mode. */\n  Interval powerSaveDelay() const;\n  /** Sets the delay before an idle radio enters power save mode. */\n  void setPowerSaveDelay(Interval interv);\n\n  /** Returns @c true if all LEDs are disabled. */\n  bool allLEDsDisabled() const;\n  /** Disables/enables all LEDs. */\n  void disableAllLEDs(bool disable);\n\n  /** Returns true if quick-key override is inhibited. */\n  bool quickKeyOverrideInhibited() const;\n  /** Inhibits quick-key override. */\n  void inhibitQuickKeyOverride(bool inhibit);\n\n  /** Returns @c true if the radio transmits on the active channel on double monitor. */\n  bool txOnActiveChannel() const;\n  /** Enables/disables transmission on active channel on double monitor. */\n  void enableTXOnActiveChannel(bool enable);\n\n  /** Returns the scan mode. */\n  ScanMode scanMode() const;\n  /** Sets the scan mode. */\n  void setScanMode(ScanMode mode);\n\n  /** Returns the repeater end delay in seconds. */\n  Interval repeaterEndDelay() const;\n  /** Sets the repeater end delay in seconds. */\n  void setRepeaterEndDelay(Interval delay);\n  /** Returns the repeater STE in seconds. */\n  Interval repeaterSTE() const;\n  /** Sets the repeater STE in seconds. */\n  void setRepeaterSTE(Interval ste);\n\n  /** Returns @c true if the TX interrupt is enabled. */\n  bool txInterrupt() const;\n  /** Enables TX interrupt. */\n  void enableTXInterrupt(bool enable);\n\n  /** Returns the UI language. */\n  Language language() const;\n  /** Sets the language. */\n  void setLanguage(Language lang);\n\n  /** Returns a weak reference to the button settings. */\n  RadioddityButtonSettingsExtension *buttons() const;\n  /** Returns a weak reference to the tone settings. */\n  RadioddityToneSettingsExtension *tone() const;\n  /** Returns a weak reference to the boot settings. */\n  RadioddityBootSettingsExtension *boot() const;\n\nprotected:\n  /** Holds the monitor type. */\n  MonitorType _monitorType;\n  /** Holds the lone-worker response time in minutes. */\n  Interval _loneWorkerResponseTime;\n  /** Holds the lone-worker reminder period in seconds. */\n  Interval _loneWorkerReminderPeriod;\n  /** If @c true down-channel mode is VFO. */\n  bool _downChannelModeVFO;\n  /** If @c true the up-channel mode is VFO. */\n  bool _upChannelModeVFO;\n  /** If @c true, the power save mode is enabled. */\n  bool _powerSaveMode;\n  /** If @c true, the wake-up preamble is sent. */\n  bool _wakeupPreamble;\n  /** Delay before an idle radio enters the power save mode. */\n  Interval _powerSaveDelay;\n  /** If @c true, all LEDs are disabled. */\n  bool _disableAllLEDs;\n  /** If @c true, the quick-key override is inhibited. */\n  bool _quickKeyOverrideInhibited;\n  /** If @c true, the radio will transmit on the active channel when double-wait is enabled. */\n  bool _txOnActiveChannel;\n  /** Holds the scan mode. */\n  ScanMode _scanMode;\n  /** Holds the repeater end delay in seconds. */\n  Interval _repeaterEndDelay;\n  /** Holds the repeater STE in seconds. */\n  Interval _repeaterSTE;\n  /** If @c true, TX interrupt is enabled. */\n  bool _txInterrupt;\n  /** UI language. */\n  Language _language;\n  /** Button settings. */\n  RadioddityButtonSettingsExtension *_buttonSettings;\n  /** Tone settings. */\n  RadioddityToneSettingsExtension *_toneSettings;\n  /** Boot settings. */\n  RadioddityBootSettingsExtension *_bootSettings;\n};\n\n#endif // RADIODDITYEXTENSIONS_HH\n"
  },
  {
    "path": "lib/radioddity_interface.cc",
    "content": "#include \"radioddity_interface.hh\"\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include \"logger.hh\"\n\n#define USB_VID 0x15a2\n#define USB_PID 0x0073\n#define MAX_RETRY 10\n\nstatic const unsigned char CMD_PRG[]   = \"\\2PROGRA\";\nstatic const unsigned char CMD_PRG2[]  = \"M\\2\";\nstatic const unsigned char CMD_ACK[]   = \"A\";\nstatic const unsigned char CMD_READ[]  = \"Raan\";\nstatic const unsigned char CMD_WRITE[] = \"Waan...\";\nstatic const unsigned char CMD_ENDR[]  = \"ENDR\";\nstatic const unsigned char CMD_ENDW[]  = \"ENDW\";\nstatic const unsigned char CMD_CWB0[]  = \"CWB\\4\\0\\0\\0\\0\";\nstatic const unsigned char CMD_CWB1[]  = \"CWB\\4\\0\\1\\0\\0\";\nstatic const unsigned char CMD_CWB3[]  = \"CWB\\4\\0\\3\\0\\0\";\nstatic const unsigned char CMD_CWB4[]  = \"CWB\\4\\0\\4\\0\\0\";\n\nRadioddityInterface::RadioddityInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : HIDevice(descr, err, parent), _current_bank(MEMBANK_NONE), _identifier()\n{\n  if (isOpen())\n    identifier();\n}\n\nRadioddityInterface::~RadioddityInterface() {\n  if (isOpen())\n    close();\n}\n\nUSBDeviceInfo\nRadioddityInterface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::HID, USB_VID, USB_PID);\n}\n\nQList<USBDeviceDescriptor>\nRadioddityInterface::detect(bool saveOnly) {\n  Q_UNUSED(saveOnly)\n  return HIDevice::detect(USB_VID, USB_PID);\n}\n\nbool\nRadioddityInterface::isOpen() const {\n  return HIDevice::isOpen();\n}\n\nvoid\nRadioddityInterface::close() {\n  logDebug() << \"Close HID connection.\";\n  _identifier = RadioInfo();\n  HIDevice::close();\n}\n\nRadioInfo\nRadioddityInterface::identifier(const ErrorStack &err) {\n  static unsigned char reply[38];\n  unsigned char ack;\n\n  if (_identifier.isValid())\n    return _identifier;\n\n  logDebug() << \"Radioddity HID interface: Enter program mode.\";\n\n  if (! hid_send_recv(CMD_PRG, 7, &ack, 1, err)) {\n    errMsg(err) << \"Cannot identify radio.\";\n    return RadioInfo();\n  }\n\n  if (ack != CMD_ACK[0]) {\n    errMsg(err) << \"Cannot identify radio: Wrong PRD acknowledge \" << (int)ack <<\n                \", expected \"<< int(CMD_ACK[0]) << \".\";\n    return RadioInfo();\n  }\n\n  if (! hid_send_recv(CMD_PRG2, 2, reply, 16, err)) {\n    errMsg(err) << \"Cannot identify radio.\";\n    return RadioInfo();\n  }\n\n  if (! hid_send_recv(CMD_ACK, 1, &ack, 1, err)) {\n    errMsg(err) << \"Cannot identify radio.\";\n    return RadioInfo();\n  }\n\n  if (ack != CMD_ACK[0]) {\n    errMsg(err) << \"Cannot identify radio: Wrong PRG2 acknowledge \"\n                << (int) ack << \", expected \" << (int)CMD_ACK[0] << \".\";\n    return RadioInfo();\n  }\n\n  // Reply:\n  // 42 46 2d 35 52 ff ff ff 56 32 31 30 00 04 80 04\n  //  B  F  -  5  R           V  2  1  0\n\n  // Terminate the string.\n  char *p = (char *)memchr(reply, 0xff, sizeof(reply));\n  if (p)\n    *p = 0;\n\n  if (0 == strcmp((char*)reply, \"BF-5R\")) {\n    _identifier = RadioInfo::byID(RadioInfo::RD5R);\n  } else if (0 == strcmp((char*)reply, \"MD-760P\")) {\n    _identifier = RadioInfo::byID(RadioInfo::GD77);\n  } else {\n    errMsg(err) << \"Unknown Radioddity device '\" << (char*)reply << \"'.\";\n    return RadioInfo();\n  }\n\n  logDebug() << \"Got device '\" << _identifier.name() << \"'.\";\n  return _identifier;\n}\n\n\nbool\nRadioddityInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(addr)\n\n  if (! selectMemoryBank(MemoryBank(bank), err)) {\n    errMsg(err) << \"Cannot select memory bank \" << bank << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nRadioddityInterface::read(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err)\n{\n  unsigned char cmd[4], reply[32+4];\n  int n;\n\n  if (! selectMemoryBank(MemoryBank(bank), err)) {\n    errMsg(err) << \"Cannot select memory bank \" << bank << \".\";\n    return false;\n  }\n\n  // send data\n  for (n=0; n<nbytes; n+=32) {\n    cmd[0] = CMD_READ[0];\n    cmd[1] = (addr + n) >> 8;\n    cmd[2] = addr + n;\n    cmd[3] = 32;\n    if (! hid_send_recv(cmd, 4, reply, sizeof(reply), err))\n      return false;\n    else\n      memcpy(data + n, reply + 4, 32);\n  }\n\n  return true;\n}\n\nbool\nRadioddityInterface::read_finish(const ErrorStack &err)\n{\n  unsigned char ack;\n\n  if (! hid_send_recv(CMD_ENDR, 4, &ack, 1, err)) {\n    errMsg(err) << \"Cannot finish read().\";\n    return false;\n  }\n  if (ack != CMD_ACK[0]) {\n    errMsg(err) << \"Cannot finish read(): Wrong acknowledge \"\n                << (int)ack << \", expected \" << (int)CMD_ACK[0] << \".\";\n    return false;\n  }\n\n  logDebug() << \"Left program mode.\";\n\n  _identifier = RadioInfo();\n\n  return true;\n}\n\n\nbool\nRadioddityInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(addr)\n\n  if (! selectMemoryBank(MemoryBank(bank), err)) {\n    errMsg(err) << \"Cannot select memory bank \" << bank << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nRadioddityInterface::write(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err)\n{\n  unsigned char ack, cmd[4+32];\n\n  if (! selectMemoryBank(MemoryBank(bank), err)) {\n    errMsg(err) << \"Cannot select memory bank \" << bank << \".\";\n    return false;\n  }\n\n  // send data\n  unsigned int count=0;\n  for (int n=0; n<nbytes; n+=32) {\n    cmd[0] = CMD_WRITE[0];\n    cmd[1] = (addr + n) >> 8;\n    cmd[2] = addr + n;\n    cmd[3] = 32;\n    memcpy(cmd + 4, data + n, 32);\n    if (! hid_send_recv(cmd, 4+32, &ack, 1, err))\n      return false;\n    else if (ack != CMD_ACK[0]) {\n      errMsg(err) << \"Cannot write block: Wrong acknowledge \" << (int)ack\n                  << \", expected \" << (int)CMD_ACK[0] << \".\";\n      n-=32;\n\n      if ((++count) > MAX_RETRY) {\n        errMsg(err) << \"Maximum retry count reached. Abort.\";\n        return false;\n      }\n    } else {\n      count = 0;\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioddityInterface::write_finish(const ErrorStack &err)\n{\n  unsigned char ack;\n\n  if (! hid_send_recv(CMD_ENDW, 4, &ack, 1, err)) {\n    errMsg(err) << \"Cannot finish write().\";\n    return false;\n  }\n  if (ack != CMD_ACK[0]) {\n    errMsg(err) << \"Cannot finish write(): Wrong acknowledge \"\n                << (int)ack << \", expected \" << (int)CMD_ACK[0] << \".\";\n    return false;\n  }\n\n  logDebug() << \"Left program mode.\";\n  _identifier = RadioInfo();\n\n  return true;\n}\n\nbool\nRadioddityInterface::selectMemoryBank(MemoryBank bank, const ErrorStack &err) {\n  unsigned char ack;\n  const uint8_t *cmd = nullptr;\n\n  if (_current_bank == bank)\n    return true;\n\n  // Select command by memory bank\n  switch (bank) {\n  case MEMBANK_CODEPLUG_LOWER : cmd = CMD_CWB0; break;\n  case MEMBANK_CODEPLUG_UPPER : cmd = CMD_CWB1; break;\n  case MEMBANK_CALLSIGN_LOWER : cmd = CMD_CWB3; break;\n  case MEMBANK_CALLSIGN_UPPER : cmd = CMD_CWB4; break;\n  default:\n    errMsg(err) << \"Cannot set memory bank: Unknown bank \" << bank << \".\";\n    return false;\n  }\n\n  logDebug() << \"Selecting memory bank \" << bank << \"...\";\n\n  // select memory bank\n  if (! hid_send_recv(cmd, 8, &ack, 1, err)) {\n    errMsg(err) << \"Cannot send memory bank select command.\";\n    return false;\n  }\n  if (ack != CMD_ACK[0]) {\n    errMsg(err) << \"Cannot select memory bank: Wrong acknowledge \"\n                << (int)ack << \", expected \" << (int)CMD_ACK[0] << \".\";\n    return false;\n  }\n\n  logDebug() << \"Memory bank \" << bank << \" selected.\";\n  _current_bank = bank;\n\n  return true;\n}\n"
  },
  {
    "path": "lib/radioddity_interface.hh",
    "content": "#ifndef RADIODDITY_INTERFACE_HH\n#define RADIODDITY_INTERFACE_HH\n\n#include <QtGlobal>\n#include <QObject>\n#include \"radiointerface.hh\"\n\n#ifdef Q_OS_MACOS\n#include \"hid_macos.hh\"\n#else\n#include \"hid_libusb.hh\"\n#endif\n\n/** Implements a radio interface for radios using the HID USB schema (i.e. Radioddity devices).\n *\n * @ingroup radioddity */\nclass RadioddityInterface: public HIDevice, public RadioInterface\n{\n\tQ_OBJECT\n\npublic:\n  /** Possible memory banks to select. */\n  enum MemoryBank {\n    MEMBANK_NONE           = -1,    ///< No bank selected.\n    MEMBANK_CODEPLUG_LOWER =  0,    ///< Lower memory bank (EEPROM).\n    MEMBANK_CODEPLUG_UPPER =  1,    ///< Upper memory bank (FLASH).\n    MEMBANK_CALLSIGN_LOWER =  3,    ///< Callsign DB memory lower bank (also FLASH).\n    MEMBANK_CALLSIGN_UPPER =  4     ///< Callsign DB memory upper bank (also FLASH).\n  };\n\npublic:\n  /** Connects to the radio via the given descriptor. */\n  explicit RadioddityInterface(const USBDeviceDescriptor &descr, const ErrorStack &err=ErrorStack(), QObject *parent = nullptr);\n  /** Destructor. */\n  virtual ~RadioddityInterface();\n\n  /** Returns @c true if the connection was established. */\n\tbool isOpen() const;\n\n  void close();\n\n  /** Returns radio identifier string. */\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n\n  /** Reads a block of data from the device at the given block number.\n   * @param bank The memory bank to read from.\n   * @param addr The address to read from within the memory bank.\n   * @param data Pointer to memory where the read data is stored.\n   * @param nbytes The number of bytes to read.\n   * @param err The error stack, messages are put onto.\n   * @returns @c true on success. */\n  bool read(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err=ErrorStack());\n\n  bool read_finish(const ErrorStack &err=ErrorStack());\n\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n\n  /** Writes a block of data to the device at the given block number.\n   * @param bank The memory bank to read from.\n   * @param addr The address to read from within the memory bank.\n   * @param data Pointer to memory where the read data is stored.\n   * @param nbytes The number of bytes to read.\n   * @param err The error stack, messages are put onto.\n   * @returns @c true on success. */\n  bool write(uint32_t bank, uint32_t addr, unsigned char *data, int nbytes, const ErrorStack &err=ErrorStack());\n\n  bool write_finish(const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about the interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected AnyTone radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Internal used function to select a memory bank. */\n  bool selectMemoryBank(MemoryBank bank, const ErrorStack &err=ErrorStack());\n\nprivate:\n  /** The currently selected memory bank. */\n  MemoryBank _current_bank;\n  /** Identifier received when entering the prog mode. */\n  RadioInfo _identifier;\n};\n\n#endif // RADIODDITY_INTERFACE_HH\n"
  },
  {
    "path": "lib/radioddity_radio.cc",
    "content": "#include \"radioddity_radio.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n\n#define BSIZE           32\n\n\nRadioddityRadio::RadioddityRadio(RadioddityInterface *device, QObject *parent)\n  : Radio(parent), _dev(device), _codeplugFlags(), _config(nullptr)\n{\n  // pass...\n}\n\nRadioddityRadio::~RadioddityRadio() {\n  if (_dev && _dev->isOpen()) {\n    logDebug() << \"Reboot and close connection to radio.\";\n    _dev->reboot();\n    _dev->close();\n  }\n  if (_dev) {\n    _dev->deleteLater();\n    _dev = nullptr;\n  }\n}\n\nbool\nRadioddityRadio::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  start();\n  return true;\n}\n\nbool\nRadioddityRadio::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (_config)\n    delete _config;\n\n  if (! (_config = config))\n    return false;\n  _config->setParent(this);\n\n  _task = StatusUpload;\n  _codeplugFlags = flags;\n  if (flags.blocking()) {\n    this->run();\n    return (StatusIdle == _task);\n  }\n\n  _errorStack = err;\n  this->start();\n  return true;\n}\n\nbool\nRadioddityRadio::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(db);\n  Q_UNUSED(selection);\n\n  errMsg(err) << \"Radio does not support a callsign DB.\";\n\n  return false;\n}\n\n\nbool\nRadioddityRadio::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(flags);\n\n  errMsg(err) << \"Satellite config upload is not implemented yet.\";\n\n  return false;\n}\n\n\nvoid\nRadioddityRadio::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    if (! download()) {\n      _dev->read_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit downloadFinished(this, &codeplug());\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! upload()) {\n      _dev->write_finish();\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n    _dev->write_finish();\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if(! uploadCallsigns()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit uploadComplete(this);\n  }\n}\n\nbool\nRadioddityRadio::download() {\n  emit downloadStarted();\n\n  unsigned btot = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    btot += codeplug().image(0).element(n).data().size()/BSIZE;\n  }\n\n  unsigned bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    int b0 = codeplug().image(0).element(n).address()/BSIZE;\n    int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n    for (int i=0; i<nb; i++, bcount++) {\n      // Select bank by addr\n      uint32_t addr = (b0+i)*BSIZE;\n      RadioddityInterface::MemoryBank bank = (\n            (0x10000 > addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER );\n      // read\n      if (! _dev->read(bank, (b0+i)*BSIZE, codeplug().data((b0+i)*BSIZE), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot download codeplug.\";\n        return false;\n      }\n      emit downloadProgress(float(bcount*100)/btot);\n    }\n  }\n\n  _dev->read_finish(_errorStack);\n  return true;\n}\n\nbool\nRadioddityRadio::upload() {\n  emit uploadStarted();\n\n  unsigned btot = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    btot += codeplug().image(0).element(n).data().size()/BSIZE;\n  }\n\n  unsigned bcount = 0;\n  if (_codeplugFlags.updateCodeplug()) {\n    // If codeplug gets updated, download codeplug from device first:\n    for (int n=0; n<codeplug().image(0).numElements(); n++) {\n      int b0 = codeplug().image(0).element(n).address()/BSIZE;\n      int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n      for (int i=0; i<nb; i++, bcount++) {\n        // Select bank by addr\n        uint32_t addr = (b0+i)*BSIZE;\n        RadioddityInterface::MemoryBank bank = (\n              (0x10000 > addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER );\n        // read\n        if (! _dev->read(bank, addr, codeplug().data(addr), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot upload codeplug.\";\n          return false;\n        }\n        emit uploadProgress(float(bcount*50)/btot);\n      }\n    }\n  }\n\n  // Encode config into codeplug\n  if (! codeplug().encode(_config, _codeplugFlags, _errorStack)) {\n    errMsg(_errorStack) << \"Codeplug upload failed.\";\n    return false;\n  }\n\n  // then, upload modified codeplug\n  bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    int b0 = codeplug().image(0).element(n).address()/BSIZE;\n    int nb = codeplug().image(0).element(n).data().size()/BSIZE;\n    for (int i=0; i<nb; i++, bcount++) {\n      // Select bank by addr\n      uint32_t addr = (b0+i)*BSIZE;\n      RadioddityInterface::MemoryBank bank = (\n            (0x10000 > addr) ? RadioddityInterface::MEMBANK_CODEPLUG_LOWER : RadioddityInterface::MEMBANK_CODEPLUG_UPPER );\n      // write block\n      if (! _dev->write(bank, addr, codeplug().data(addr), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot upload codeplug.\";\n        return false;\n      }\n      emit uploadProgress(50+float(bcount*50)/btot);\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioddityRadio::uploadCallsigns() {\n  return false;\n}\n"
  },
  {
    "path": "lib/radioddity_radio.hh",
    "content": "/** @defgroup radioddity Radioddity radios\n * Abstract classes for Radioddity radios.\n *\n * @ingroup dsc */\n#ifndef RADIODDITY_RADIO_HH\n#define RADIODDITY_RADIO_HH\n\n#include \"radio.hh\"\n#include \"radioddity_interface.hh\"\n\n/** Base class for all Radioddity radios.\n *\n * @ingroup radioddity */\nclass RadioddityRadio: public Radio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit RadioddityRadio(RadioddityInterface *device=nullptr, QObject *parent=nullptr);\n\n  virtual ~RadioddityRadio();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, const Codeplug::Flags &flags = Codeplug::Flags(), const ErrorStack &err=ErrorStack());\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(), const ErrorStack &err=ErrorStack());\n\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err);\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n\tvoid run();\n\nprivate:\n  virtual bool download();\n  virtual bool upload();\n  virtual bool uploadCallsigns();\n\nprotected:\n  /** The interface to the radio. */\n  RadioddityInterface *_dev;\n  /** Holds the flags to control assembly and upload of code-plugs. */\n  Codeplug::Flags _codeplugFlags;\n  /** The generic configuration. */\n\tConfig *_config;\n  /** A weak reference to the user-database. */\n  UserDatabase *_userDB;\n};\n\n#endif // RADIODDITY_RADIO_HH\n"
  },
  {
    "path": "lib/radioid.cc",
    "content": "#include \"radioid.hh\"\n#include \"utils.hh\"\n#include \"contact.hh\"\n#include \"config.hh\"\n#include \"radiosettings.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioID\n * ********************************************************************************************* */\nRadioID::RadioID(QObject *parent)\n  : ConfigObject(parent)\n{\n  // pass...\n}\n\nRadioID::RadioID(const QString &name, QObject *parent)\n  : ConfigObject(name, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRRadioID\n * ********************************************************************************************* */\nDMRRadioID::DMRRadioID(QObject *parent)\n  : RadioID(parent), _number(0)\n{\n  // pass...\n}\n\nDMRRadioID::DMRRadioID(const QString &name, uint32_t id, QObject *parent)\n  : RadioID(name, parent), _number(id)\n{\n  // pass...\n}\n\nConfigItem *\nDMRRadioID::clone() const {\n  DMRRadioID *id = new DMRRadioID();\n  if (! id->copy(*this)) {\n    id->deleteLater();\n    return nullptr;\n  }\n  return id;\n}\n\nuint32_t\nDMRRadioID::number() const {\n  return _number;\n}\n\nvoid\nDMRRadioID::setNumber(uint32_t id) {\n  if (id == _number)\n    return;\n  _number = id;\n  emit modified(this);\n}\n\nYAML::Node\nDMRRadioID::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = RadioID::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  type[\"dmr\"] = node;\n\n  return type;\n}\n\nbool\nDMRRadioID::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse radio id: Expected object with one child.\";\n    return false;\n  }\n\n  return ConfigObject::parse(node.begin()->second, ctx, err);\n}\n\nbool\nDMRRadioID::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot link radio id: Expected object with one child.\";\n    return false;\n  }\n\n  return ConfigObject::link(node.begin()->second, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DefaultRadioID\n * ********************************************************************************************* */\nDefaultRadioID *DefaultRadioID::_instance = nullptr;\n\nDefaultRadioID::DefaultRadioID(QObject *parent)\n  : DMRRadioID(tr(\"[Default]\"),0,parent)\n{\n  // pass...\n}\n\nDefaultRadioID *\nDefaultRadioID::get() {\n  if (nullptr == _instance)\n    _instance = new DefaultRadioID();\n  return _instance;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of M17RadioID\n * ********************************************************************************************* */\nM17RadioID::M17RadioID(QObject *parent)\n  : RadioID(parent), _call()\n{\n  // pass...\n}\n\nM17RadioID::M17RadioID(const QString &name, const QString &call, QObject *parent)\n  : RadioID(name, parent), _call()\n{\n  setCall(call);\n}\n\nConfigItem *\nM17RadioID::clone() const {\n  M17RadioID *id = new M17RadioID();\n  if (! id->copy(*this)) {\n    id->deleteLater();\n    return nullptr;\n  }\n  return id;\n}\n\nconst QString &\nM17RadioID::call() const {\n  return _call;\n}\n\nvoid\nM17RadioID::setCall(const QString &call) {\n  if (call == _call)\n    return;\n  _call = M17Contact::normalizeCall(call);\n  emit modified(this);\n}\n\nYAML::Node\nM17RadioID::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = RadioID::serialize(context, err);\n  if (node.IsNull())\n    return node;\n\n  YAML::Node type;\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  type[\"m17\"] = node;\n\n  return type;\n}\n\nbool\nM17RadioID::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse M17 radio id: Expected object with one child.\";\n    return false;\n  }\n\n  return ConfigObject::parse(node.begin()->second, ctx, err);\n}\n\nbool\nM17RadioID::link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot link M17 radio id: Expected object with one child.\";\n    return false;\n  }\n\n  return ConfigObject::link(node.begin()->second, ctx, err);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DTMFRadioID\n * ********************************************************************************************* */\nDTMFRadioID::DTMFRadioID(QObject *parent)\n  : RadioID(parent)\n{\n  // pass...\n}\n\nDTMFRadioID::DTMFRadioID(const QString &name, const QString &number, QObject *parent)\n  : RadioID(name, parent), _number()\n{\n  setNumber(number.simplified());\n}\n\nConfigItem *\nDTMFRadioID::clone() const {\n  DTMFRadioID *newId = new DTMFRadioID();\n  if (! newId->copy(*this)) {\n    newId->deleteLater();\n    return nullptr;\n  }\n  return newId;\n}\n\nconst QString &\nDTMFRadioID::number() const {\n  return _number;\n}\nvoid\nDTMFRadioID::setNumber(const QString &number) {\n  if (! validDTMFNumber(number))\n    return;\n  _number = number.simplified();\n  emit modified(this);\n  return;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioIDList\n * ********************************************************************************************* */\nRadioIDList::RadioIDList(QObject *parent)\n  : ConfigObjectList(DMRRadioID::staticMetaObject, parent)\n{\n  // pass...\n}\n\nvoid\nRadioIDList::clear() {\n  ConfigObjectList::clear();\n}\n\nDMRRadioID *\nRadioIDList::getId(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<DMRRadioID>();\n  return nullptr;\n}\n\nDMRRadioID *\nRadioIDList::find(uint32_t id) const {\n  for (int i=0; i<count(); i++) {\n    if (get(i)->is<DMRRadioID>() && (id == get(i)->as<DMRRadioID>()->number()))\n      return get(i)->as<DMRRadioID>();\n  }\n  return nullptr;\n}\n\nint\nRadioIDList::add(ConfigObject *obj, int row, bool unique) {\n  if ((nullptr == obj) || (! obj->is<DMRRadioID>()))\n    return -1;\n\n  int idx = ConfigObjectList::add(obj, row, unique);\n  if (parent() && obj->is<DMRRadioID>() && qobject_cast<Config*>(parent())->settings()->defaultIdRef()->isNull())\n    qobject_cast<Config*>(parent())->settings()->setDefaultId(obj->as<DMRRadioID>());\n\n  return idx;\n}\n\nint\nRadioIDList::addId(const QString &name, uint32_t id) {\n  return add(new DMRRadioID(name, id, this));\n}\n\nbool\nRadioIDList::delId(uint32_t id) {\n  return del(find(id));\n}\n\n\nConfigItem *\nRadioIDList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node)\n    return nullptr;\n\n  if ((! node.IsMap()) || (1 != node.size())) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create radio id: Expected object with one child.\";\n    return nullptr;\n  }\n\n  QString type = QString::fromStdString(node.begin()->first.as<std::string>());\n  if (\"dmr\" == type) {\n    return new DMRRadioID();\n  } else if (\"m17\" == type) {\n    return new M17RadioID();\n  } else if (\"dtmf\" == type) {\n    return new DTMFRadioID();\n  }\n\n  errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n              << \": Cannot create radio id: Unknown type '\" << type << \"'.\";\n\n  return nullptr;\n}\n\n"
  },
  {
    "path": "lib/radioid.hh",
    "content": "#ifndef RADIOID_HH\n#define RADIOID_HH\n\n#include \"configobject.hh\"\n\nclass DMRRadioIDReference;\n\n\n/** Abstract base class for all radio IDs.\n *\n * That is, DMR radio IDs as well as M17, DTMF, ZVEI, 5-tone etc PTT-IDs.\n *\n * @ingroup conf */\nclass RadioID: public ConfigObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden default constructor.\n   * Use one of the derived classes to instantiate radio IDs. */\n  explicit RadioID(QObject *parent=nullptr);\n  /** Hidden constructor with name. */\n  RadioID(const QString &name, QObject *parent=nullptr);\n};\n\n\n/** Represents a DMR radio ID within the abstract config.\n *\n * This class is used to store the DMR ID(s) of the radio.\n *\n * @ingroup conf */\nclass DMRRadioID : public RadioID\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"id\")\n\n  /** The number of the radio ID. */\n  Q_PROPERTY(unsigned number READ number WRITE setNumber)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit DMRRadioID(QObject *parent=nullptr);\n\n  /** Constructor.\n   * @param name Specifies the name of the ID.\n   * @param number Specifies the DMR ID.\n   * @param parent Specifies the parent QObject owning this object. */\n  DMRRadioID(const QString &name, uint32_t number, QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the DMR ID. */\n  uint32_t number() const;\n  /** Sets the DMR ID. */\n  void setNumber(uint32_t number);\n\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the DMR ID. */\n  uint32_t _number;\n};\n\n\n/** A singleton radio ID representing the default DMR radio ID within the abstract config.\n * @ingroup conf */\nclass DefaultRadioID: public DMRRadioID\n{\n  Q_OBJECT\n\nprotected:\n  /** Constructor. */\n  explicit DefaultRadioID(QObject *parent=nullptr);\n\npublic:\n  /** Factory method returning the singleton instance. */\n  static DefaultRadioID *get();\n\nprivate:\n  /** The singleton instance. */\n  static DefaultRadioID *_instance;\n};\n\n\n/** Implements the M17 radio ID.\n * @ingroup conf */\nclass M17RadioID: public RadioID\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"id\")\n\n  /** The callsign of the radio ID. */\n  Q_PROPERTY(QString call READ call WRITE setCall)\n\npublic:\n  /** Default constructor. */\n  explicit M17RadioID(QObject *parent=nullptr);\n\n  /** Constructor.\n   * @param name Specifies the name of the ID.\n   * @param call Specifies the M17 callsign/ID.\n   * @param parent Specifies the parent QObject owning this object. */\n  M17RadioID(const QString &name, const QString &call, QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the M17 call/ID. */\n  const QString &call() const;\n  /** Sets the M17 call/ID. */\n  void setCall(const QString &call);\n\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n  bool parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n  bool link(const YAML::Node &node, const ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Holds the M17 call/ID. */\n  QString _call;\n};\n\n\n/** Represents a DTMF radio ID as used for PTT-ID on analog channels.\n *\n * This class just holds the name and DTMF number of the ID.\n * @ingroup conf */\nclass DTMFRadioID: public RadioID\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"dtmf\")\n\n  /** The DTMF number of the radio ID. */\n  Q_PROPERTY(QString number READ number WRITE setNumber)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit DTMFRadioID(QObject *parent=nullptr);\n\n  /** Constructor from name and number.\n   * @param name Specifies the name of the DTMF radio ID.\n   * @param number Specifies the DTMF number of the radio ID.\n   * @param parent Specifies the QObject parent, the object that owns this one. */\n  explicit DTMFRadioID(const QString &name, const QString &number, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the DTMF number of the radio ID. */\n  const QString &number() const;\n  /** Sets the DTMF number of the radio ID. */\n  void setNumber(const QString &number);\n\nprotected:\n  /** Holds the DTMF number of the radio ID. */\n  QString _number;\n};\n\n\n/** Represents the list of configured DMR IDs (radio IDs) within the abstract config.\n * @ingroup conf */\nclass RadioIDList: public ConfigObjectList\n{\n  Q_OBJECT\n\n\npublic:\n  /** Constructor. */\n  explicit RadioIDList(QObject *parent=nullptr);\n\n  void clear();\n\n  /** Returns the radio ID at the given index. */\n  [[deprecated(\"Use indexing instead.\")]] DMRRadioID *getId(int idx) const;\n  /** Searches the DMR ID object associated with the given DMR ID. */\n  DMRRadioID *find(uint32_t id) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\n  /** Adds the given DMR ID. */\n  virtual int addId(const QString &name, uint32_t id);\n  /** Deletes and removes the given DMR ID. */\n  virtual bool delId(uint32_t id);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n#endif // RADIOID_HH\n"
  },
  {
    "path": "lib/radioinfo.cc",
    "content": "#include \"radioinfo.hh\"\n\n#include \"opengd77.hh\"\n#include \"openuv380.hh\"\n#include \"openrtx.hh\"\n#include \"gd73.hh\"\n#include \"gd77.hh\"\n#include \"rd5r.hh\"\n#include \"md390.hh\"\n#include \"uv390.hh\"\n#include \"md2017.hh\"\n#include \"d868uv.hh\"\n#include \"d878uv.hh\"\n#include \"d878uv2.hh\"\n#include \"d578uv.hh\"\n#include \"d168uv.hh\"\n#include \"dmr6x2uv.hh\"\n#include \"dmr6x2uv2.hh\"\n#include \"dm1701.hh\"\n#include \"dr1801uv.hh\"\n#include \"dm32uv.hh\"\n\n\nQHash<QString, RadioInfo::Radio>\nRadioInfo::_radiosByName = QHash<QString, RadioInfo::Radio>{\n  {\"opengd77\",  RadioInfo::OpenGD77},\n  {\"openuv380\", RadioInfo::OpenUV380},\n  {\"openrtx\",   RadioInfo::OpenRTX},\n  {\"rd5r\",      RadioInfo::RD5R},\n  {\"gd73\",      RadioInfo::GD73},\n  {\"gd77\",      RadioInfo::GD77},\n  {\"md380\",     RadioInfo::MD380},\n  {\"md390\",     RadioInfo::MD390},\n  {\"rt8\",       RadioInfo::RT8},\n  {\"uv380\",     RadioInfo::UV380},\n  {\"uv390\",     RadioInfo::UV390},\n  {\"rt3s\",      RadioInfo::RT3S},\n  {\"md2017\",    RadioInfo::MD2017},\n  {\"rt82\",      RadioInfo::RT82},\n  {\"dm1701\",    RadioInfo::DM1701},\n  {\"rt84\",      RadioInfo::RT84},\n  {\"d868uv\",    RadioInfo::D868UV},\n  {\"d868uve\",   RadioInfo::D868UVE},\n  {\"dmr6x2uv\",  RadioInfo::DMR6X2UV},\n  {\"dmr6x2uv2\", RadioInfo::DMR6X2UV2},\n  {\"d878uv\",    RadioInfo::D878UV},\n  {\"d878uv2\",   RadioInfo::D878UVII},\n  {\"d578uv\",    RadioInfo::D578UV},\n  {\"d578uv2\",   RadioInfo::D578UVII},\n  {\"d168uv\",    RadioInfo::D168UV},\n  {\"dr1801uv\",  RadioInfo::DR1801UV},\n  {\"dm32uv\",    RadioInfo::DM32UV}\n};\n\nQHash<unsigned, RadioInfo>\nRadioInfo::_radiosById = QHash<unsigned, RadioInfo>{\n  {RadioInfo::OpenGD77,  OpenGD77::defaultRadioInfo()},\n  {RadioInfo::OpenUV380, OpenUV380::defaultRadioInfo()},\n  {RadioInfo::OpenRTX,   OpenRTX::defaultRadioInfo()},\n  {RadioInfo::RD5R,      RD5R::defaultRadioInfo()},\n  {RadioInfo::GD73,      GD73::defaultRadioInfo()},\n  {RadioInfo::GD77,      GD77::defaultRadioInfo()},\n  {RadioInfo::MD390,     MD390::defaultRadioInfo()},\n  {RadioInfo::UV390,     UV390::defaultRadioInfo()},\n  {RadioInfo::MD2017,    MD2017::defaultRadioInfo()},\n  {RadioInfo::DM1701,    DM1701::defaultRadioInfo()},\n  {RadioInfo::D868UVE,   D868UV::defaultRadioInfo()},\n  {RadioInfo::D878UV,    D878UV::defaultRadioInfo()},\n  {RadioInfo::D878UVII,  D878UV2::defaultRadioInfo()},\n  {RadioInfo::D578UV,    D578UV::defaultRadioInfo()},\n  {RadioInfo::D168UV,    D168UV::defaultRadioInfo()},\n  {RadioInfo::DMR6X2UV,  DMR6X2UV::defaultRadioInfo()},\n  {RadioInfo::DMR6X2UV2, DMR6X2UV2::defaultRadioInfo()},\n  {RadioInfo::DR1801UV,  DR1801UV::defaultRadioInfo()},\n  {RadioInfo::DM32UV,    DM32UV::defaultRadioInfo()}\n};\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioInfo\n * ********************************************************************************************* */\nRadioInfo::RadioInfo(Radio radio, const QString &name, const QString manufacturer,\n                     const QSet<USBDeviceInfo> &interfaces, const QList<RadioInfo> &alias)\n  : _radio(radio), _key(name.toLower()), _name(name), _manufacturer(manufacturer), _alias(alias),\n    _interfaces(interfaces)\n{\n  // pass...\n}\n\nRadioInfo::RadioInfo(Radio radio, const QString &key, const QString &name,\n                     const QString manufacturer, const QSet<USBDeviceInfo> &interfaces,\n                     const QList<RadioInfo> &alias)\n  : _radio(radio), _key(key), _name(name), _manufacturer(manufacturer), _alias(alias),\n    _interfaces(interfaces)\n{\n  // pass...\n}\n\nRadioInfo::RadioInfo()\n  : _key(\"\")\n{\n  // pass...\n}\n\nbool\nRadioInfo::isValid() const {\n  return ! _key.isEmpty();\n}\n\nconst QString &\nRadioInfo::key() const {\n  return _key;\n}\n\nconst QString &\nRadioInfo::name() const {\n  return _name;\n}\n\nconst QString &\nRadioInfo::manufacturer() const {\n  return _manufacturer;\n}\n\nbool\nRadioInfo::interfaceMatches(const USBDeviceInfo &other) const {\n  foreach (auto interface, _interfaces)\n    if (interface == other)\n      return true;\n  return false;\n}\n\nbool\nRadioInfo::hasAlias() const {\n  return 0 != _alias.count();\n}\nconst QList<RadioInfo> &\nRadioInfo::alias() const {\n  return _alias;\n}\n\nRadioInfo::Radio\nRadioInfo::id() const {\n  return _radio;\n}\n\nbool\nRadioInfo::hasRadioKey(const QString &key) {\n  return _radiosByName.contains(key);\n}\n\nRadioInfo\nRadioInfo::byKey(const QString &key) {\n  if (! hasRadioKey(key))\n    return RadioInfo();\n  return byID(_radiosByName[key]);\n}\n\nRadioInfo\nRadioInfo::byID(Radio radio) {\n  return _radiosById[radio];\n}\n\nQList<RadioInfo>\nRadioInfo::allRadios(bool flat) {\n  QList<RadioInfo> radios;\n  QHash<unsigned, RadioInfo>::const_iterator it = _radiosById.constBegin();\n  for (; it!=_radiosById.constEnd(); it++) {\n    radios.push_back(*it);\n    if (flat)\n      radios.append(it->_alias);\n  }\n  std::sort(radios.begin(), radios.end(), [](const RadioInfo &a, const RadioInfo &b) {\n    return a.id()<b.id();\n  });\n  return radios;\n}\n\nQList<RadioInfo>\nRadioInfo::allRadios(const USBDeviceInfo &interface, bool flat) {\n  QList<RadioInfo> radios;\n  QHash<unsigned, RadioInfo>::const_iterator it = _radiosById.constBegin();\n  for (; it!=_radiosById.constEnd(); it++) {\n    if (! it->interfaceMatches(interface))\n      continue;\n    radios.push_back(*it);\n    if (flat)\n      radios.append(it->_alias);\n  }\n  std::sort(radios.begin(), radios.end(), [](const RadioInfo &a, const RadioInfo &b) {\n    return a.id()<b.id();\n  });\n  return radios;\n}\n"
  },
  {
    "path": "lib/radioinfo.hh",
    "content": "#ifndef RADIOINFO_HH\n#define RADIOINFO_HH\n\n#include <QString>\n#include <QHash>\n#include <QList>\n#include \"usbdevice.hh\"\n\n\n/** Provides some information about a radio model.\n *\n * This class is used to unify radio enumeration and detection.\n *\n * @since 0.9.0 */\nclass RadioInfo\n{\npublic:\n  /** Known radios. */\n  enum Radio {\n    // Open source firmware\n    OpenGD77,\n    OpenUV380,\n    OpenRTX,\n    // Radioddity devices\n    RD5R,\n    GD73,\n    GD77,\n    // TyT devices\n    MD390, MD380 = MD390, RT8 = MD390,\n    UV390, UV380 = UV390, RT3S = UV390,\n    MD2017, RT82 = MD2017,\n    // Anytone devices\n    D868UVE,\n    D868UV = D868UVE,\n    DMR6X2UV,\n    DMR6X2UV2,\n    D878UV,\n    D878UVII,\n    D578UV,\n    D578UVII = D578UV,\n    D168UV,\n    // Baofeng/BTECH\n    DM1701, RT84 = DM1701,\n    DR1801UV,\n    DM32UV\n  };\n\npublic:\n  /** Use static methods the access radio info or call @c Radio::defaultRadioInfo. */\n  RadioInfo(Radio radio, const QString &name, const QString manufacturer,\n            const QSet<USBDeviceInfo> &interfaces,\n            const QList<RadioInfo> &alias=QList<RadioInfo>());\n  /** Use static methods the access radio info or call @c Radio::defaultRadioInfo. */\n  RadioInfo(Radio radio, const QString &key, const QString &name, const QString manufacturer,\n            const QSet<USBDeviceInfo> &interfaces,\n            const QList<RadioInfo> &alias=QList<RadioInfo>());\n\n  /** Empty constructor. */\n  RadioInfo();\n\n  /** Returns @c true if the info is valid. */\n  bool isValid() const;\n\n  /** Returns the radio key (used to identify radios in the command line). */\n  const QString &key() const;\n  /** Returns the radio name. */\n  const QString &name() const;\n  /** Returns the manufacturer name. */\n  const QString &manufacturer() const;\n  /** Returns some information about the interface to the radio. */\n  bool interfaceMatches(const USBDeviceInfo &other) const;\n\n  /** Returns @c true if the radio has aliases.\n   * That is other radios that are identical. */\n  bool hasAlias() const;\n  /** Returns the list of alias radios. */\n  const QList<RadioInfo> &alias() const;\n\n  /** Returns the unique device ID (alias radios share ID). */\n  Radio id() const;\n\npublic:\n  /** Returns @c true if the given key is known. */\n  static bool hasRadioKey(const QString &key);\n  /** Returns the radio info by key. */\n  static RadioInfo byKey(const QString &key);\n  /** Returns the radio info by id. */\n  static RadioInfo byID(Radio radio);\n\n  /** Returns the list of all known radios. */\n  static QList<RadioInfo> allRadios(bool flat=true);\n  /** Returns a list of all known radios for the specified interface. */\n  static QList<RadioInfo> allRadios(const USBDeviceInfo &interface, bool flat=true);\n\nprotected:\n  /** Holds the radio id. */\n  Radio _radio;\n  /** Holds the key of the radio. */\n  QString _key;\n  /** Holds the name of the radio. */\n  QString _name;\n  /** Holds the name of the manufacturer. */\n  QString _manufacturer;\n  /** Holds possible identical radios from other manufacturers. */\n  QList<RadioInfo> _alias;\n  /** Holds some information about the interface to the radio. */\n  QSet<USBDeviceInfo> _interfaces;\n\nprotected:\n  /** Key->ID map. */\n  static QHash<QString, Radio> _radiosByName;\n  /** ID->Info map. */\n  static QHash<unsigned, RadioInfo> _radiosById;\n};\n\n\n#endif // RADIOINFO_HH\n"
  },
  {
    "path": "lib/radiointerface.cc",
    "content": "#include \"radiointerface.hh\"\n\n#include \"anytone_interface.hh\"\n#include \"opengd77_interface.hh\"\n#include \"radioddity_interface.hh\"\n#include \"tyt_interface.hh\"\n\n/* ********************************************************************************************* *\n * Implementation of RadioInterface\n * ********************************************************************************************* */\nRadioInterface::RadioInterface()\n{\n\t// pass...\n}\n\nRadioInterface::~RadioInterface() {\n  // pass...\n}\n\nbool\nRadioInterface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err)\n  return true;\n}\n\nbool\nRadioInterface::reboot(const ErrorStack &err) {\n  Q_UNUSED(err)\n  return true;\n}\n\nbool\nRadioInterface::setDateTime(const QDateTime &datetime, const ErrorStack &err) {\n  Q_UNUSED(datetime); Q_UNUSED(err);\n  return true;\n}\n"
  },
  {
    "path": "lib/radiointerface.hh",
    "content": "/** @defgroup rif Radio interfaces\n * Depending on the manufacturer or model, different radios have different computer-radio\n * interfaces. This module collects all classes that provide these interfaces. */\n#ifndef RADIOINFERFACE_HH\n#define RADIOINFERFACE_HH\n\n#include <QString>\n#include \"usbdevice.hh\"\n#include \"radioinfo.hh\"\n#include \"errorstack.hh\"\n\n/** Abstract radio interface.\n * A radion interface must provide means to communicate with the device. That is, open a connection\n * to the device, allow for reading and writing specific memory blocks.\n *\n * This class defines the common interface for all radio-interface classes, irrespective of the\n * actual communication protocol being used by the device.\n *\n * @ingroup rif */\nclass RadioInterface\n{\nprotected:\n  /** Hidden constructor. */\n  explicit RadioInterface();\n\npublic:\n  /** Destructor. */\n\tvirtual ~RadioInterface();\n\n  /** Return @c true if a connection to the device has been established. */\n\tvirtual bool isOpen() const = 0;\n  /** Closes the connection to the device. */\n\tvirtual void close() = 0;\n\n  /** Returns a device identifier. */\n  virtual RadioInfo identifier(const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Starts the write process into the specified bank and at the given address.\n   * @param bank Specifies the memory bank to write to. Usually there is only one bank. Some radios,\n   *    however, to have several memory banks to hold the codeplug. For example the Open GD77 has\n   *    EEPROM and Flash memory banks with independent addresses.\n   * @param addr Specifies the address to write to.\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack())=0;\n\n  /** Writes a chunk of @c data at the address @c addr.\n   * @param bank Specifies the memory bank to write to. Usually there is only one bank. Some radios,\n   *    however, to have several memory banks to hold the codeplug. For example the Open GD77 has\n   *    EEPROM and Flash memory banks with independent addresses.\n   * @param addr Specifies the address to write to.\n   * @param data Pointer to the actual data to be written.\n   * @param nbytes Specifies the number of bytes to write.\n   * @param err Passes an error stack to put error messages on.\n   * @returns @c true on success. */\n  virtual bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** This function ends a series of @c write operations.\n   * This function will be implemented by certain interfaces that need completion of write\n   * operations (e.g., HID).\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool write_finish(const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Starts the read process from the specified bank and at the given address.\n   * @param bank Specifies the memory bank to read from. Usually there is only one bank. Some radios,\n   *    however, to have several memory banks to hold the codeplug. For example the Open GD77 has\n   *    EEPROM and Flash memory banks with independent addresses.\n   * @param addr Specifies the address to read from.\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Reads a chunk of data from the block-address @c bno (block number).\n   * @param bank Specifies the memory bank to read from. Usually there is only one bank. Some radios,\n   *    however, to have several memory banks to hold the codeplug. For example the Open GD77 has\n   *    EEPROM and Flash memory banks with independent addresses.\n   * @param addr Specifies the address to read from.\n   * @param data Pointer where to store the read data.\n   * @param nbytes Specifies the number of bytes to read.\n   * @param err Passes an error stack to put error messages on.\n   * @returns @c true on success. */\n  virtual bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** This function ends a series of @c read operations.\n   * This function will be re-implemented by certain interfaces that need completion of read\n   * operations (e.g., HID).\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool read_finish(const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Some radios need to be rebooted after being read or programmed. This function\n   * will be re-implemented by some interfaces (e.g., DFUDevice) to reboot the radio. By default\n   * this function does nothing.\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool reboot(const ErrorStack &err=ErrorStack());\n\n  /** Some radios allow to set date and time of the internal clock during codeplug upload.\n   * This function might be re-implemented by interfaces for those radios\n   * (e.g., the OpenGD77 one).\n   * @param datetime [in] Specifies the timestamp to set.\n   * @param err Passes an error stack to put error messages on. */\n  virtual bool setDateTime(const QDateTime &datetime, const ErrorStack &err=ErrorStack());\n};\n\n#endif // RADIOINFERFACE_HH\n"
  },
  {
    "path": "lib/radiolimits.cc",
    "content": "#include \"radiolimits.hh\"\n#include \"configobject.hh\"\n#include \"config.hh\"\n#include <QMetaProperty>\n#include <QRegularExpression>\n\n// Utility function to check string content for ASCII encoding\ninline bool qstring_is_ascii(const QString &text) {\n  foreach (QChar c, text) {\n    if ((c.unicode() < 0x20) || (c.unicode() > 0x7e))\n      return false;\n  }\n  return true;\n}\n\n// Utility function to check string content for DTMF encoding\ninline bool qstring_is_dtmf(const QString &text) {\n  return QRegularExpression(\"^[0-9A-Da-d*#]*$\").match(text).hasMatch();\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitIssue\n * ********************************************************************************************* */\nRadioLimitIssue::RadioLimitIssue(Severity severity, const QStringList &stack)\n  : QTextStream(), _severity(severity), _stack(stack), _message()\n{\n  setString(&_message);\n}\n\nRadioLimitIssue::RadioLimitIssue(const RadioLimitIssue &other)\n  : QTextStream(), _severity(other._severity), _stack(other._stack), _message(other._message)\n{\n  setString(&_message);\n}\n\nRadioLimitIssue &\nRadioLimitIssue::operator =(const RadioLimitIssue &other) {\n  QTextStream::flush();\n  _severity = other._severity;\n  _stack = other._stack;\n  _message = other._message;\n  return *this;\n}\n\nRadioLimitIssue &\nRadioLimitIssue::operator =(const QString &message) {\n  _message = message;\n  return *this;\n}\n\nRadioLimitIssue::Severity\nRadioLimitIssue::severity() const {\n  return _severity;\n}\n\nconst QString &\nRadioLimitIssue::message() const {\n  return _message;\n}\n\nconst QStringList &\nRadioLimitIssue::stack() const {\n  return _stack;\n}\n\nQString\nRadioLimitIssue::format() const {\n  QString res; QTextStream stream(&res);\n  switch (_severity) {\n  case Silent: stream << \"Silent: \"; break;\n  case Hint: stream << \"Hint: \"; break;\n  case Warning: stream << \"Warn: \"; break;\n  case Critical: stream << \"Crit: \"; break;\n  }\n  stream << _stack.join(\", \") << \": \" << _message;\n  stream.flush();\n  return res;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitContext\n * ********************************************************************************************* */\nRadioLimitContext::RadioLimitContext(bool ignoreFrequencyLimits)\n  : _stack(), _ignoreFrequencyLimits(ignoreFrequencyLimits), _maxSeverity(RadioLimitIssue::Silent)\n{\n  // pass...\n}\n\nRadioLimitIssue &\nRadioLimitContext::newMessage(RadioLimitIssue::Severity severity) {\n  _messages.push_back(RadioLimitIssue(severity, _stack));\n  if (severity > _maxSeverity)\n    _maxSeverity = severity;\n  return _messages.back();\n}\n\nint\nRadioLimitContext::count() const {\n  return _messages.count();\n}\n\nconst RadioLimitIssue &\nRadioLimitContext::message(int n) const {\n  return _messages.at(n);\n}\n\nvoid\nRadioLimitContext::push(const QString &element) {\n  _stack.append(element);\n}\n\nvoid\nRadioLimitContext::pop() {\n  _stack.pop_back();\n}\n\nbool\nRadioLimitContext::ignoreFrequencyLimits() const {\n  return _ignoreFrequencyLimits;\n}\nvoid\nRadioLimitContext::enableIgnoreFrequencyLimits(bool enable) {\n  _ignoreFrequencyLimits = enable;\n}\n\nRadioLimitIssue::Severity\nRadioLimitContext::maxSeverity() const {\n  return _maxSeverity;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitElement\n * ********************************************************************************************* */\nRadioLimitElement::RadioLimitElement(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nRadioLimitElement::~RadioLimitElement() {\n  // pass ...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitIgnored\n * ********************************************************************************************* */\nRadioLimitIgnored::RadioLimitIgnored(RadioLimitIssue::Severity notify, QObject *parent)\n  : RadioLimitObject(parent), _notification(notify)\n{\n  // pass...\n}\n\nbool\nRadioLimitIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  auto obj = prop.read(item).value<const ConfigObject *>();\n  if (nullptr != obj)\n    return verifyObject(obj, context);\n\n  return true;\n}\n\nbool\nRadioLimitIgnored::verifyObject(const ConfigObject *item, RadioLimitContext &context) const {\n  if (nullptr == item)\n    return true;\n  auto &msg = context.newMessage(_notification);\n  msg = tr(\"Ignore %1 '%2'. Not applicable/supported by this radio.\")\n      .arg(item->metaObject()->className()).arg(item->name());\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitValue\n * ********************************************************************************************* */\nRadioLimitValue::RadioLimitValue(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitElement(parent), _severity(severity)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitString\n * ********************************************************************************************* */\nRadioLimitString::RadioLimitString(int minLen, int maxLen, Encoding enc, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _minLen(minLen), _maxLen(maxLen), _encoding(enc)\n{\n  // pass...\n}\n\nbool\nRadioLimitString::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (QMetaType::QString != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected string.\";\n    return false;\n  }\n\n  QString value = prop.read(item).toString();\n  bool success = true;\n\n  if ((0<_maxLen) && (value.size() > int(_maxLen))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"String length of \" << prop.name() << \" ('\" << value << \"', \" << value.size()\n        << \") exceeds maximum length \" << _maxLen << \".\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  if ((0<_minLen) && (value.size() < int(_minLen))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"String length of \" << prop.name() << \" ('\" << value << \"', \" << value.size()\n        << \") is shorter than minimum size \" << _minLen << \".\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  if ((ASCII == _encoding) && (! qstring_is_ascii(value))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Cannot encode string '\" << value << \"' in ASCII.\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  } else if ((DTMF == _encoding) && (! qstring_is_dtmf(value))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Cannot encode string '\" << value << \"' in DTMF.\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitStringRegEx\n * ********************************************************************************************* */\nRadioLimitStringRegEx::RadioLimitStringRegEx(const QString &pattern, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _pattern(pattern)\n{\n  // pass...\n}\n\nbool\nRadioLimitStringRegEx::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (QMetaType::QString != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected string.\";\n    return false;\n  }\n\n  bool success = true;\n\n  QString value = prop.read(item).toString();\n  auto match = _pattern.match(value);\n  if (! match.hasMatch()) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Value '\" << value << \"' of property \" << prop.name()\n        << \" does not match pattern '\" << _pattern.pattern() << \"'.\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitPin\n * ********************************************************************************************* */\nRadioLimitPin::RadioLimitPin(int length, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitStringRegEx(\"\", severity, parent)\n{\n  if (length <= 0)\n    _pattern = QRegularExpression(\"^[0-9]+\");\n  else\n    _pattern = QRegularExpression(QString(\"[0-9]{0,%1}\").arg(length));\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitStringIgnored\n * ********************************************************************************************* */\nRadioLimitStringIgnored::RadioLimitStringIgnored(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent)\n{\n  // pass...\n}\n\nbool\nRadioLimitStringIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (QMetaType::QString != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg = tr(\"Expected value of '%1' to be string.\").arg(prop.name());\n    return true;\n  }\n\n  bool success = true;\n\n  QVariant value = prop.read(item);\n  if (! value.toString().isEmpty()) {\n    auto &msg = context.newMessage(_severity);\n    msg = tr(\"Value of '%1' is ignored. Not applicable/supported by the radio.\").arg(prop.name());\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitBool\n * ********************************************************************************************* */\nRadioLimitBool::RadioLimitBool(QObject *parent)\n  : RadioLimitValue(RadioLimitIssue::Severity::Hint, parent)\n{\n  // pass...\n}\n\nbool\nRadioLimitBool::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  Q_UNUSED(item)\n\n  if (QMetaType::Bool != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected bool.\";\n    return false;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitIgnoredBool\n * ********************************************************************************************* */\nRadioLimitIgnoredBool::RadioLimitIgnoredBool(RadioLimitIssue::Severity notify, QObject *parent)\n  : RadioLimitBool(parent), _severity(notify)\n{\n  // pass...\n}\n\nbool\nRadioLimitIgnoredBool::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (QMetaType::Bool != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected bool.\";\n    return false;\n  }\n\n  bool success = true;\n  bool value = prop.read(item).toBool();\n  if (value) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Setting \" << prop.name() << \" is ignored.\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitUInt\n * ********************************************************************************************* */\nRadioLimitUInt::RadioLimitUInt(qint64 minValue, qint64 maxValue, qint64 defValue,\n                               RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _minValue(minValue), _maxValue(maxValue), _defValue(defValue)\n{\n  // pass...\n}\n\nbool\nRadioLimitUInt::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (QMetaType::UInt != prop.typeId()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected uint.\";\n    return false;\n  }\n\n  bool success = true;\n  unsigned value = prop.read(item).toUInt();\n\n  if ((0<_maxValue) && (value > qint64(_maxValue)) && ((0>_defValue) || (value!=_defValue))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Value \" << value << \" of \" << prop.name() << \" exceeds maximum \" << _maxValue << \".\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  if ((0<_minValue) && (value < qint64(_minValue)) && ((0>_defValue) || (value!=_defValue))) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Value \" << value << \" of \" << prop.name() << \" is smaller than minimum \" << _minValue << \".\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitDMRId\n * ********************************************************************************************* */\nRadioLimitDMRId::RadioLimitDMRId(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitUInt(1, 16777215, -1, severity, parent)\n{\n  // pass...\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitEnum\n * ********************************************************************************************* */\nRadioLimitEnum::RadioLimitEnum(const std::initializer_list<unsigned> &values, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _values(values)\n{\n  // pass...\n}\n\nbool\nRadioLimitEnum::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (! prop.isEnumType()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected enum type.\";\n    return false;\n  }\n\n  bool success = true;\n  unsigned value = prop.read(item).toUInt();\n\n  if (! _values.contains(value)) {\n    QMetaEnum e = prop.enumerator();\n    QStringList possible;\n    foreach (unsigned val, _values)\n      possible.append(e.valueToKey(val));\n\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg << \"The enum value '\" << e.valueToKey(value) << \"' cannot be encoded. \"\n        << \"Valid values are \" << possible.join(\", \") << \". \"\n        << \"Another value might be chosen automatically.\";\n    success &= (_severity < RadioLimitIssue::Severity::Critical);\n  }\n\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitFrequencies\n * ********************************************************************************************* */\nRadioLimitFrequencies::RadioLimitFrequencies(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _frequencyRanges()\n{\n  // pass...\n}\n\nRadioLimitFrequencies::RadioLimitFrequencies(const RangeList &ranges, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _frequencyRanges()\n{\n  for (auto range=ranges.begin(); range!=ranges.end(); range++) {\n    _frequencyRanges.append({range->first, range->second});\n  }\n}\n\nbool\nRadioLimitFrequencies::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (qMetaTypeId<Frequency>() != prop.userType()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected frequency.\";\n    return false;\n  }\n\n  if (0 == _frequencyRanges.size())\n    return true;\n\n  Frequency value = prop.read(item).value<Frequency>();\n  foreach (const FrequencyRange &range, _frequencyRanges) {\n    if (range.contains(value))\n      return true;\n  }\n\n  if (context.ignoreFrequencyLimits())\n    return true;\n\n  auto &msg = context.newMessage(_severity);\n  msg << \"Frequency \" << value.inMHz() << \"MHz is outside of allowed frequency ranges.\";\n\n  return _severity < RadioLimitIssue::Severity::Critical;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitTransmitFrequencies\n * ********************************************************************************************* */\nRadioLimitTransmitFrequencies::RadioLimitTransmitFrequencies(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitFrequencies(severity, parent)\n{\n  // pass...\n}\n\nRadioLimitTransmitFrequencies::RadioLimitTransmitFrequencies(const RangeList &ranges, RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitFrequencies(ranges, severity, parent)\n{\n  // pass...\n}\n\nbool\nRadioLimitTransmitFrequencies::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (qMetaTypeId<Frequency>() != prop.userType()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected frequency in MHz.\";\n    return false;\n  }\n\n  if (item->is<Channel>() && (! item->as<Channel>()->rxOnly())) {\n    return RadioLimitFrequencies::verify(item, prop, context);\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitInterval\n * ********************************************************************************************* */\nRadioLimitInterval::RadioLimitInterval(RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _durations{Interval::null(), Interval::infinity()}\n{\n  // pass...\n}\n\nRadioLimitInterval::RadioLimitInterval(const Range<Interval> &range,\n                                       RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _durations{range}\n{\n  // pass...\n}\n\nbool\nRadioLimitInterval::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (qMetaTypeId<Interval>() != prop.userType()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected interval.\";\n    return false;\n  }\n\n  auto value = prop.read(item).value<Interval>();\n  if (_durations.contains(value))\n    return true;\n\n  auto &msg = context.newMessage(_severity);\n  msg << \"Interval \" << value.format() << \" is outside of allowed range.\";\n\n  return _severity < RadioLimitIssue::Severity::Critical;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitLevel\n * ********************************************************************************************* */\nRadioLimitLevel::RadioLimitLevel(const Range<unsigned int> &range, bool allowInvalid,\n                                 RadioLimitIssue::Severity severity, QObject *parent)\n  : RadioLimitValue(severity, parent), _range(range), _allowInvalid(allowInvalid)\n{\n  // pass...\n}\n\nbool\nRadioLimitLevel::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  Q_UNUSED(item);\n\n  if (qMetaTypeId<Level>() != prop.userType()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Expected interval.\";\n    return false;\n  }\n\n  Level value = prop.read(item).value<Level>();\n  if (value.isInvalid() && (! _allowInvalid)) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Invalid level not allowed for property \" << prop.name() << \".\";\n    return _severity <= RadioLimitIssue::Warning;\n  }\n\n  if (!_range.contains(value.value())) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"Value of property \" << prop.name() << \" \" << value.value() << \" exceeds range [\" << _range.lower << \", \" << _range.upper << \"].\";\n    return _severity <= RadioLimitIssue::Warning;\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitItem\n * ********************************************************************************************* */\nRadioLimitItem::RadioLimitItem(QObject *parent)\n  : RadioLimitElement(parent), _elements()\n{\n  // pass...\n}\n\nRadioLimitItem::RadioLimitItem(const PropList &list, QObject *parent)\n  : RadioLimitElement(parent), _elements(list)\n{\n  for (QHash<QString,RadioLimitElement*>::iterator item=_elements.begin(); item != _elements.end(); item++) {\n    item.value()->setParent(this);\n  }\n}\n\nbool\nRadioLimitItem::add(const QString &prop, RadioLimitElement *structure) {\n  if (_elements.contains(prop) || (nullptr == structure))\n    return false;\n  _elements.insert(prop, structure);\n  structure->setParent(this);\n  return true;\n}\n\nbool\nRadioLimitItem::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (! prop.isReadable()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not readable.\";\n    return false;\n  }\n\n  if (! propIsInstance<ConfigItem>(prop)) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not an instance of ConfigItem.\";\n    return false;\n  }\n\n  if (prop.read(item).isNull())\n    return true;\n\n  context.push(QString(\"In property '%1'\").arg(prop.name()));\n  bool success = verifyItem(prop.read(item).value<ConfigItem*>(), context);\n  context.pop();\n  return success;\n}\n\nbool\nRadioLimitItem::verifyItem(const ConfigItem *item, RadioLimitContext &context) const {\n  const QMetaObject *meta = item->metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    // This property\n    QMetaProperty prop = meta->property(p);\n    // Should never happen\n    if (! prop.isValid())\n      continue;\n    // Verify property\n    if (_elements.contains(prop.name())) {\n      if (! _elements[prop.name()]->verify(item, prop, context))\n        return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitObject\n * ********************************************************************************************* */\nRadioLimitObject::RadioLimitObject(QObject *parent)\n  : RadioLimitItem(parent)\n{\n  // pass...\n}\n\nRadioLimitObject::RadioLimitObject(const PropList &list, QObject *parent)\n  : RadioLimitItem(list, parent)\n{\n  // pass...\n}\n\nbool\nRadioLimitObject::verifyObject(const ConfigObject *item, RadioLimitContext &context) const {\n  context.push(QString(\"In object '%1'\").arg(item->name()));\n  bool success = verifyItem(item, context);\n  context.pop();\n  return success;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitObjects\n * ********************************************************************************************* */\nRadioLimitObjects::RadioLimitObjects(const TypeList &list, QObject *parent)\n  : RadioLimitObject(parent), _types()\n{\n  for (auto type=list.begin(); type!=list.end(); type++) {\n    _types[type->first.className()] = type->second;\n    type->second->setParent(this);\n  }\n}\n\nbool\nRadioLimitObjects::verifyItem(const ConfigItem *item, RadioLimitContext &context) const {\n  if (! _types.contains(item->metaObject()->className())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check item of type \" << item->metaObject()->className()\n        << \". Unexpected type. Expected one of \" << QStringList(_types.keys()).join(\", \") << \".\";\n    return false;\n  }\n  return _types[item->metaObject()->className()]->verifyItem(item, context);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitObjRef\n * ********************************************************************************************* */\nRadioLimitObjRef::RadioLimitObjRef(const QMetaObject &type, bool allowNull, QObject *parent)\n  : RadioLimitElement(parent), _allowNull(allowNull), _types()\n{\n  _types.insert(type.className());\n}\n\nRadioLimitObjRef::RadioLimitObjRef(const MetaObjectList &types, bool allowNull, QObject *parent)\n  : RadioLimitElement(parent), _allowNull(allowNull), _types()\n{\n  for (auto type=types.begin(); type!=types.end(); type++)\n    _types.insert(type->className());\n}\n\nbool\nRadioLimitObjRef::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  ConfigObjectReference *ref = prop.read(item).value<ConfigObjectReference *>();\n\n  if (nullptr == ref) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check type of property '\" << prop.name() << \"'. Expected ConfigObjectReference.\";\n    return false;\n  }\n\n  if (ref->isNull()) {\n    if (_allowNull)\n      return true;\n\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg << \"Property '\" << prop.name() << \"' must refer to an instances of \"\n        << QStringList(_types.begin(), _types.end()).join(\", \") << \".\";\n\n    return true;\n  }\n\n  if (! validType(ref->as<ConfigObject>()->metaObject())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Property '\" << prop.name() << \"' must refer to an instances of \"\n        << QStringList(_types.begin(), _types.end()).join(\", \") << \".\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nRadioLimitObjRef::validType(const QMetaObject *type) const {\n  if (_types.contains(type->className()))\n    return true;\n  if (type->superClass())\n    return validType(type->superClass());\n  return false;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitObjRefIgnored\n * ********************************************************************************************* */\nRadioLimitObjRefIgnored::RadioLimitObjRefIgnored(\n    ConfigObject *defObj, RadioLimitIssue::Severity notify, QObject *parent)\n  : RadioLimitObjRef(ConfigObject::staticMetaObject, true, parent), _severity(notify),\n    _default(defObj)\n{\n  // pass...\n}\n\nbool\nRadioLimitObjRefIgnored::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  // Get referenced object\n  ConfigObjectReference *ref = prop.read(item).value<ConfigObjectReference *>();\n  // If reference is set and not default reference\n  if ((! ref->isNull()) && (_default != ref->as<ConfigObject>())) {\n    auto &msg = context.newMessage(_severity);\n    msg << \"The reference '\" << prop.name() <<\n           \"' is ignored. Not applicable/supported by this radio.\";\n  }\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitList\n * ********************************************************************************************* */\nRadioLimitList::RadioLimitList(const QMetaObject &type, int minSize, int maxSize, RadioLimitObject *element, QObject *parent)\n  : RadioLimitElement(parent), _elements(), _minCount(), _maxCount()\n{\n  _elements.insert(type.className(), element);\n  _minCount.insert(type.className(), minSize);\n  _maxCount.insert(type.className(), maxSize);\n  element->setParent(this);\n}\n\nRadioLimitList::RadioLimitList(const std::initializer_list<ElementLimits> &elements, QObject *parent)\n  : RadioLimitElement(parent), _elements(), _minCount(), _maxCount()\n{\n  for (auto el=elements.begin(); el!=elements.end(); el++) {\n    QString className = el->type.className();\n    _elements.insert(className, el->structure); el->structure->setParent(this);\n    _minCount.insert(className, el->minCount);\n    _maxCount.insert(className, el->maxCount);\n  }\n}\n\nbool\nRadioLimitList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (! prop.isReadable()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not readable.\";\n    return false;\n  }\n\n  if (nullptr == prop.read(item).value<ConfigObjectList *>()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not an instance of ConfigObjectList.\";\n    return false;\n  }\n\n  const ConfigObjectList *plist = prop.read(item).value<ConfigObjectList*>();\n\n  QHash<QString, unsigned> counts;\n  foreach (QString type, _elements.keys())\n    counts.insert(type,0);\n\n  context.push(QString(\"In list '%1'\").arg(prop.name()));\n\n  // Check type and structure\n  for (int i=0; i<plist->count(); i++) {\n    // Check type\n    ConfigObject *obj = plist->get(i);\n    QString className = findClassName(*(obj->metaObject()));\n    if (className.isEmpty()) {\n      auto &msg = context.newMessage(RadioLimitIssue::Critical);\n      msg << \"Unexpected element type '\" << obj->metaObject()->className()\n          << \"'. Expected one of \" << _elements.keys().join(\", \") << \".\";\n      context.pop();\n      return false;\n    }\n\n    counts[className]++;\n\n    context.push(QString(\"In element %1 ('%2')\").arg(i).arg(obj->name()));\n    if (! _elements[className]->verifyObject(obj, context)) {\n      context.pop();\n      context.pop();\n      return false;\n    }\n    context.pop();\n  }\n\n  // Check counts\n  foreach (QString className, _elements.keys()) {\n    if ((0 <= _minCount[className]) && (counts[className]<_minCount[className])) {\n      auto &msg = context.newMessage(RadioLimitIssue::Critical);\n      msg << \"The number of elements of type '\" << className << \"' \" << counts[className]\n             << \" is less than the required count \" << _minCount[className] << \".\";\n    }\n    if ((0 <= _maxCount[className]) && (counts[className]>_maxCount[className])) {\n      auto &msg = context.newMessage(RadioLimitIssue::Warning);\n      msg << \"The number of elements of type '\" << className << \"' \" << counts[className]\n             << \" is greater than the maximum count \" << _maxCount[className] << \".\";\n    }\n  }\n\n  context.pop();\n\n  return true;\n}\n\nQString\nRadioLimitList::findClassName(const QMetaObject &type) const {\n  if (_elements.contains(type.className()))\n    return type.className();\n  if (const QMetaObject *super = type.superClass())\n    return findClassName(*super);\n  return \"\";\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitRefList\n * ********************************************************************************************* */\nRadioLimitRefList::RadioLimitRefList(int minSize, int maxSize, const QMetaObject &type, QObject *parent)\n  : RadioLimitElement(parent), _minSize(minSize), _maxSize(maxSize), _types()\n{\n  _types.insert(type.className());\n}\n\nbool\nRadioLimitRefList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (! prop.isReadable()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not readable.\";\n    return false;\n  }\n\n  if (nullptr == prop.read(item).value<ConfigObjectRefList *>()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not an instance of ConfigObjectRefList.\";\n    return false;\n  }\n\n  const ConfigObjectRefList *plist = prop.read(item).value<ConfigObjectRefList*>();\n  if ((0 <= _minSize) && (_minSize > plist->count())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"List '\" << prop.name() << \"' requires at least \" << _minSize\n        << \" elements, \" << plist->count() << \" elements found.\";\n    return false;\n  }\n\n  if ((0 <= _maxSize) && (_maxSize < plist->count())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg << \"List '\" << prop.name() << \"' takes at most \" << _maxSize\n        << \" elements, \" << plist->count() << \" elements found.\";\n    return false;\n  }\n\n  for (int i=0; i<plist->count(); i++) {\n    if (! validType(plist->get(i)->metaObject())) {\n      auto &msg = context.newMessage(RadioLimitIssue::Critical);\n      msg << \"Reference to \" << plist->get(i)->metaObject()->className() << \" is not allowed here. \"\n          << \"Must be one of \" << QStringList(_types.begin(), _types.end()).join(\", \") << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nRadioLimitRefList::validType(const QMetaObject *type) const {\n  if (_types.contains(type->className()))\n    return true;\n  if (type->superClass())\n    return validType(type->superClass());\n  return false;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitPrivateCallRefList\n * ********************************************************************************************* */\nRadioLimitGroupCallRefList::RadioLimitGroupCallRefList(int minSize, int maxSize, QObject *parent)\n  : RadioLimitElement(parent), _minSize(minSize), _maxSize(maxSize)\n{\n  // pass...\n}\n\nbool\nRadioLimitGroupCallRefList::verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const {\n  if (! prop.isReadable()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not readable.\";\n    return false;\n  }\n\n  if (nullptr == prop.read(item).value<ConfigObjectRefList *>()) {\n    auto &msg = context.newMessage(RadioLimitIssue::Critical);\n    msg << \"Cannot check property \" << prop.name() << \": Not an instance of ConfigObjectRefList.\";\n    return false;\n  }\n\n  const ConfigObjectRefList *plist = prop.read(item).value<ConfigObjectRefList*>();\n  if ((0 <= _minSize) && (_minSize > plist->count())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg << \"List '\" << prop.name() << \"' requires at least \" << _minSize\n        << \" elements, \" << plist->count() << \" elements found.\";\n    return false;\n  }\n\n  if ((0 <= _maxSize) && (_maxSize < plist->count())) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg << \"List '\" << prop.name() << \"' takes at most \" << _maxSize\n        << \" elements, \" << plist->count() << \" elements found.\";\n    return false;\n  }\n\n  for (int i=0; i<plist->count(); i++) {\n    if (! plist->get(i)->is<DMRContact>()) {\n      auto &msg = context.newMessage(RadioLimitIssue::Critical);\n      msg << \"Reference to \" << plist->get(i)->metaObject()->className() << \" is not allowed here. \"\n          << \"Must be DigtialContact.\";\n      return false;\n    }\n    if (DMRContact::GroupCall != plist->get(i)->as<DMRContact>()->type()) {\n      auto &msg = context.newMessage(RadioLimitIssue::Critical);\n      msg << \"Expected reference to a group call digital contact.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimitSingleZone\n * ********************************************************************************************* */\nRadioLimitSingleZone::RadioLimitSingleZone(qint64 maxSize, const PropList &list, QObject *parent)\n  : RadioLimitObject(list, parent)\n{\n  // A and B may hold up to maxSize references to channels\n  _elements[\"A\"] = new RadioLimitRefList(1, maxSize, Channel::staticMetaObject, this);\n  _elements[\"B\"] = new RadioLimitRefList(0, maxSize, Channel::staticMetaObject, this);\n}\n\nbool\nRadioLimitSingleZone::verifyItem(const ConfigItem *item, RadioLimitContext &context) const {\n  if (! RadioLimitObject::verifyItem(item, context))\n    return false;\n\n  const Zone *zone = item->as<Zone>();\n  if (zone && zone->B()->count()) {\n    auto &msg = context.newMessage();\n    msg << \"This radio does not support dual channel zones. The zone '\" << zone->name()\n        << \"' will be split into two.\";\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioLimits\n * ********************************************************************************************* */\nRadioLimits::RadioLimits(bool betaWarning, QObject *parent)\n  : RadioLimitItem(parent), _betaWarning(betaWarning),\n    _hasCallSignDB(false), _callSignDBImplemented(false), _numCallSignDBEntries(0),\n    _hasSatelliteConfig(false), _satelliteConfigImplemented(false), _numSatellites(0)\n{\n  // pass...\n}\n\nRadioLimits::RadioLimits(const std::initializer_list<std::pair<QString, RadioLimitElement *> > &list, QObject *parent)\n  : RadioLimitItem(list, parent), _betaWarning(false),\n    _hasCallSignDB(false), _callSignDBImplemented(false), _numCallSignDBEntries(0),\n    _hasSatelliteConfig(false), _satelliteConfigImplemented(false), _numSatellites(0)\n{\n  // pass...\n}\n\n\nbool\nRadioLimits::hasCallSignDB() const {\n  return _hasCallSignDB;\n}\n\nbool\nRadioLimits::callSignDBImplemented() const {\n  return _callSignDBImplemented;\n}\n\nunsigned\nRadioLimits::numCallSignDBEntries() const {\n  return _numCallSignDBEntries;\n}\n\n\nbool\nRadioLimits::hasSatelliteConfig() const {\n  return _hasSatelliteConfig;\n}\n\nbool\nRadioLimits::satelliteConfigImplemented() const {\n  return _satelliteConfigImplemented;\n}\n\nunsigned\nRadioLimits::numSatellites() const {\n  return _numSatellites;\n}\n\n\nbool\nRadioLimits::verifyConfig(const Config *config, RadioLimitContext &context) const {\n  if (_betaWarning) {\n    auto &msg = context.newMessage(RadioLimitIssue::Warning);\n    msg = tr(\"The support for this radio is still under development. Some features may sill be \"\n             \"missing or are not well tested.\");\n  }\n\n  return verifyItem(config, context);\n}\n"
  },
  {
    "path": "lib/radiolimits.hh",
    "content": "/** @defgroup limits Radio Limits\n * This module collects all classes used to define the limits for each supported radio. That is,\n * a tree of objects that hold the limitations like string length, number of elements in a list etc.\n * for the various settings of a radio including their extensions.\n *\n * This system will replace the static @c Radio::Features struct. The associated limits for each\n * radio can be obtained using the @c Radio::limits method.\n *\n * Many classes in this module provide an initializer list constructor. This allows for an easy\n * construction of radio limits programmatically like\n * @code\n * new RadioLimitItem {                       // < Describes an ConfigItem\n *   { \"radioIDs\",                            // < with an 'radioIDs' property\n *     new RadioLimitList(                    // < that is a list,\n *       RadioId::staticMetaObject,           // < holding instances of RadioId,\n *       1, 10,                               //   of at least one but max 10 elements\n *       new RadioLimitObject {               // < of objects, with\n *        { \"name\",                           // < a name\n *          new RadioLimitString(1, 16) },    //   being a string between 1 and 10 chars\n *        { \"id\",                             // < and an ID\n *          new RadioLimitUInt(0, 16777216) } //   being an unsigned integer between 0 and 16777216\n *       }\n *     )\n *   }\n * };\n * @endcode\n *\n * @ingroup conf */\n#ifndef RADIOLIMITS_HH\n#define RADIOLIMITS_HH\n\n#include <QObject>\n#include <QRegularExpression>\n#include <QTextStream>\n#include <QMetaType>\n#include <QSet>\n\n#include \"frequency.hh\"\n#include \"ranges.hh\"\n\n// Forward declaration\nclass Config;\nclass ConfigItem;\nclass ConfigObject;\nclass RadioLimits;\n\n\n/** Represents a single issue found during verification.\n * @ingroup limits */\nclass RadioLimitIssue: public QTextStream\n{\npublic:\n  /** Defines the possible severity levels. */\n  enum Severity {\n    Silent,  ///< The user will not be notified.\n    Hint,    ///< Just a hint, a working codeplug will be assembled.\n    Warning, ///< The codeplug gets changed but a working codeplug might be assembled.\n    Critical ///< Assembly of the codeplug will fail or a non-functional codeplug might be created.\n  };\n\npublic:\n  /** Constructs an empty message for the specified severity at the specified point of the\n   * verification. */\n  RadioLimitIssue(Severity severity, const QStringList &stack);\n  /** Copy constructor. */\n  RadioLimitIssue(const RadioLimitIssue &other);\n\n  /** Copy assignment. */\n  RadioLimitIssue &operator =(const RadioLimitIssue &other);\n  /** Set message. */\n  RadioLimitIssue &operator =(const QString &message);\n\n  /** Returns the severity of the issue. */\n  Severity severity() const;\n  /** Returns the text message. */\n  const QString &message() const;\n  /** Returns the traceback of the issue. */\n  const QStringList &stack() const;\n  /** Formats the message. */\n  QString format() const;\n\nprotected:\n  /** Holds the severity of the issue. */\n  Severity _severity;\n  /** Holds the item-stack (where the issue occurred). */\n  QStringList _stack;\n  /** Holds the text message. */\n  QString _message;\n};\n\n\n/** Collects the issues found during verification.\n * This class also tracks where the issues arise.\n *\n * @ingroup limits */\nclass RadioLimitContext\n{\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitContext(bool ignoreFrequencyLimits=false);\n\n  /** Constructs a new message and puts it into the list of issues. */\n  RadioLimitIssue &newMessage(RadioLimitIssue::Severity severity = RadioLimitIssue::Hint);\n\n  /** Returns the number of issues. */\n  int count() const;\n  /** Returns the n-th issue. */\n  const RadioLimitIssue &message(int n) const;\n\n  /** Push a property name/element index onto the stack.\n   * This method is used to track the origin of an issue. */\n  void push(const QString &element);\n  /** Pops the top-most property name/element index from the stack. */\n  void pop();\n\n  /** If @c true, frequency limit voilations are warnings. */\n  bool ignoreFrequencyLimits() const;\n  /** Enables/disables that frequency range voilations are handled as warnings. */\n  void enableIgnoreFrequencyLimits(bool enable=true);\n\n  /** Returns the highest severity of the messages. */\n  RadioLimitIssue::Severity maxSeverity() const;\n\nprotected:\n  /** The current item stack. */\n  QStringList _stack;\n  /** The list of issues found. */\n  QList<RadioLimitIssue> _messages;\n  /** If @c true, any frequency range voilation is a warning. */\n  bool _ignoreFrequencyLimits;\n  /** Holds the highest severity of all messages. */\n  RadioLimitIssue::Severity _maxSeverity;\n};\n\n\n/** Abstract base class for all radio limits.\n *\n * @ingroup limits */\nclass RadioLimitElement: public QObject\n{\n  Q_OBJECT\n\npublic:\n  /** Initializer lists of ConfigItem properties. */\n  typedef std::initializer_list< std::pair<QString, RadioLimitElement *> > PropList;\n\nprotected:\n  /** Hidden constructor. */\n  explicit RadioLimitElement(QObject *parent=nullptr);\n\npublic:\n  /** Verifies the given property of the specified item.\n   * This method gets implemented by the specialized classes to implement the actual verification. */\n  virtual bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const = 0;\n\npublic:\n  /** Destructor. */\n  virtual ~RadioLimitElement();\n};\n\n\n/** Base class to verify values.\n *\n * That is, the verification of strings, integers, floats, etc.\n * @ingroup limits */\nclass RadioLimitValue: public RadioLimitElement\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit RadioLimitValue(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint,\n                           QObject *parent=nullptr);\n\nprotected:\n  /** The severity of the issue, if the test fails. */\n  RadioLimitIssue::Severity _severity;\n};\n\n\n/** Checks a string valued property.\n *\n * Instances of this class can be used to verify string values. That is, checking the length of the\n * string and its encoding.\n *\n * @ingroup limits */\nclass RadioLimitString: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Possible encoding of strings. */\n  enum Encoding {\n    DTMF,    ///< Just DTMF symbols are allowed (0-9, A-D, *, #).\n    ASCII,   ///< Just ASCII is allowed.\n    Unicode  ///< Any Unicode character is allowed.\n  };\n\npublic:\n  /** Constructor.\n   * @param minLen Specifies the minimum length of the string. If -1, check is disabled.\n   * @param maxLen Specifies the maximum length of the string. If -1, check is disabled.\n   * @param enc Specifies the allowed string encoding.\n   * @param parent Specifies the QObject parent object. */\n  RadioLimitString(int minLen, int maxLen, Encoding enc,\n                   RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint,\n                   QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the minimum length of the string. If -1, the check is disabled. */\n  int _minLen;\n  /** Holds the maximum length of the string. If -1, the check is disabled. */\n  int _maxLen;\n  /** Holds the allowed character encoding. */\n  Encoding _encoding;\n};\n\n\n/** Verifies that a string matches a regular expression.\n * @ingroup limits */\nclass RadioLimitStringRegEx: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param pattern Specifies the regular expression pattern, the string must match.\n   * @param severity Specifies the severity of the issue.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitStringRegEx(const QString &pattern,\n                        RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                        QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the regular expression pattern. */\n  QRegularExpression _pattern;\n};\n\n\n/** Verifies string values containing only numbers.\n * @ingroup limits */\nclass RadioLimitPin: public RadioLimitStringRegEx\n{\n  Q_OBJECT\n\npublic:  /** Constructor.\n   * @param length Specifies the maximum length of the pin. If -1, length is not limited.\n   * @param severity Specifies the severity of the issue.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitPin(int length = -1,\n                RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                QObject *parent=nullptr);\n};\n\n\n/** Notifies the user that a string gets ignored.\n * This is usually the case, when named elements are referenced within the codeplug by index.\n * @ingroup limits */\nclass RadioLimitStringIgnored: public RadioLimitValue {\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  RadioLimitStringIgnored(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n};\n\n\n/** Checks if a property is a boolean value.\n * @ingroup limits */\nclass RadioLimitBool: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RadioLimitBool(QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n};\n\n\n/** Specifies an boolean value as ignored.\n * If the boolean value is @c true, a message is generated indicating that this property is ignored.\n * If the value is @c false, nothing happens. */\nclass RadioLimitIgnoredBool: public RadioLimitBool\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param notify Specifies the severity of the generated message.\n   * @param parent Specifies the QObject parent. */\n  explicit RadioLimitIgnoredBool(RadioLimitIssue::Severity notify=RadioLimitIssue::Hint,\n                                 QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** The severity of the issue generated. */\n  RadioLimitIssue::Severity _severity;\n};\n\n\n/** Represents a limit for an unsigned integer value.\n * @ingroup limits */\nclass RadioLimitUInt: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param minValue Specifies the minimum value. If -1, no check is performed.\n   * @param maxValue Specifies the maximum value. If -1, no check is performed.\n   * @param defValue Specifies the default value. If -1, no default value is set.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitUInt(qint64 minValue=-1, qint64 maxValue=-1, qint64 defValue=-1,\n                 RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint,\n                 QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the minimum value. If -1, the check is disabled. */\n  qint64 _minValue;\n  /** Holds the maximum value. If -1, the check is disabled. */\n  qint64 _maxValue;\n  /** Holds the default value. If -1, no default value is set. */\n  qint64 _defValue;\n};\n\n\n/** Represents a DMR ID.\n *  That is an uint between 1 and 16777215 without any default value.\n * @ingroup limits */\nclass RadioLimitDMRId: public RadioLimitUInt\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param parent Specifies the QObject parent. */\n  explicit RadioLimitDMRId(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint,\n                           QObject *parent=nullptr);\n};\n\n\n/** Represents a limit for a set of enum values.\n * @ingroup limits */\nclass RadioLimitEnum: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor from initializer list of possible enum values. */\n  RadioLimitEnum(const std::initializer_list<unsigned> &values,\n                 RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Hint,\n                 QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the set of valid values. */\n  QSet<unsigned> _values;\n};\n\n\n/** Represents a limit on frequencies in MHz.\n * @ingroup limits */\nclass RadioLimitFrequencies: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Typedef for the initializer list. */\n  typedef std::initializer_list<std::pair<Frequency, Frequency>> RangeList;\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitFrequencies(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                                 QObject *parent=nullptr);\n  /** Constructor from initializer list. */\n  RadioLimitFrequencies(const RangeList &ranges,\n                        RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                        QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the frequency ranges for the device. */\n  QList<FrequencyRange> _frequencyRanges;\n};\n\n\n/** Specialization for transmit frequency limits.\n * The verification is only performed if the channel is not \"RX Only\".\n * @ingroup limits */\nclass RadioLimitTransmitFrequencies: public RadioLimitFrequencies\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitTransmitFrequencies(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                                         QObject *parent=nullptr);\n  /** Constructor from initializer list. */\n  RadioLimitTransmitFrequencies(const RangeList &ranges,\n                                RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                                QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n};\n\n\n/** Represents a limit on intervals.\n * @ingroup limits */\nclass RadioLimitInterval: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitInterval(RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                              QObject *parent=nullptr);\n  /** Constructor from range. */\n  RadioLimitInterval(const Range<Interval> &range,\n                     RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                     QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the duration ranges for the interval. */\n  Range<Interval> _durations;\n};\n\n\n/** Represents a limit on levels.\n * @ingroup limits */\nclass RadioLimitLevel: public RadioLimitValue\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitLevel(const Range<unsigned int> &range = {0, 10}, bool allowInvalid=true,\n                           RadioLimitIssue::Severity severity=RadioLimitIssue::Severity::Warning,\n                           QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  Range<unsigned int> _range;\n  bool _allowInvalid;\n};\n\n\n/** Represents the limits for a @c ConfigItem instance.\n *\n * That is, it holds the limits for every property of the @c ConfigItem instance. This class\n * provides a initializer list constructor for easy programmatic construction of limits.\n *\n * @ingroup limits */\nclass RadioLimitItem: public RadioLimitElement\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitItem(QObject *parent=nullptr);\n  /** Constructor from initializer list.\n   * The ownership of all passed elements are taken. */\n  RadioLimitItem(const PropList &list, QObject *parent=nullptr);\n\n  /** Adds a property declaration.\n   *\n   * The item takes the ownership of the structure declaration. If a property is already defined\n   * with the same name, @c false is returned.\n   *\n   * @param prop Specifies the name of the property.\n   * @param structure Specifies the structure declaration of the property value.\n   * @returns @c false If a property with the same name is already defined. */\n  bool add(const QString &prop, RadioLimitElement *structure);\n\n  virtual bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n  /** Verifies the properties of the given item. */\n  virtual bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the property <-> limits map. */\n  QHash<QString, RadioLimitElement *> _elements;\n};\n\n\n/** Represents the limits for all properties of a @c ConfigObject instance.\n * @ingroup limits */\nclass RadioLimitObject: public RadioLimitItem\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimitObject(QObject *parent=nullptr);\n  /** Constructor from initializer list.\n   * The ownership of all passed elements are taken. */\n  RadioLimitObject(const PropList &list, QObject *parent=nullptr);\n\n  /** Verifies the properties of the given object. */\n  virtual bool verifyObject(const ConfigObject *item, RadioLimitContext &context) const;\n};\n\n\n/** Represents an ignored element in the codeplug.\n *\n * Instances of this class might be used to inform the user about a configured feature not present\n * in the particular radio.\n *\n * @ingroup limits */\nclass RadioLimitIgnored: public RadioLimitObject\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor for a ignored setting verification element. */\n  RadioLimitIgnored(RadioLimitIssue::Severity notify=RadioLimitIssue::Hint, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n  bool verifyObject(const ConfigObject *item, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the level of the notification. */\n  RadioLimitIssue::Severity _notification;\n};\n\n\n/** Dispatch by class.\n *\n * Sometimes, a property may hold objects of different type. In these cases, a dispatcher is needed\n * to specify which limits to apply based on the type of the object. This class implements this\n * dispatcher.\n *\n * @ingroup limits */\nclass RadioLimitObjects: public RadioLimitObject\n{\n  Q_OBJECT\n\npublic:\n  /** Initializer lists of type properties. */\n  typedef std::initializer_list<std::pair<const QMetaObject&, RadioLimitObject *> > TypeList;\n\npublic:\n  /** Constructor from initializer list.\n   *\n   * A list of pairs of a @c QMetaObject and a @c RadioLimitObject must be given. The meta object\n   * specifies the type of the @c ConfigObject and the associated @c RadioLimitObject the limits\n   * for this type.\n   *\n   * A dispatch for Analog and DigitalChannel may look like\n   * @code\n   * new RadioLimitObjects{\n   *   { AnalogChannel::staticMetaObject, new RadioLimitObject{\n   *       // Limits for analog channel objects\n   *     } },\n   *   {DigialChannel::staticMetaObject, new RadioLimitObject{\n   *       // Limits for digital channel objects\n   *     } }\n   * };\n   * @endcode\n   */\n  RadioLimitObjects(const TypeList &list, QObject *parent=nullptr);\n\n  bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const;\n\nprotected:\n  /** Maps class-names to object limits. */\n  QHash<QString,  RadioLimitObject *> _types;\n};\n\n\n/** Limits the possible classes a @c ConfigObjectReference may refer to.\n * @ingroup limits */\nclass RadioLimitObjRef: public RadioLimitElement\n{\n  Q_OBJECT\n\npublic:\n  /** Initializer lists of allowed classes. */\n  typedef std::initializer_list<QMetaObject> MetaObjectList;\n\npublic:\n  /** Constructor.\n   * @param type Specifies the type that might be referenced.\n   * @param allowNull If @c true, the reference may be a nullptr.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitObjRef(const QMetaObject &type, bool allowNull=true, QObject *parent=nullptr);\n\n  /** Constructor.\n   * @param types Specifies the types that might be referenced.\n   * @param allowNull If @c true, the reference may be a nullptr.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitObjRef(const MetaObjectList &types, bool allowNull=true, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Checks if the given type is one of the valid ones in @c _types. */\n  bool validType(const QMetaObject *type) const;\n\nprotected:\n  /** If @c true, a null reference is allowed. */\n  bool _allowNull;\n  /** Possible classes of instances, the reference may point to. */\n  QSet<QString> _types;\n};\n\n\n/** Issues a notification if a reference is set.\n * @ingroup limits */\nclass RadioLimitObjRefIgnored: public RadioLimitObjRef\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param defObj Specifies a weak reference to a default object that gets silently ignored.\n   * @param notify Specifies the issue severity.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitObjRefIgnored(\n      ConfigObject *defObj=nullptr, RadioLimitIssue::Severity notify=RadioLimitIssue::Hint,\n      QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** The severity of the issue. */\n  RadioLimitIssue::Severity _severity;\n  /** A weak reference to a default value, that gets silently ignored. */\n  ConfigObject *_default;\n};\n\n\n/** Specifies the limits for a list of @c ConfigObject instances.\n * @ingroup limits */\nclass RadioLimitList: public RadioLimitElement\n{\n  Q_OBJECT\n\npublic:\n  /** Helper struct to pass list entry definitions */\n  struct ElementLimits {\n    const QMetaObject &type;      ///< The type of the object\n    int minCount;                 ///< Minimum count of elements.\n    int maxCount;                 ///< Maximum count of elements.\n    RadioLimitObject *structure;  ///< The structure of the elements.\n  };\n\npublic:\n  /** Constructor.\n   * @param type Specifies the type of the elements.\n   * @param minSize Specifies the minimum size of the list. If -1, no check is performed.\n   * @param maxSize Specifies the maximum size of the list. If -1, no check is performed.\n   * @param element Specifies the limits for all objects in the list. If the list contains instances\n   *                of different ConfigObject types, use @c RadioLimitObjects dispatcher.\n   * @param parent  Specifies the QObject parent. */\n  RadioLimitList(const QMetaObject &type, int minSize, int maxSize, RadioLimitObject *element, QObject *parent=nullptr);\n  /** Constructor from initializer list. */\n  RadioLimitList(const std::initializer_list<ElementLimits> &elements, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Searches for the specified type or one of its super-clsases in the set of allowed types. */\n  QString findClassName(const QMetaObject &type) const;\n\nprotected:\n  /** Maps typename to element definition. */\n  QHash<QString, RadioLimitObject *> _elements;\n  /** Maps typename to minimum count. */\n  QHash<QString, qint64> _minCount;\n  /** Maps typename to maximum count. */\n  QHash<QString, qint64> _maxCount;\n};\n\n\n/** Implements the limits for reference lists.\n * @ingroup limits */\nclass RadioLimitRefList: public RadioLimitElement\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param minSize Specifies the minimum size of the list. If -1, no check is performed.\n   * @param maxSize Specifies the maximum size of the list. If -1, no check is performed.\n   * @param type Specifies the type, the references must be instances of.\n   * @param parent  Specifies the QObject parent. */\n  RadioLimitRefList(int minSize, int maxSize, const QMetaObject &type, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Checks if the given type is one of the valid ones in @c _types. */\n  bool validType(const QMetaObject *type) const;\n\nprotected:\n  /** Holds the minimum size of the list. */\n  qint64 _minSize;\n  /** Holds the maximum size of the list. */\n  qint64 _maxSize;\n  /** Possible classes of instances, the references may point to. */\n  QSet<QString> _types;\n};\n\n\n/** Implements the limits for a list of references to group call contacts.\n * This is used to restrict the elements of group lists to group calls.\n * @ingroup limits */\nclass RadioLimitGroupCallRefList: public RadioLimitElement\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor\n   * @param minSize Specifies the minimum size of the list. If -1, no check is performed.\n   * @param maxSize Specifies the maximum size of the list. If -1, no check is performed.\n   * @param parent  Specifies the QObject parent. */\n  RadioLimitGroupCallRefList(int minSize, int maxSize, QObject *parent=nullptr);\n\n  bool verify(const ConfigItem *item, const QMetaProperty &prop, RadioLimitContext &context) const;\n\nprotected:\n  /** Holds the minimum size of the list. */\n  qint64 _minSize;\n  /** Holds the maximum size of the list. */\n  qint64 _maxSize;\n};\n\n\n/** Specialized RadioLimitObject handling a zone for radio supporting only a single channel list\n * per zone.\n *\n * Checks if a zone contains two sets of channel lists and notifies the user that the zone gets\n * split.\n *\n * @ingroup limits */\nclass RadioLimitSingleZone: public RadioLimitObject\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param maxSize Specifies the maximum size of the zone. If -1, no check is performed.\n   * @param list Initializer list for further zone properties.\n   * @param parent Specifies the QObject parent. */\n  RadioLimitSingleZone(qint64 maxSize, const PropList &list, QObject *parent=nullptr);\n\n  bool verifyItem(const ConfigItem *item, RadioLimitContext &context) const;\n};\n\n\n/** Represents the limits or the entire codeplug.\n *\n * Use @c Radio::limits to obtain an instance.\n * @ingroup limits */\nclass RadioLimits : public RadioLimitItem\n{\n  Q_OBJECT\n\npublic:\n  /** Empty constructor. */\n  explicit RadioLimits(bool betaWarning, QObject *parent = nullptr);\n  /** Constructor from initializer list. */\n  RadioLimits(const std::initializer_list<std::pair<QString,RadioLimitElement *> > &list, QObject *parent=nullptr);\n\n  /** Verifies the given configuration. */\n  virtual bool verifyConfig(const Config *config, RadioLimitContext &context) const;\n\n  /** Returns @c true if the radio supports a call-sign DB. */\n  bool hasCallSignDB() const;\n  /** Returns @c true if the call-sign DB is implemented. */\n  bool callSignDBImplemented() const;\n  /** Returns the maximum number of entries in the call-sign DB. */\n  unsigned numCallSignDBEntries() const;\n\n  /** Returns @c true if the radio supports satellite config. */\n  bool hasSatelliteConfig() const;\n  /** Returns @c true if satellite config is implemented. */\n  bool satelliteConfigImplemented() const;\n  /** Returns the maximum number of satellites. */\n  unsigned numSatellites() const;\n\nprotected:\n  /** If @c true, a warning is issued that the radio is still under development and not well\n   * tested yet. */\n  bool _betaWarning;\n  /** If @c true, the radio supports a call-sign DB. */\n  bool _hasCallSignDB;\n  /** If @c true, the call-sign is implemented. */\n  bool _callSignDBImplemented;\n  /** Holds the number of possible call-sign DB entries. */\n  unsigned _numCallSignDBEntries;\n  /** If @c true, the radio supports satellite config. */\n  bool _hasSatelliteConfig;\n  /** If @c true, satellite config is implemented. */\n  bool _satelliteConfigImplemented;\n  /** Holds the number of possible satellites. */\n  unsigned _numSatellites;\n};\n\n#endif // RADIOLIMITS_HH\n"
  },
  {
    "path": "lib/radiosettings.cc",
    "content": "#include \"radiosettings.hh\"\n#include \"radioid.hh\"\n#include \"gnsssettings.hh\"\n\n\nRadioSettings::RadioSettings(QObject *parent)\n  : ConfigItem(parent),\n    _power(Channel::Power::High),\n    _transmitTimeOut(Interval::infinity()), _defaultId(new DMRRadioIDReference(this)),\n    _boot(new BootSettings(this)), _audio(new AudioSettings(this)),\n    _tone(new ToneSettings(this)),\n    _gnss(new GNSSSettings(this)), _dmr(new DMRSettings(this)),\n    _tytExtension(nullptr), _radioddityExtension(nullptr),\n    _anytoneExtension(nullptr)\n{\n  connect(_boot, &BootSettings::modified, this, &RadioSettings::modified);\n  connect(_audio, &AudioSettings::modified, this, &RadioSettings::modified);\n  connect(_tone, &ToneSettings::modified, this, &RadioSettings::modified);\n  connect(_gnss, &GNSSSettings::modified, this, &RadioSettings::modified);\n  connect(_dmr, &DMRSettings::modified, this, &RadioSettings::modified);\n}\n\nbool\nRadioSettings::copy(const ConfigItem &other) {\n  const RadioSettings *set = other.as<RadioSettings>();\n  if ((nullptr==set) || (!ConfigItem::copy(other)))\n    return false;\n  if (set->totDisabled())\n    disableTOT();\n  return true;\n}\n\nConfigItem *\nRadioSettings::clone() const {\n  RadioSettings *set = new RadioSettings();\n  if (! set->copy(*this)) {\n    set->deleteLater();\n    return nullptr;\n  }\n  return set;\n}\n\nvoid\nRadioSettings::clear() {\n  ConfigItem::clear();\n\n  _power = Channel::Power::High;\n  disableTOT();\n  defaultIdRef()->clear();\n\n  // delete extensions\n  setTyTExtension(nullptr);\n  setRadioddityExtension(nullptr);\n  setAnytoneExtension(nullptr);\n}\n\nChannel::Power\nRadioSettings::power() const {\n  return _power;\n}\nvoid\nRadioSettings::setPower(Channel::Power power) {\n  _power = power;\n  emit modified(this);\n}\n\n\nbool\nRadioSettings::totDisabled() const {\n  return _transmitTimeOut.isInfinite() || _transmitTimeOut.isNull();\n}\n\nInterval\nRadioSettings::tot() const {\n  return _transmitTimeOut;\n}\n\nvoid\nRadioSettings::setTOT(const Interval &sec) {\n  if (_transmitTimeOut == sec)\n    return;\n  _transmitTimeOut = sec;\n  emit modified(this);\n}\n\nvoid\nRadioSettings::disableTOT() {\n  setTOT(Interval::infinity());\n}\n\n\nDMRRadioIDReference *\nRadioSettings::defaultIdRef() const {\n  return _defaultId;\n}\nDMRRadioID *\nRadioSettings::defaultId() const {\n  if (_defaultId->isNull())\n    return nullptr;\n  return _defaultId->as<DMRRadioID>();\n}\nvoid\nRadioSettings::setDefaultId(DMRRadioID *id) {\n  _defaultId->set(id);\n}\n\nBootSettings *\nRadioSettings::boot() const {\n  return _boot;\n}\n\nAudioSettings *\nRadioSettings::audio() const {\n  return _audio;\n}\n\nToneSettings *\nRadioSettings::tone() const {\n  return _tone;\n}\n\nGNSSSettings *\nRadioSettings::gnss() const {\n  return _gnss;\n}\n\nDMRSettings *\nRadioSettings::dmr() const {\n  return _dmr;\n}\n\nTyTSettingsExtension *\nRadioSettings::tytExtension() const {\n  return _tytExtension;\n}\n\nvoid\nRadioSettings::setTyTExtension(TyTSettingsExtension *ext) {\n  if (_tytExtension) {\n    disconnect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n    _tytExtension->deleteLater();\n  }\n  _tytExtension = ext;\n  if (_tytExtension) {\n    _tytExtension->setParent(this);\n    connect(_tytExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n  }\n  emit modified(this);\n}\n\n\nRadiodditySettingsExtension *\nRadioSettings::radioddityExtension() const {\n  return _radioddityExtension;\n}\n\nvoid\nRadioSettings::setRadioddityExtension(RadiodditySettingsExtension *ext) {\n  if (_radioddityExtension) {\n    disconnect(_radioddityExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n    _radioddityExtension->deleteLater();\n  }\n  _radioddityExtension = ext;\n  if (_radioddityExtension) {\n    _radioddityExtension->setParent(this);\n    connect(_radioddityExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n  }\n  emit modified(this);\n}\n\n\nAnytoneSettingsExtension *\nRadioSettings::anytoneExtension() const {\n  return _anytoneExtension;\n}\n\nvoid\nRadioSettings::setAnytoneExtension(AnytoneSettingsExtension *ext) {\n  if (_anytoneExtension) {\n    disconnect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n    _anytoneExtension->deleteLater();\n  }\n  _anytoneExtension = ext;\n  if (_anytoneExtension) {\n    _anytoneExtension->setParent(this);\n    connect(_anytoneExtension, SIGNAL(modified(ConfigItem*)), this, SLOT(onExtensionModified()));\n  }\n  emit modified(this);\n}\n\n\nvoid\nRadioSettings::onExtensionModified() {\n  emit modified(this);\n}\n\n\nbool\nRadioSettings::parse(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  if (! node)\n    return false;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot parse radio settings: Expected object.\";\n    return false;\n  }\n\n  if (! node[\"tot\"]) {\n    disableTOT();\n  } else if (node[\"tot\"] && node[\"tot\"].IsScalar()) {\n    Interval to;\n    if (! to.parse(QString::fromStdString(node[\"tot\"].as<std::string>()), Interval::Format::Seconds))\n      disableTOT();\n    else\n      setTOT(to);\n  }\n\n  /// @todo Remove with 0.17.0. Only parse \"old\" global settings, will be serialized in boot and audio sections.\n  if (Level micLevel; node[\"micLevel\"] && node[\"micLevel\"].IsScalar()\n    && micLevel.parse(QString::fromStdString(node[\"micLevel\"].as<std::string>())))\n    audio()->setMicGain(micLevel);\n  if (node[\"speech\"] && node[\"speech\"].IsScalar())\n    audio()->enableSpeechSynthesis(\"true\" == node[\"speech\"].as<std::string>());\n  if (Level squelch; node[\"squelch\"] && node[\"squelch\"].IsScalar()\n    && squelch.parse(QString::fromStdString(node[\"squelch\"].as<std::string>())))\n    audio()->setSquelch(squelch);\n  if (Level vox; node[\"vox\"] && node[\"vox\"].IsScalar()\n    && vox.parse(QString::fromStdString(node[\"vox\"].as<std::string>())))\n    audio()->setVox(vox);\n  if (node[\"introLine1\"] && node[\"introLine1\"].IsScalar())\n    boot()->setMessage1(QString::fromStdString(node[\"introLine1\"].as<std::string>()));\n  if (node[\"introLine2\"] && node[\"introLine2\"].IsScalar())\n    boot()->setMessage2(QString::fromStdString(node[\"introLine2\"].as<std::string>()));\n  return ConfigItem::parse(node, ctx, err);\n}\n\n\nbool\nRadioSettings::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  if (! ConfigItem::populate(node, context, err))\n    return false;\n\n  if (! totDisabled())\n    node[\"tot\"] = tot().format().toStdString();\n\n  return true;\n}\n"
  },
  {
    "path": "lib/radiosettings.hh",
    "content": "#ifndef RADIOSETTINGS_HH\n#define RADIOSETTINGS_HH\n\n#include \"configobject.hh\"\n#include \"channel.hh\"\n\n#include \"bootsettings.hh\"\n#include \"audiosettings.hh\"\n#include \"tonesettings.hh\"\n#include \"gnsssettings.hh\"\n#include \"dmrsettings.hh\"\n#include \"radioddity_extensions.hh\"\n#include \"anytone_settingsextension.hh\"\n#include \"tyt_extensions.hh\"\n\n\n/** Represents the common radio-global settings.\n * @ingroup conf */\nclass RadioSettings : public ConfigItem\n{\n  Q_OBJECT\n  /** The default channel power */\n  Q_PROPERTY(Channel::Power power READ power WRITE setPower)\n  /** The default transmit timeout */\n  Q_PROPERTY(Interval tot READ tot WRITE setTOT SCRIPTABLE false)\n  /** The default DMR radio ID. */\n  Q_PROPERTY(DMRRadioIDReference *defaultID READ defaultIdRef)\n  /** Common boot settings. */\n  Q_PROPERTY(BootSettings* boot READ boot);\n  /** Common audio and tone settings. */\n  Q_PROPERTY(AudioSettings *audio READ audio);\n  /** Common tone settings. */\n  Q_PROPERTY(ToneSettings *tone READ tone);\n  /** The GNSS settings. */\n  Q_PROPERTY(GNSSSettings *gnss READ gnss);\n  /** The common DMR settings. */\n  Q_PROPERTY(DMRSettings *dmr READ dmr);\n  /** The settings extension for TyT devices. */\n  Q_PROPERTY(TyTSettingsExtension* tyt READ tytExtension WRITE setTyTExtension)\n  /** The settings extension for Radioddity devices. */\n  Q_PROPERTY(RadiodditySettingsExtension * radioddity READ radioddityExtension WRITE setRadioddityExtension)\n  /** Settings for AnyTone devices. */\n  Q_PROPERTY(AnytoneSettingsExtension *anytone READ anytoneExtension WRITE setAnytoneExtension)\n\npublic:\n  /** Default constructor. */\n  explicit RadioSettings(QObject *parent=nullptr);\n\n  bool copy(const ConfigItem &other);\n  ConfigItem *clone() const;\n\n  /** Resets the settings. */\n  void clear();\n\n  /** Returns the default channel power. */\n  Channel::Power power() const;\n  /** Sets the default channel power. */\n  void setPower(Channel::Power power);\n\n  /** Returns @c true if the transmit timeout (TOT) is disabled. */\n  bool totDisabled() const;\n  /** Returns the default transmit timeout (TOT) in seconds, 0=disabled. */\n  Interval tot() const;\n  /** Sets the default transmit timeout (TOT) in seconds, 0=disabled. */\n  void setTOT(const Interval &sec);\n  /** Disables the transmit timeout (TOT). */\n  void disableTOT();\n\n  /** Returns a reference to the default DMR radio Id. */\n  DMRRadioIDReference *defaultIdRef() const;\n  /** Returns the default DMR ID or nullptr, if non is set. */\n  DMRRadioID *defaultId() const;\n  /** Sets the default DMR ID. */\n  void setDefaultId(DMRRadioID *id);\n\n  /** Returns the boot settings. */\n  BootSettings *boot() const;\n  /** Returns the audio/tone settings. */\n  AudioSettings *audio() const;\n  /** Returns the tone settings. */\n  ToneSettings *tone() const;\n  /** Returns the GNSS settings. */\n  GNSSSettings *gnss() const;\n  /** Returns the DMR settings. */\n  DMRSettings *dmr() const;\n\n  /** Returns the TyT device specific radio settings. */\n  TyTSettingsExtension *tytExtension() const;\n  /** Sets the TyT device specific radio settings. */\n  void setTyTExtension(TyTSettingsExtension *ext);\n\n  /** Returns the Radioddity device specific radio settings. */\n  RadiodditySettingsExtension *radioddityExtension() const;\n  /** Sets the Radioddity device specific radio settings. */\n  void setRadioddityExtension(RadiodditySettingsExtension *ext);\n\n  /** Returns the AnyTone device specific radio settings. */\n  AnytoneSettingsExtension *anytoneExtension() const;\n  /** Sets the AnyTone device specific radio settings. */\n  void setAnytoneExtension(AnytoneSettingsExtension *ext);\n\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err=ErrorStack());\n\nprotected:\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected slots:\n  /** Internal used callback whenever an extension is modified. */\n  void onExtensionModified();\n\nprotected:\n  /** Holds the global power setting. */\n  Channel::Power _power;\n  /** Holds the global transmit timeout. */\n  Interval _transmitTimeOut;\n  /** Reference to the default DMR radio ID. */\n  DMRRadioIDReference *_defaultId;\n  /** The boot settings. */\n  BootSettings *_boot;\n  /** The audio/tone settings. */\n  AudioSettings *_audio;\n  /** The tone settings. */\n  ToneSettings *_tone;\n  /** The GNSS settings. */\n  GNSSSettings *_gnss;\n  /** The DMR settings. */\n  DMRSettings *_dmr;\n  /** Device specific settings extension for TyT devices. */\n  TyTSettingsExtension *_tytExtension;\n  /** Device specific settings extension for Radioddity devices. */\n  RadiodditySettingsExtension *_radioddityExtension;\n  /** Device specific settings extension for AnyTone devices. */\n  AnytoneSettingsExtension *_anytoneExtension;\n};\n\n#endif // RADIOCONFIG_HH\n"
  },
  {
    "path": "lib/ranges.cc",
    "content": "#include \"ranges.hh\"\n"
  },
  {
    "path": "lib/ranges.hh",
    "content": "#ifndef RANGES_HH\n#define RANGES_HH\n\n#include <initializer_list>\n#include \"interval.hh\"\n#include \"frequency.hh\"\n\n/** Simple range class representing some range in some data type. Provides some methods to limit\n * values to these ranges.\n *\n * @ingroup utils */\ntemplate <class T>\nclass Range\n{\npublic:\n  /** Maps a given value onto the range. */\n  inline T map(const T &n) const {\n    if (n < lower)\n      return lower;\n    if (upper < n)\n      return upper;\n    return n;\n  }\n\n  /** Checks, if the given value lays within the range. */\n  inline bool contains(const T &n) const {\n    return ((lower<n) || (lower==n)) && ((n<upper) || (n==upper));\n  }\n\npublic:\n  T lower;   ///< Specifies the lower bound.\n  T upper;   ///< Specifies the upper bound.\n};\n\n\n/** Implements a set of possibly overlapping ranges. */\ntemplate <class T>\nclass Ranges\n{\npublic:\n  /** Checks, if the given value lays within the range. */\n  inline bool contains(const T &n) const {\n    for (auto range: ranges) {\n      if (range.contains(n))\n        return true;\n    }\n    return false;\n  }\n\npublic:\n  /** The set of ranges. */\n  QSet<Range<T>> ranges;\n};\n\n\n/** An integer range. */\ntypedef Range<unsigned int> IntRange;\n/** A time range. */\ntypedef Range<Interval>     TimeRange;\n/** A frequency range. */\ntypedef Range<Frequency>    FrequencyRange;\n\n#endif // RANGES_HH\n"
  },
  {
    "path": "lib/rd5r.cc",
    "content": "#include \"rd5r.hh\"\n#include \"config.hh\"\n#include \"radiolimits.hh\"\n#include \"rd5r_limits.hh\"\n\n#define BSIZE 128\n\n\nRadioLimits *RD5R::_limits = nullptr;\n\nRD5R::RD5R(RadioddityInterface *device, QObject *parent)\n  : RadioddityRadio(device, parent), _name(\"Baofeng/Radioddity RD-5R\"), _codeplug()\n{\n  // pass...\n}\n\nRD5R::~RD5R() {\n  // pass...\n}\n\nconst QString &\nRD5R::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nRD5R::limits() const {\n  if (nullptr == _limits)\n    _limits = new RD5RLimits();\n  return *_limits;\n}\n\nconst Codeplug &\nRD5R::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nRD5R::codeplug() {\n  return _codeplug;\n}\n\nRadioInfo\nRD5R::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::RD5R, \"rd5r\", \"RD-5R\", \"Radioddity\", {RadioddityInterface::interfaceInfo()});\n}\n"
  },
  {
    "path": "lib/rd5r.hh",
    "content": "/** @defgroup rd5r Baofeng/Radioddity RD-5R\n * Device specific classes for Baofeng/Radioddity RD-5R.\n *\n * \\image html rd5r.jpg \"RD-5R\" width=200px\n * \\image latex rd5r.jpg \"RD-5R\" width=200px\n *\n * The Baofeng/Radioddity RD-5R radio is likely the cheapest fully DMR compatiple (Tier I&II) VHF/UHF\n * radio on the market. Consequently, it is quiet popular and is usually the first DMR radio\n * many operators may buy. In my opinion it is a decent radio with reasonable sensitivity and audio\n * quality (for a handheld). However, the receiver frontend seems to be identical to the analog\n * Baofeng handhelds and thus suffers from the same well known issue of blocking whenever a strong\n * signal is nearby (even across bands). But you get a lot for a $70 radio.\n *\n * It features up to 1024 channels organized in 250 zones, where each zone may contain up to\n * 16 channels. The radio is a dual VFO and each VFO might be assigned to a different zone. Hence, a\n * zone is just a single list of up to 16 channels (in contrast to many other radios where a zone\n * contains two lists of channels for each VFO).\n *\n * The radio can also hold up to 255 contacts (actually 256, but due to a bug in the firmware RX is\n * disabled whenever all 256 contacts are set), 64 RX group lists and 250 scanlists.\n *\n * @ingroup radioddity */\n#ifndef RD5R_HH\n#define RD5R_HH\n\n#include \"radioddity_radio.hh\"\n#include \"rd5r_codeplug.hh\"\n\n/** Implements an interface to the Baofeng/Radioddity RD-5R VHF/UHF 5W DMR (Tier I/II) radio.\n *\n * The Baofeng/Radioddity RD-5R radio uses a weird HID (human-interface device, see @c HID and\n * @c HIDevice) protocol for communication. This class implements the communication details with\n * the radio to read and write codeplugs on the device.\n *\n * @ingroup rd5r */\nclass RD5R: public RadioddityRadio\n{\n\tQ_OBJECT\n\npublic:\n  /** Constructor.\n   * Do not call this constructor directly. Consider using the factory method\n   * @c Radio::detect. */\n  RD5R(RadioddityInterface *device=nullptr, QObject *parent=nullptr);\n\n  virtual ~RD5R();\n\n\tconst QString &name() const;\n  const RadioLimits &limits() const;\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  /** Returns the default info about the radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** Device identifier string. */\n\tQString _name;\n  /** Current device specific codeplug. */\n\tRD5RCodeplug _codeplug;\n\nprivate:\n  /** Holds the singleton instance of the radio limits. */\n  static RadioLimits *_limits;\n};\n\n#endif // RD5R_HH\n"
  },
  {
    "path": "lib/rd5r_codeplug.cc",
    "content": "#include \"rd5r_codeplug.hh\"\n#include \"config.hh\"\n#include \"channel.hh\"\n#include \"logger.hh\"\n#include <QDateTime>\n\n\n/* ******************************************************************************************** *\n * Implementation of RD5RCodeplug::ChannelElement\n * ******************************************************************************************** */\nRD5RCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : RadioddityCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nRD5RCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : RadioddityCodeplug::ChannelElement(ptr)\n{\n  // pass...\n}\n\nvoid\nRD5RCodeplug::ChannelElement::clear() {\n  RadioddityCodeplug::ChannelElement::clear();\n  setSquelch(Level::null());\n}\n\nLevel\nRD5RCodeplug::ChannelElement::squelch() const {\n  return Level::fromValue(getUInt8(Offset::squelch()), {1,9});\n}\nvoid\nRD5RCodeplug::ChannelElement::setSquelch(Level level) {\n  setUInt8(Offset::squelch(), level.mapTo({1,9}));\n}\n\nbool\nRD5RCodeplug::ChannelElement::fromChannelObj(const Channel *c, Context &ctx, const ErrorStack& err) {\n  if (! RadioddityCodeplug::ChannelElement::fromChannelObj(c, ctx, err))\n    return false;\n\n  if (c->is<FMChannel>()) {\n    const FMChannel *ac = c->as<FMChannel>();\n    if (ac->defaultSquelch())\n      setSquelch(ctx.config()->settings()->audio()->squelch());\n    else if (ac->squelchDisabled())\n      setSquelch(Level::null());\n    else\n      setSquelch(ac->squelch());\n  } else {\n    // If digital channel, reuse global squelch setting\n    setSquelch(ctx.config()->settings()->audio()->squelch());\n  }\n\n  return true;\n}\n\nChannel *\nRD5RCodeplug::ChannelElement::toChannelObj(Context &ctx, const ErrorStack& err) const {\n  Channel *ch = RadioddityCodeplug::ChannelElement::toChannelObj(ctx, err);\n  if (nullptr == ch)\n    return nullptr;\n\n  if (ch->is<FMChannel>()) {\n    FMChannel *ac = ch->as<FMChannel>();\n    ac->setSquelch(squelch());\n  }\n\n  return ch;\n}\n\nbool\nRD5RCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack& err) const {\n  if (! RadioddityCodeplug::ChannelElement::linkChannelObj(c, ctx, err))\n    return false;\n  /*\n  if (c->is<AnalogChannel>()) {\n    AnalogChannel *ac = c->as<AnalogChannel>();\n    if (ctx.config()->settings()->squelch() == ac->squelch()) {\n      ac->setSquelchDefault();\n    }\n  }\n  */\n  return true;\n}\n\n/* ********************************************************************************************* *\n * Implementation of RD5RCodeplug::TimestampElement\n * ********************************************************************************************* */\nRD5RCodeplug::TimestampElement::TimestampElement(uint8_t *ptr, unsigned size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nRD5RCodeplug::TimestampElement::TimestampElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nRD5RCodeplug::TimestampElement::~TimestampElement() {\n  // pass...\n}\n\nvoid\nRD5RCodeplug::TimestampElement::clear() {\n  set();\n}\n\nQDateTime\nRD5RCodeplug::TimestampElement::get() const {\n  return QDateTime(QDate(getBCD4_be(Offset::year()), getBCD2(Offset::month()), getBCD2(Offset::day())),\n                   QTime(getBCD2(Offset::hour()), getBCD2(Offset::minute())));\n}\nvoid\nRD5RCodeplug::TimestampElement::set(const QDateTime &ts) {\n  setBCD4_be(Offset::year(), ts.date().year());\n  setBCD2(Offset::month(), ts.date().month());\n  setBCD2(Offset::day(), ts.date().day());\n  setBCD2(Offset::hour(), ts.time().hour());\n  setBCD2(Offset::minute(), ts.time().minute());\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of RD5RCodeplug::EncryptionElement\n * ******************************************************************************************** */\nRD5RCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr)\n  : RadioddityCodeplug::EncryptionElement(ptr)\n{\n  // pass...\n}\n\nbool\nRD5RCodeplug::EncryptionElement::isBasicKeySet(unsigned n) const {\n  if (n>0)\n    return false;\n  return RadioddityCodeplug::EncryptionElement::isBasicKeySet(n);\n}\n\nQByteArray\nRD5RCodeplug::EncryptionElement::basicKey(unsigned n) const {\n  if (n>0)\n    return QByteArray();\n  return QByteArray(\"\\x53\\x47\\x4c\\x39\");\n}\n\nvoid\nRD5RCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) {\n  if ((0 != n) || (key != \"\\x53\\x47\\x4c\\x39\")){\n    logError() << \"The RD5R only supports a single fixed DMR basic key '53474c39'.\";\n    return;\n  }\n  RD5RCodeplug::EncryptionElement::setBasicKey(n, key);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of RD5RCodeplug\n * ******************************************************************************************** */\nRD5RCodeplug::RD5RCodeplug(QObject *parent)\n  : RadioddityCodeplug(parent)\n{\n  addImage(\"Radioddity RD5R Codeplug\");\n  image(0).addElement(0x00080, 0x07b80);\n  image(0).addElement(0x08000, 0x16300);\n}\n\nvoid\nRD5RCodeplug::clear() {\n  RadioddityCodeplug::clear();\n  this->clearTimestamp();\n}\n\nbool\nRD5RCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  if (! RadioddityCodeplug::encodeElements(flags, ctx, err))\n    return false;\n\n  // Set timestamp\n  if (! this->encodeTimestamp(err)) {\n    errMsg(err) << \"Cannot encode time-stamp.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nRD5RCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  if (! RadioddityCodeplug::decodeElements(ctx, err))\n    return false;\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearTimestamp() {\n  encodeTimestamp();\n}\n\nbool\nRD5RCodeplug::encodeTimestamp(const ErrorStack &err) {\n  Q_UNUSED(err)\n  TimestampElement(data(Offset::timestamp())).set();\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearGeneralSettings() {\n  TimestampElement(data(Offset::settings())).clear();\n}\n\nbool\nRD5RCodeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  GeneralSettingsElement el(data(Offset::settings()));\n  if (! flags.updateCodeplug())\n    el.clear();\n  return el.fromConfig(ctx, err);\n}\n\nbool\nRD5RCodeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  return GeneralSettingsElement(data(Offset::settings())).updateConfig(ctx, err);\n}\n\nvoid\nRD5RCodeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(Offset::buttons())).clear();\n}\nbool\nRD5RCodeplug::encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  return ButtonSettingsElement(data(Offset::buttons())).encode(ctx, err);\n}\nbool\nRD5RCodeplug::decodeButtonSettings(Context &ctx, const ErrorStack &err) {\n  return ButtonSettingsElement(data(Offset::buttons())).decode(ctx, err);\n}\n\nvoid\nRD5RCodeplug::clearMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\nbool\nRD5RCodeplug::encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  if (! MessageBankElement(data(Offset::messages())).encode(ctx, flags, err)) {\n    errMsg(err) << \"Cannot encode preset messages.\";\n    return false;\n  }\n  return true;\n}\nbool\nRD5RCodeplug::decodeMessages(Context &ctx, const ErrorStack &err) {\n  if (! MessageBankElement(data(Offset::messages())).decode(ctx, err)) {\n    errMsg(err) << \"Cannot decode preset messages.\";\n    return false;\n  }\n  return true;\n}\n\n\nvoid\nRD5RCodeplug::clearContacts() {\n  for (unsigned int i=0; i<Limit::contactCount(); i++)\n    ContactElement(data(Offset::contacts() + i*ContactElement::size())).clear();\n}\n\nbool\nRD5RCodeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    ContactElement el(data(Offset::contacts() + i*ContactElement::size()));\n    el.clear();\n    if (i >= (unsigned int) ctx.count<DigitalContact>())\n      continue;\n    if (! el.fromContactObj(ctx.get<DMRContact>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot encode contact '\" << ctx.get<DMRContact>(i+1)->name()\n                  << \"' at index \" << i+1  << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  /* Unpack Contacts */\n  for (unsigned int i=0; i<Limit::contactCount(); i++) {\n    ContactElement el(data(Offset::contacts() + i*ContactElement::size()));\n    if (! el.isValid())\n      continue;\n\n    DMRContact *cont = el.toContactObj(ctx);\n    ctx.add(cont, i+1); ctx.config()->contacts()->add(cont);\n  }\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearDTMFContacts() {\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++)\n    DTMFContactElement(data(Offset::dtmfContacts() + i*DTMFContactElement::size())).clear();\n}\n\nbool\nRD5RCodeplug::encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++) {\n    DTMFContactElement el(data(Offset::dtmfContacts() + i*DTMFContactElement::size()));\n    el.clear();\n    if (i >= (unsigned int) ctx.count<DTMFContact>())\n      continue;\n    if (! el.fromContactObj(ctx.get<DTMFContact>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot encode DTMF contact '\" << ctx.get<DTMFContact>(i+1)->name()\n                  << \"' at index \" << i+1 << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::createDTMFContacts(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  for (unsigned int i=0; i<Limit::dtmfContactCount(); i++) {\n    DTMFContactElement el(data(Offset::dtmfContacts()+i*DTMFContactElement::size()));\n    // If contact is disabled\n    if (! el.isValid())\n      continue;\n    DTMFContact *cont = el.toContactObj(ctx, err);\n    if (nullptr == cont) {\n      errMsg(err) << \"Cannot decode DTMF contact at index \" << i << \".\";\n      return false;\n    }\n    ctx.add(cont, i+1); ctx.config()->contacts()->add(cont);\n  }\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearChannels() {\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr); bank.clear();\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++)\n      ChannelElement(bank.get(i)).clear();\n  }\n}\n\nbool\nRD5RCodeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr); bank.clear();\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      ChannelElement el(bank.get(i));\n      if (c < ctx.count<Channel>()) {\n        if (! el.fromChannelObj(ctx.get<Channel>(c+1), ctx)) {\n          errMsg(err) << \"Cannot encode channel \" << c << \" (\" << i << \" of bank \" << b <<\").\";\n          return false;\n        }\n        bank.enable(i,true);\n      } else {\n        el.clear();\n        bank.enable(i, false);\n      }\n    }\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr);\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      if (! bank.isEnabled(i))\n        continue;\n      Channel *ch = ChannelElement(bank.get(i)).toChannelObj(ctx, err);\n      if (nullptr == ch) {\n        errMsg(err) << \"Cannot create channel at index \" << i << \".\";\n        return false;\n      }\n      ctx.config()->channelList()->add(ch); ctx.add(ch, c+1);\n    }\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  for (unsigned int b=0,c=0; b<Limit::channelBankCount(); b++) {\n    uint8_t *ptr = nullptr;\n    if (0 == b) ptr = data(Offset::channelBank0());\n    else ptr = data(Offset::channelBank1() + (b-1)*ChannelBankElement::size());\n    ChannelBankElement bank(ptr);\n    for (unsigned int i=0; (i<ChannelBankElement::Limit::channelCount())&&(c<Limit::channelCount()); i++, c++) {\n      if (! bank.isEnabled(i))\n        continue;\n      if (!ChannelElement(bank.get(i)).linkChannelObj(ctx.get<Channel>(c+1), ctx, err))\n        return false;\n    }\n  }\n  return true;\n}\n\n\nvoid\nRD5RCodeplug::clearMenuSettings() {\n  MenuSettingsElement(data(Offset::menuSettings())).clear();\n}\n\nvoid\nRD5RCodeplug::clearBootSettings() {\n  BootSettingsElement(data(Offset::bootSettings())).clear();\n  BootTextElement(data(Offset::bootText())).clear();\n}\n\nbool\nRD5RCodeplug::encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  BootSettingsElement(data(Offset::bootSettings())).encode(ctx, err);\n  BootTextElement(data(Offset::bootText())).fromConfig(ctx, err);\n  return true;\n}\n\nbool\nRD5RCodeplug::decodeBootSettings(Context &ctx, const ErrorStack &err) {\n  BootSettingsElement(data(Offset::bootSettings())).decode(ctx, err);\n  BootTextElement(data(Offset::bootText())).updateConfig(ctx, err);\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearVFOSettings() {\n  ChannelElement(data(Offset::vfoA())).clear();\n  ChannelElement(data(Offset::vfoB())).clear();\n}\n\nvoid\nRD5RCodeplug::clearZones() {\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  bank.clear();\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++)\n    ZoneElement(bank.get(i)).clear();\n}\n\nbool\nRD5RCodeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags)\n\n  ZoneBankElement bank(data(Offset::zoneBank()));\n\n  // Pack Zones\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    ZoneElement z(bank.get(i));\n    if (! ctx.has<Zone>(i+1)) {\n      bank.enable(i, false);\n      continue;\n    }\n\n    // Construct from Zone obj\n    z.fromZoneObjA(ctx.get<Zone>(i+1), ctx, err);\n    bank.enable(i, true);\n  }\n\n  return true;\n}\n\nbool\nRD5RCodeplug::createZones(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ZoneElement z(bank.get(i));\n    Zone *zone = z.toZoneObj(ctx, err);\n    ctx.config()->zones()->add(zone);\n    ctx.add(zone, i+1);\n  }\n\n  return true;\n}\n\nbool\nRD5RCodeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  ZoneBankElement bank(data(Offset::zoneBank()));\n  for (unsigned int i=0; i<ZoneBankElement::Limit::zoneCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ZoneElement z(bank.get(i));\n    Zone *zone = ctx.get<Zone>(i+1);\n    if (! z.linkZoneObj(zone, ctx)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearScanLists() {\n  ScanListBankElement bank(data(Offset::scanListBank())); bank.clear();\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++)\n    ScanListElement(bank.get(i)).clear();\n}\n\nbool\nRD5RCodeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (i >= ctx.count<ScanList>()) {\n      bank.enable(i, false);\n      continue;\n    }\n    if (! ScanListElement(bank.get(i)).fromScanListObj(ctx.get<ScanList>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot encode scan list at index \" << i << \".\";\n      return false;\n    }\n    bank.enable(i, true);\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    ScanListElement el(bank.get(i));\n    ScanList *scan = el.toScanListObj(ctx);\n    if (nullptr == scan) {\n      errMsg(err) << \"Cannot decode scan list at index \" << i+1 << \".\";\n      return false;\n    }\n    ctx.config()->scanlists()->add(scan); ctx.add(scan, i+1);\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  ScanListBankElement bank(data(Offset::scanListBank()));\n  for (unsigned int i=0; i<ScanListBankElement::Limit::scanListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    if (! ScanListElement(bank.get(i)).linkScanListObj(ctx.get<ScanList>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot link scan list '\" << ctx.get<ScanList>(i+1)\n                  << \"' at index \" << i+1 << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nRD5RCodeplug::clearGroupLists() {\n  GroupListBankElement bank(data(Offset::groupListBank())); bank.clear();\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++)\n    GroupListElement(bank.get(i)).clear();\n}\n\nbool\nRD5RCodeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  GroupListBankElement bank(data(Offset::groupListBank())); bank.clear();\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (i >= ctx.count<RXGroupList>())\n      continue;\n    GroupListElement el(bank.get(i));\n    el.fromRXGroupListObj(ctx.get<RXGroupList>(i+1), ctx, err);\n    // Only group calls are encoded\n    int count = 0;\n    for (int j=0; j<ctx.get<RXGroupList>(i+1)->count(); j++)\n      if (DMRContact::GroupCall == ctx.get<RXGroupList>(i+1)->contact(j)->type())\n        count++;\n    bank.setContactCount(i, count);\n  }\n\n  return true;\n}\n\nbool\nRD5RCodeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  GroupListBankElement bank(data(Offset::groupListBank()));\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    GroupListElement el(bank.get(i));\n    RXGroupList *list = el.toRXGroupListObj(ctx, err);\n    ctx.config()->rxGroupLists()->add(list); ctx.add(list, i+1);\n  }\n  return true;\n}\n\nbool\nRD5RCodeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  GroupListBankElement bank(data(Offset::groupListBank()));\n  for (unsigned int i=0; i<GroupListBankElement::Limit::groupListCount(); i++) {\n    if (! bank.isEnabled(i))\n      continue;\n    GroupListElement el(bank.get(i));\n    /*logDebug() << \"Link \" << bank.contactCount(i) << \" members of group list '\"\n               << ctx.get<RXGroupList>(i+1)->name() << \"'.\";*/\n    if (! el.linkRXGroupListObj(bank.contactCount(i), ctx.get<RXGroupList>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot link group list '\" << ctx.get<RXGroupList>(i+1)->name() << \"'.\";\n      return false;\n    }\n  }\n  return true;\n}\n\n\nvoid\nRD5RCodeplug::clearEncryption() {\n  EncryptionElement enc(data(Offset::encryption()));\n  enc.clear();\n}\n\nbool\nRD5RCodeplug::encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags);\n  clearEncryption();\n  EncryptionElement enc(data(Offset::encryption()));\n  return enc.fromCommercialExt(ctx.config()->commercialExtension(), ctx, err);\n}\n\nbool\nRD5RCodeplug::createEncryption(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err);\n  EncryptionElement enc(data(Offset::encryption()));\n  if (EncryptionElement::PrivacyType::None == enc.privacyType())\n    return true;\n  return enc.updateCommercialExt(ctx, err);\n}\n\nbool\nRD5RCodeplug::linkEncryption(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx); Q_UNUSED(err);\n  return true;\n}\n"
  },
  {
    "path": "lib/rd5r_codeplug.hh",
    "content": "#ifndef RD5R_CODEPLUG_HH\n#define RD5R_CODEPLUG_HH\n\n#include <QObject>\n#include \"radioddity_codeplug.hh\"\n#include \"signaling.hh\"\n\nclass Channel;\n\n/** Represents, encodes and decodes the device specific codeplug for a Baofeng/Radioddity RD-5R.\n *\n * This codeplug format is quiet funny. It reveals some history of this device. First of all, the\n * channels are organizes in two blocks. The first block contains only a single bank of 128 channels,\n * while the second block contains 7 banks with a total of 896 channels. I would guess there was a\n * previous firmware or even hardware version with only 128 channels.\n *\n * Moreover, channels, zones, rx group lists and scan lists are organized in tables or banks, with\n * some preceding bitfield indicating which channel is enabled/valid. Contacts, however, are just\n * organized in a list, where each entry has a field, indicating whether that contact is valid.\n *\n * This difference looks like, as if the firmware code for the contacts stems from a different\n * device or was developed by a different engineer. Moreover, the message list again, uses yet\n * another method. Here a simple counter precedes the messages, indicating how many valid messages\n * there are. All in all, a rather inconsistent way of representing variable length lists in the\n * codeplug. I would guess, that over time, different people/teams worked on different revisions\n * of the firmware. It must have been a real nightmare to Serge Vakulenko reverse-engineering this\n * codeplug.\n *\n * @section rd5rcpl Codeplug structure within radio\n * This implementation targets firmware version 2.1.6.\n *\n * The memory representation of the codeplug within the radio is divided into two segments.\n * The first segment starts at the address 0x00080 and ends at 0x07c00 while the second section\n * starts at 0x08000 and ends at 0x1e300.\n *\n * Please note, that the codeplug is not yet fully understood and a full codeplug cannot be build\n * from scratch. That is, it is necessary to update an existing codeplug on the radio.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>     <th>Size</th>  <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x00080-0x07c00</th></tr>\n *  <tr><td>0x00080</td> <td>0x00088</td> <td>0x0008</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x00088</td> <td>0x0008e</td> <td>0x0006</td> <td>Timestamp, see @c RadioddityCodeplug::TimestampElement.</td></tr>\n *  <tr><td>0x0008e</td> <td>0x000e0</td> <td>0x0052</td> <td>CPS, firmware, DSP version numbers (not touched).</td></tr>\n *  <tr><td>0x000e0</td> <td>0x00108</td> <td>0x0028</td> <td>General settings, see @c RadioddityCodeplug::GeneralSettingsElement.</td></tr>\n *  <tr><td>0x00108</td> <td>0x00128</td> <td>0x0020</td> <td>Button settings, see @c RadioddityCodeplug::ButtonSettingsElement.</td></tr>\n *  <tr><td>0x00128</td> <td>0x01370</td> <td>0x1248</td> <td>32 preset message texts, see @c RadioddityCodeplug::MessageBankElement.</td></tr>\n *  <tr><td>0x01370</td> <td>0x01588</td> <td>0x0218</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x01588</td> <td>0x01788</td> <td>0x0200</td> <td>??? 32 Emergency systems ???</td></tr>\n *  <tr><td>0x01788</td> <td>0x02f88</td> <td>0x1800</td> <td>256 contacts, see @c RadioddityCodeplug::ContactElement.</td></tr>\n *  <tr><td>0x02f88</td> <td>0x03388</td> <td>0x0400</td> <td>32 DTMF contacts, see @c RadioddityCodeplug::DTMFContactElement.</td></tr>\n *  <tr><td>0x03388</td> <td>0x03780</td> <td>0x03f8</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x03780</td> <td>0x05390</td> <td>0x1c10</td> <td>First 128 channels (bank 0), see @c RadioddityCodeplug::ChannelBankElement\n *                                                            and @c RD5RCodeplug::ChannelElement.</td></tr>\n *  <tr><td>0x05390</td> <td>0x07518</td> <td>0x2188</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07518</td> <td>0x07538</td> <td>0x0020</td> <td>Boot settings, see @c RadioddityCodeplug::BootSettingsElement.</td></tr>\n *  <tr><td>0x07538</td> <td>0x07540</td> <td>0x0008</td> <td>Menu settings, see @c RadioddityCodeplug::MenuSettingsElement.</td></tr>\n *  <tr><td>0x07540</td> <td>0x07560</td> <td>0x0020</td> <td>2 intro lines, @c RadioddityCodeplug::BootTextElement.</td></tr>\n *  <tr><td>0x07560</td> <td>0x07590</td> <td>0x0030</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x07590</td> <td>0x075c8</td> <td>0x0038</td> <td>VFO A settings @c RadioddityCodeplug::ChannelElement</td></tr>\n *  <tr><td>0x075c8</td> <td>0x07600</td> <td>0x0038</td> <td>VFO B settings @c RadioddityCodeplug::ChannelElement</td></tr>\n *  <tr><td>0x07600</td> <td>0x07c00</td> <td>0x0600</td> <td>??? Unknown ???</td></tr>\n *\n *  <tr><th colspan=\"4\">Second segment 0x08000-0x1e300</th></tr>\n *  <tr><td>0x08000</td> <td>0x08010</td> <td>0x0010</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x08010</td> <td>0x0af10</td> <td>0x2f00</td> <td>250 zones, see @c RadioddityCodeplug::ZoneBankElement.</td></tr>\n *  <tr><td>0x0af10</td> <td>0x0b1b0</td> <td>0x02a0</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x0b1b0</td> <td>0x17620</td> <td>0xc470</td> <td>Remaining 896 channels (bank 1-7), see @c RadioddityCodeplug::ChannelBankElement\n *                                                            and @c RD5RCodeplug::ChannelElement.</td></tr>\n *  <tr><td>0x17620</td> <td>0x1cd10</td> <td>0x56f0</td> <td>250 scan lists, see @c RadioddityCodeplug::ScanListBankElement</td></tr>\n *  <tr><td>0x1cd10</td> <td>0x1d620</td> <td>0x0910</td> <td>??? Unknown ???</td></tr>\n *  <tr><td>0x1d620</td> <td>0x1e2a0</td> <td>0x0c80</td> <td>64 RX group lists, see @c RadioddityCodeplug::GroupListBankElement</td></tr>\n *  <tr><td>0x1e2a0</td> <td>0x1e300</td> <td>0x0060</td> <td>??? Unknown ???</td></tr>\n * </table>\n *\n * @ingroup rd5r */\nclass RD5RCodeplug : public RadioddityCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Implements the specialization of the Radioddity channel for the RD5R radio.\n   *\n   * Memory layout of encoded channel:\n   * @verbinclude rd5r_channel.txt */\n  class ChannelElement: public RadioddityCodeplug::ChannelElement\n  {\n  protected:\n    /** Hidden constructor. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns the squelch level. */\n    virtual Level squelch() const;\n    /** Sets the squelch level. */\n    virtual void setSquelch(Level level);\n\n    bool fromChannelObj(const Channel *c, Context &ctx, const ErrorStack &err=ErrorStack());\n    Channel *toChannelObj(Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  protected:\n    /** Internal offsets within the channel element. */\n    struct Offset: RadioddityCodeplug::ChannelElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int squelch() { return 0x0037; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the timestamp for RD-5R codeplugs.\n   *\n   * Encoding of messages (size: 0x0006b):\n   * @verbinclude rd5r_timestamp.txt */\n  class TimestampElement: Element\n  {\n  protected:\n    /** Hidden constructor. */\n    TimestampElement(uint8_t *ptr, unsigned size);\n\n  public:\n    /** Constructor. */\n    explicit TimestampElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~TimestampElement();\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x0006; }\n\n    /** Resets the timestamp. */\n    void clear();\n\n    /** Returns the time stamp. */\n    virtual QDateTime get() const;\n    /** Sets the time stamp. */\n    virtual void set(const QDateTime &ts=QDateTime::currentDateTime());\n\n  protected:\n    /** Internal offsets within the element. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int year() { return 0x0000; }\n      static constexpr unsigned int month() { return 0x0002; }\n      static constexpr unsigned int day() { return 0x0003; }\n      static constexpr unsigned int hour() { return 0x0004; }\n      static constexpr unsigned int minute() { return 0x0005; }\n      /// @endcond\n    };\n  };\n\n\n  /** Implements the encoding/decoding of encryption keys for the RD-5R radio.\n   * @note The RD5R only supports a single basic DMR encryption key with a fixed value!\n   *\n   * Encoding of encryption keys (size: 0x00088):\n   * @verbinclude radioddity_privacy.txt */\n  class EncryptionElement: public RadioddityCodeplug::EncryptionElement\n  {\n  public:\n    /** Constructor. */\n    EncryptionElement(uint8_t *ptr);\n\n    bool isBasicKeySet(unsigned n) const;\n    QByteArray basicKey(unsigned n) const;\n    void setBasicKey(unsigned n, const QByteArray &key);\n  };\n\npublic:\n  /** Empty constructor. */\n  RD5RCodeplug(QObject *parent=0);\n\n  void clear();\n\npublic:\n  bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Clears the time-stamp in the codeplug. */\n  virtual void clearTimestamp();\n  /** Sets the time-stamp. */\n  virtual bool encodeTimestamp(const ErrorStack &err=ErrorStack());\n\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearMessages();\n  bool encodeMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n  bool decodeMessages(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearDTMFContacts();\n  bool encodeDTMFContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createDTMFContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearMenuSettings();\n\n  void clearBootSettings();\n  bool encodeBootSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeBootSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearVFOSettings();\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearEncryption();\n  bool encodeEncryption(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool createEncryption(Context &ctx, const ErrorStack &err);\n  bool linkEncryption(Context &ctx, const ErrorStack &err);\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit {\n    static constexpr unsigned int channelBankCount() { return 8; }   ///< The number of channel banks.\n    static constexpr unsigned int channelCount() { return 1024; }    ///< Maximum number of channels in the codeplug.\n    static constexpr unsigned int contactCount() { return 256; }     ///< Maximum number of DMR contacts.\n    static constexpr unsigned int dtmfContactCount() { return 32; }  ///< Maximum number of DTMF contacts.\n    static constexpr unsigned int zoneCount() { return 250; }        ///< Maximum number of zones.\n  };\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int timestamp()     { return 0x000088; }\n    static constexpr unsigned int settings()      { return 0x0000e0; }\n    static constexpr unsigned int buttons()       { return 0x000108; }\n    static constexpr unsigned int messages()      { return 0x000128; }\n    static constexpr unsigned int encryption()    { return 0x001370; }\n    static constexpr unsigned int contacts()      { return 0x001788; }\n    static constexpr unsigned int dtmfContacts()  { return 0x002f88; }\n    static constexpr unsigned int channelBank0()  { return 0x003780; }\n    static constexpr unsigned int bootSettings()  { return 0x007518; }\n    static constexpr unsigned int menuSettings()  { return 0x007538; }\n    static constexpr unsigned int bootText()      { return 0x007540; }\n    static constexpr unsigned int vfoA()          { return 0x007590; }\n    static constexpr unsigned int vfoB()          { return 0x0075c8; }\n    static constexpr unsigned int zoneBank()      { return 0x008010; }\n    static constexpr unsigned int channelBank1()  { return 0x00b1b0; }\n    static constexpr unsigned int scanListBank()  { return 0x017620; }\n    static constexpr unsigned int groupListBank() { return 0x01d620; }\n    /// @endcond\n  };\n};\n\n#endif // RD5R_CODEPLUG_HH\n"
  },
  {
    "path": "lib/rd5r_filereader.cc",
    "content": "#include \"rd5r_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_ADDR 0x00000080\n#define SEGMENT0_SIZE 0x00007b80\n#define SEGMENT1_ADDR 0x00008000\n#define SEGMENT1_SIZE 0x00016300\n\nbool\nRD5RFileReader::read(const QString &filename, RD5RCodeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (131072 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 131072 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  if (! file.seek(SEGMENT1_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  ptr = (char *)codeplug->data(SEGMENT1_ADDR);\n  n = SEGMENT1_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/rd5r_filereader.hh",
    "content": "#ifndef RD5RFILEREADER_HH\n#define RD5RFILEREADER_HH\n\n#include \"rd5r_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is pretty simple. It is a one-to-one dump of the codeplug\n * data as written to the device. This makes the decoding of the manufacturer codeplug files very\n * easy. Some memory regions, however, are not written to the deivice although they are present in\n * the codeplug file.\n *\n * <table>\n *  <tr><th>Start</th>   <th>End</th>      <th>Size</th></tr>\n *  <tr><td>0x00080</td> <td>0x07c00</td>  <td>0x07b80</td></tr>\n *  <tr><td>0x08000</td> <td>0x1e300</td>  <td>0x16300</td></tr>\n * </table>\n *\n * @ingroup rd5r */\nclass RD5RFileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err Error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, RD5RCodeplug *codeplug, const ErrorStack &err=ErrorStack());\n};\n\n#endif // RD5RFILEREADER_HH\n"
  },
  {
    "path": "lib/rd5r_limits.cc",
    "content": "#include \"rd5r_limits.hh\"\n#include \"rd5r_codeplug.hh\"\n#include \"radioid.hh\"\n#include \"channel.hh\"\n#include \"scanlist.hh\"\n#include \"zone.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"roamingzone.hh\"\n\n\nRD5RLimits::RD5RLimits(QObject *parent)\n  : RadioLimits(false, parent)\n{  \n  // Define limits for call-sign DB\n  _hasCallSignDB          = false;\n  _callSignDBImplemented  = false;\n  _numCallSignDBEntries   = 0;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  /* Define limits for the general settings. */\n  add(\"settings\",\n      new RadioLimitItem{\n        { \"introLine1\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"introLine2\", new RadioLimitString(-1, 16, RadioLimitString::ASCII) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum({unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}) },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(RD5RCodeplug::BootSettingsElement::Limit::passwordLength(), RadioLimitIssue::Critical) } } }\n      });\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\",\n      new RadioLimitList {\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1,8, RadioLimitString::ASCII) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n        /// @todo check default radio ID.\n      });\n\n  /* Define limits for contacts. */\n  add(\"contacts\",\n      new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 256, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum{\n                (unsigned)DMRContact::PrivateCall,\n                    (unsigned)DMRContact::GroupCall,\n                    (unsigned)DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, 0, 8, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n            { \"ring\", new RadioLimitBool() },\n            { \"number\", new RadioLimitStringRegEx(\"^[0-9A-Fa-f]+$\") }\n          } }\n      });\n\n  /* Define limits for group lists. */\n  add(\"groupLists\",\n      new RadioLimitList(\n            RXGroupList::staticMetaObject, 1, 64, new RadioLimitObject {\n              { \"name\", new RadioLimitString(1,16, RadioLimitString::ASCII) },\n              { \"contacts\", new RadioLimitGroupCallRefList(1,16) }\n        }));\n\n  /* Define limits for channel list. */\n  add(\"channels\",\n      new RadioLimitList(\n        Channel::staticMetaObject, 1, 1024, // < up to 1024 channels\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored()}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1,16, RadioLimitString::ASCII)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(470.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored() },\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get()) },\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ));\n\n  /* Define limits for zone list. */\n  add(\"zones\",\n      new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitSingleZone(\n          16, {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) }, // 16 ASCII chars in name\n            { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n          })\n        ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\",\n      new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::ASCII) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, false) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }));\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList(\n         BasicEncryptionKey::staticMetaObject,\n         0, RadioddityCodeplug::EncryptionElement::Limit::keyCount(),\n         new RadioLimitObject {\n           {\"name\", new RadioLimitIgnored()},\n           {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{8}\")}\n         })}\n      });\n\n  /* Ignore positioning systems. */\n  add(\"positioning\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n\n  /* Ignore roaming zones. */\n  add(\"roaming\",\n      new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/rd5r_limits.hh",
    "content": "#ifndef RD5RLIMITS_HH\n#define RD5RLIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the configuration limits for the Radioddity RD-5R.\n * @ingroup rd5r */\nclass RD5RLimits : public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RD5RLimits(QObject *parent=nullptr);\n};\n\n#endif // RD5RLIMITS_HH\n"
  },
  {
    "path": "lib/roamingchannel.cc",
    "content": "#include \"roamingchannel.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingChannel\n * ********************************************************************************************* */\nRoamingChannel::RoamingChannel(QObject *parent)\n  : ConfigObject(parent), _rxFrequency(Frequency::fromHz(0)), _txFrequency(Frequency::fromHz(0)),\n    _overrideColorCode(false), _colorCode(0), _overrideTimeSlot(false),\n    _timeSlot(DMRChannel::TimeSlot::TS1)\n{\n  // pass...\n}\n\nRoamingChannel::RoamingChannel(const RoamingChannel &other, QObject *parent)\n  : ConfigObject(parent)\n{\n  copy(other);\n}\n\nvoid\nRoamingChannel::clear() {\n  ConfigObject::clear();\n  _rxFrequency = _txFrequency = Frequency::fromHz(0);\n  _overrideColorCode = false; _colorCode = 0;\n  _overrideTimeSlot = false; _timeSlot = DMRChannel::TimeSlot::TS1;\n}\n\nConfigItem *\nRoamingChannel::clone() const {\n  RoamingChannel *c = new RoamingChannel();\n  if (! c->copy(*this)) {\n    c->deleteLater();\n    return nullptr;\n  }\n  return c;\n}\n\nFrequency\nRoamingChannel::rxFrequency() const {\n  return _rxFrequency;\n}\nvoid\nRoamingChannel::setRXFrequency(Frequency f) {\n  if (f == _rxFrequency)\n    return;\n  _rxFrequency = f;\n  emit modified(this);\n}\n\nFrequency\nRoamingChannel::txFrequency() const {\n  return _txFrequency;\n}\nvoid\nRoamingChannel::setTXFrequency(Frequency f) {\n  if (f == _txFrequency)\n    return;\n  _txFrequency = f;\n  emit modified(this);\n}\n\nbool\nRoamingChannel::colorCodeOverridden() const {\n  return _overrideColorCode;\n}\nvoid\nRoamingChannel::overrideColorCode(bool override) {\n  if (override == _overrideColorCode)\n    return;\n  _overrideColorCode = override;\n  emit modified(this);\n}\nunsigned int\nRoamingChannel::colorCode() const {\n  return _colorCode;\n}\nvoid\nRoamingChannel::setColorCode(unsigned int cc) {\n  cc = std::min(15U, cc);\n  if (_colorCode == cc)\n    return;\n  _colorCode = cc;\n  emit modified(this);\n}\n\nbool\nRoamingChannel::timeSlotOverridden() const {\n  return _overrideTimeSlot;\n}\nvoid\nRoamingChannel::overrideTimeSlot(bool override) {\n  if (override == _overrideTimeSlot)\n    return;\n  _overrideTimeSlot = override;\n  emit modified(this);\n}\nDMRChannel::TimeSlot\nRoamingChannel::timeSlot() const {\n  return _timeSlot;\n}\nvoid\nRoamingChannel::setTimeSlot(DMRChannel::TimeSlot ts) {\n  if (_timeSlot == ts)\n    return;\n  _timeSlot = ts;\n  emit modified(this);\n}\n\nRoamingChannel *\nRoamingChannel::fromDMRChannel(DMRChannel *ch, DMRChannel* ref) {\n  RoamingChannel *rch = new RoamingChannel();\n  rch->setName(QString(\"R %1\").arg(ch->name()));\n  rch->setRXFrequency(ch->rxFrequency());\n  rch->setTXFrequency(ch->txFrequency());\n  rch->overrideColorCode(true);\n  rch->setColorCode(ch->colorCode());\n  rch->overrideTimeSlot(true);\n  rch->setTimeSlot(ch->timeSlot());\n  if (nullptr != ref) {\n    if (ch->colorCode() == ref->colorCode())\n      rch->overrideColorCode(false);\n    if (ch->timeSlot() == ref->timeSlot())\n      rch->overrideTimeSlot(false);\n  }\n  return rch;\n}\n\nbool\nRoamingChannel::parse(const YAML::Node &node, Context &ctx, const ErrorStack &err) {\n  if (! ConfigObject::parse(node, ctx, err))\n    return false;\n\n  setRXFrequency(node[\"rxFrequency\"].as<Frequency>());\n\n  if (node[\"txFrequency\"].IsNull()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \"Cannot parse roaming channel. No txFrequency specified.\";\n    return false;\n  }\n  setTXFrequency(node[\"txFrequency\"].as<Frequency>());\n\n  if (! node[\"timeSlot\"]) {\n    this->overrideTimeSlot(false);\n  } else {\n    if (! node[\"timeSlot\"].IsScalar()) {\n      errMsg(err) << node[\"timeSlot\"].Mark().line << \":\" << node[\"timeSlot\"].Mark().column\n                  << \"Cannot parse 'timeSlot' of RoamingChannel: time slot is not scalar.\";\n      return false;\n    }\n    QMetaEnum e = QMetaEnum::fromType<DMRChannel::TimeSlot>();\n    std::string key = node[\"timeSlot\"].as<std::string>();\n    bool ok=true; int value = e.keyToValue(key.c_str(), &ok);\n    if (! ok) {\n      QStringList lst;\n      for (int i=0; i<e.keyCount(); i++)\n        lst.push_back(e.key(i));\n      errMsg(err) << node[\"timeSlot\"].Mark().line << \":\" << node[\"timeSlot\"].Mark().column\n                  << \": Unknown key '\" << key.c_str() << \"' for enum 'DMRChannel::TimeSlot'.\"\n                  << \" Expected one of \" << lst.join(\", \") << \".\";\n      return false;\n    }\n\n    // finally set property\n    this->overrideTimeSlot(true);\n    this->setTimeSlot((DMRChannel::TimeSlot)value);\n  }\n\n  if (! node[\"colorCode\"]) {\n    this->overrideTimeSlot(false);\n  } else {\n    if (! node[\"colorCode\"].IsScalar()) {\n      errMsg(err) << node[\"colorCode\"].Mark().line << \":\" << node[\"colorCode\"].Mark().column\n                  << \"Cannot parse 'colorCode' of RoamingChannel: color code is not scalar.\";\n      return false;\n    }\n    // finally set property\n    this->overrideColorCode(true);\n    this->setColorCode(node[\"colorCode\"].as<unsigned>());\n  }\n\n  return true;\n}\n\n\nbool\nRoamingChannel::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  // First, populate scriptable properties\n  if (! ConfigObject::populate(node, context, err))\n    return false;\n\n  // Serialize frequency in MHz\n  node[\"rxFrequency\"] = _rxFrequency;\n  node[\"txFrequency\"] = _txFrequency;\n\n  if (timeSlotOverridden()) {\n    QMetaEnum e = QMetaEnum::fromType<DMRChannel::TimeSlot>();\n    node[\"timeSlot\"] = e.valueToKey((int)timeSlot());\n  }\n\n  if (colorCodeOverridden()) {\n    node[\"colorCode\"] = colorCode();\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingChannelList\n * ********************************************************************************************* */\nRoamingChannelList::RoamingChannelList(QObject *parent)\n  : ConfigObjectList(RoamingChannel::staticMetaObject, parent)\n{\n  // pass...\n}\n\nRoamingChannel *\nRoamingChannelList::channel(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<RoamingChannel>();\n  return nullptr;\n}\n\nint\nRoamingChannelList::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<RoamingChannel>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nRoamingChannelList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node)\n    return nullptr;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create roaming zone: Expected object.\";\n    return nullptr;\n  }\n\n  return new RoamingChannel();\n}\n"
  },
  {
    "path": "lib/roamingchannel.hh",
    "content": "#ifndef ROAMINGCHANNEL_HH\n#define ROAMINGCHANNEL_HH\n\n#include \"channel.hh\"\n\n/** Represents a roaming channel.\n *\n *  That is, an incomplete DMR channel, that overrides some channel\n *  settings to allow for roaming between repeaters. To this end, the roaming channel may override\n *  only those channel settings, that are repeater specific like frequencies and color-codes but\n *  keeps DMR contact settings. Some of these properties are overridden optionally (time slot and\n *  color code) while the RX/TX frequencies are overridden always.\n *\n *  @ingroup config */\nclass RoamingChannel : public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"rch\")\n\n  /** Holds the RX frequency in Hz. */\n  Q_PROPERTY(Frequency rxFrequency READ rxFrequency WRITE setRXFrequency SCRIPTABLE false)\n  /** Holds the TX frequency in Hz. */\n  Q_PROPERTY(Frequency txFrequency READ txFrequency WRITE setTXFrequency SCRIPTABLE false)\n  /** If @c true, the color code of the channel gets overridden by the one specified in @c colorCode. */\n  Q_PROPERTY(bool overrideColorCode READ colorCodeOverridden WRITE overrideColorCode SCRIPTABLE false)\n  /** If @c overrideColorCode is @c true, specifies the color code. */\n  Q_PROPERTY(unsigned int colorCode READ colorCode WRITE setColorCode SCRIPTABLE false)\n  /** If @c true, the time slot of the channel gets overridden by the one specified in @c timeSlot. */\n  Q_PROPERTY(bool overrideTimeSlot READ timeSlotOverridden WRITE overrideTimeSlot SCRIPTABLE false)\n  /** If @c overrideTimeSlot is @c true, specifies the time slot. */\n  Q_PROPERTY(DMRChannel::TimeSlot timeSlot READ timeSlot WRITE setTimeSlot SCRIPTABLE false)\n\npublic:\n  /** Default constructor for a roaming channel. */\n  Q_INVOKABLE explicit RoamingChannel(QObject *parent = nullptr);\n  /** Copy constructor. */\n  RoamingChannel(const RoamingChannel &other, QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n  void clear();\n\n  /** Returns the RX frequency in Hz. */\n  Frequency rxFrequency() const;\n  /** Sets the RX frequency in Hz. */\n  void setRXFrequency(Frequency f);\n  /** Returns the TX frequency in Hz. */\n  Frequency txFrequency() const;\n  /** Sets the TX frequency in Hz. */\n  void setTXFrequency(Frequency f);\n\n  /** Returns @c true, if the color code of the channel gets overridden. */\n  bool colorCodeOverridden() const;\n  /** Enables/disables overriding the color code of the channel. */\n  void overrideColorCode(bool override);\n  /** Returns the color code. */\n  unsigned int colorCode() const;\n  /** Sets the color code. */\n  void setColorCode(unsigned int cc);\n\n  /** Returns @c true, if the time slot of the channel gets overridden. */\n  bool timeSlotOverridden() const;\n  /** Enables/disables overriding the time slot of the channel. */\n  void overrideTimeSlot(bool override);\n  /** Returns the time slot. */\n  DMRChannel::TimeSlot timeSlot() const;\n  /** Sets the time slot. */\n  void setTimeSlot(DMRChannel::TimeSlot ts);\n\n  bool parse(const YAML::Node &node, Context &ctx, const ErrorStack &err);\n\npublic:\n  /** Helper method to construct a Roaming channel from a given DMR channel. Optionally with\n   * reference to a third one. */\n  static RoamingChannel *fromDMRChannel(DMRChannel *ch, DMRChannel *ref=nullptr);\n\nprotected:\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err);\n\nprotected:\n  /** Holds the RX frequency in Hz. */\n  Frequency _rxFrequency;\n  /** Holds the TX frequency in Hz. */\n  Frequency _txFrequency;\n  /** If @c true, the color code of the channel gets overridden by the one specified in @c _colorCode. */\n  bool _overrideColorCode;\n  /** If @c _overrideColorCode is @c true, specifies the color code. */\n  unsigned int _colorCode;\n  /** If @c true, the time slot of the channel gets overridden by the one specified in @c _timeSlot. */\n  bool _overrideTimeSlot;\n  /** If @c _overrideTimeSlot is @c true, specifies the time slot. */\n  DMRChannel::TimeSlot _timeSlot;\n};\n\n\n/** Represents the list of roaming channels within the abstract device configuration.\n *\n * @ingroup config */\nclass RoamingChannelList: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RoamingChannelList(QObject *parent=nullptr);\n\n  /** Returns the roaming channel at the given index. */\n  RoamingChannel *channel(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n#endif // ROAMINGCHANNEL_HH\n"
  },
  {
    "path": "lib/roamingzone.cc",
    "content": "#include \"roamingzone.hh\"\n#include \"channel.hh\"\n#include <QSet>\n#include \"config.hh\"\n\n/* ********************************************************************************************* *\n * Implementation of RoamingZone\n * ********************************************************************************************* */\nRoamingZone::RoamingZone(QObject *parent)\n  : ConfigObject(\"roam\", parent), _channel()\n{\n  // pass...\n}\n\nRoamingZone::RoamingZone(const QString &name, QObject *parent)\n  : ConfigObject(name, parent), _channel()\n{\n  // pass...\n}\n\nRoamingZone &\nRoamingZone::operator =(const RoamingZone &other) {\n  copy(other);\n  return *this;\n}\n\nConfigItem *\nRoamingZone::clone() const {\n  RoamingZone *z = new RoamingZone();\n  if (! z->copy(*this)) {\n    z->deleteLater();\n    return nullptr;\n  }\n  return z;\n}\n\nint\nRoamingZone::count() const {\n  return _channel.count();\n}\n\nvoid\nRoamingZone::clear() {\n  _channel.clear();\n}\n\nbool\nRoamingZone::contains(const RoamingChannel *ch) const {\n  for (int i=0; i<count(); i++)\n    if (ch == channel(i))\n      return true;\n  return false;\n}\n\nRoamingChannel*\nRoamingZone::channel(int idx) const {\n  if ((idx < 0) || (idx >= count()))\n    return nullptr;\n  return _channel.get(idx)->as<RoamingChannel>();\n}\n\nint\nRoamingZone::addChannel(RoamingChannel* ch, int row) {\n  row = _channel.add(ch, row);\n  if (0 > row)\n    return row;\n  emit modified(this);\n  return row;\n}\n\nbool\nRoamingZone::remChannel(int row) {\n  return _channel.del(_channel.get(row));\n}\n\nbool\nRoamingZone::remChannel(RoamingChannel* ch) {\n  return _channel.del(ch);\n}\n\nconst RoamingChannelRefList *\nRoamingZone::channels() const {\n  return &_channel;\n}\n\nRoamingChannelRefList*\nRoamingZone::channels() {\n  return &_channel;\n}\n\nbool\nRoamingZone::link(const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  // First, run default link\n  if (! ConfigObject::link(node, ctx, err))\n    return false;\n\n  // Handle channel references separately\n\n  if (! node[\"channels\"])\n    return true;\n\n  // check type\n  if (! node[\"channels\"].IsSequence()) {\n    errMsg(err) << node[\"channels\"].Mark().line << \":\" << node[\"channels\"].Mark().column\n                << \": Cannot link 'channels' of 'RoamingZone': Expected sequence.\";\n    return false;\n  }\n  YAML::Node lst = node[\"channels\"];\n  for (YAML::const_iterator it=lst.begin(); it!=lst.end(); it++) {\n    if (! it->IsScalar()) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot link 'channels' of 'RoamingZone': Expected ID string.\";\n      return false;\n    }\n    QString id = QString::fromStdString(it->as<std::string>());\n    if (! ctx.contains(id)) {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot link 'channels' of 'RoamingZone': Reference '\"\n                  << id << \"' not defined.\";\n      return false;\n    }\n    // Handle referenced object (either DMR channel or roaming channel)\n    ConfigObject *obj = ctx.getObj(id);\n    if (obj->is<DMRChannel>()) {\n      RoamingChannel *rch = RoamingChannel::fromDMRChannel(obj->as<DMRChannel>());\n      config()->roamingChannels()->add(rch);\n      addChannel(rch);\n    } else if (obj->is<RoamingChannel>()) {\n      addChannel(obj->as<RoamingChannel>());\n    } else {\n      errMsg(err) << it->Mark().line << \":\" << it->Mark().column\n                  << \": Cannot link 'channels' of 'RoamingZone': \"\n                  << \"Cannot add reference to '\" << id << \"' to list. \"\n                  << \"Not a roaming channel.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nRoamingZone::populate(YAML::Node &node, const Context &context, const ErrorStack &err) {\n  if (! ConfigObject::populate(node, context, err))\n    return false;\n\n  // Serialize list of channel references.\n  for (int i=0; i<count(); i++) {\n    if (! context.contains(channel(i))) {\n      errMsg(err) << \"Cannot store reference to roaming channel '\" << channel(i)->name()\n                  << \"': No ID assigned.\";\n      return false;\n    }\n    node[\"channels\"].push_back(context.getId(channel(i)).toStdString());\n  }\n\n  return true;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of DefaultRoamingZone\n * ********************************************************************************************* */\nDefaultRoamingZone *DefaultRoamingZone::_instance = nullptr;\n\nDefaultRoamingZone::DefaultRoamingZone(QObject *parent)\n  : RoamingZone(tr(\"[Default]\"), parent)\n{\n  // pass...\n}\n\nDefaultRoamingZone *\nDefaultRoamingZone::get() {\n  if (nullptr == _instance)\n    _instance = new DefaultRoamingZone();\n  return _instance;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingZoneList\n * ********************************************************************************************* */\nRoamingZoneList::RoamingZoneList(QObject *parent)\n  : ConfigObjectList(RoamingZone::staticMetaObject, parent)\n{\n  // pass...\n}\n\nRoamingZone *\nRoamingZoneList::zone(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<RoamingZone>();\n  return nullptr;\n}\n\nint\nRoamingZoneList::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<RoamingZone>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nRoamingZoneList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node)\n    return nullptr;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create roaming zone: Expected object.\";\n    return nullptr;\n  }\n\n  return new RoamingZone();\n}\n"
  },
  {
    "path": "lib/roamingzone.hh",
    "content": "#ifndef ROAMINGZONE_HH\n#define ROAMINGZONE_HH\n\n#include <QAbstractListModel>\n#include \"configreference.hh\"\n#include \"roamingchannel.hh\"\n\n/** Represents a RoamingZone within the abstract device configuration.\n *\n * A roaming zone collects a set of repeaters that act as alternatives to each other. When a selected\n * repeater gets out of range, another one might be found automatically from within the roaming zone.\n *\n * @ingroup config */\nclass RoamingZone : public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"roam\")\n\n  /** The channels in the roaming zone.\n   * @todo This property is marked non-scriptable to handle references to DMR channels before\n   *       version 0.11.0. Remove in future. */\n  Q_PROPERTY(RoamingChannelRefList * channels READ channels SCRIPTABLE false)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit RoamingZone(QObject *parent=nullptr);\n\n  /** Constructor.\n   * @param name Specifies the name of the roaming zone.\n   * @param parent Specifies the QObject parent of this zone. */\n  RoamingZone(const QString &name, QObject *parent = nullptr);\n\n  /** Copies the given zone. */\n  RoamingZone &operator =(const RoamingZone &other);\n  ConfigItem *clone() const;\n\n  /** Returns the number of zones. */\n  int count() const;\n\n  /** Clears the zone list. */\n  void clear();\n\n  /** Returns @c true, if the given roaming channel is member of this zone. */\n  bool contains(const RoamingChannel *ch) const;\n  /** Returns the roaming channel, which is the member at index @c idx (0-based).\n   * @param idx Specifies the index of the member channel. */\n  RoamingChannel *channel(int idx) const;\n  /** Adds a channel to the roaming zone.\n   * @param ch Specifies the channel to add.\n   * @param row Speicifies the index where to insert the channel\n   *        (optional, default insert at end). */\n  int addChannel(RoamingChannel *ch, int row=-1);\n  /** Removes the channel from the roaming zone at index @c row. */\n  bool remChannel(int row);\n  /** Removes the given channel from the roaming zone. */\n  bool remChannel(RoamingChannel *ch);\n\n  /** Returns the list of digital channels in this roaming zone. */\n  const RoamingChannelRefList *channels() const;\n  /** Returns the list of digital channels in this roaming zone. */\n  RoamingChannelRefList *channels();\n\n  /** Links the channel reference list.\n   * @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0. */\n  bool link(const YAML::Node &node, const Context &ctx, const ErrorStack &err);\n  /** Serializes the channel reference list.\n   * @todo Implemented for backward compatibility with version 0.10.0, remove for 1.0.0. */\n  bool populate(YAML::Node &node, const Context &context, const ErrorStack &err);\n\nprotected:\n  /** Holds the actual channels of the roaming zone. */\n  RoamingChannelRefList _channel;\n};\n\n\n/** Dummy roaming zone class that represents the default roaming zone.\n *\n * This is a singleton class. That is, there can only be one instance of this class.\n * @ingroup config */\nclass DefaultRoamingZone: public RoamingZone\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor.\n   * Use @c DefaultRoamingZone::get() to obtain an instance. */\n  explicit DefaultRoamingZone(QObject *parent=nullptr);\n\npublic:\n  /** Returns the singleton instance of this class. */\n  static DefaultRoamingZone *get();\n\nprotected:\n  /** Holds a reference to the singleton instance of this class. */\n  static DefaultRoamingZone *_instance;\n};\n\n\n/** Represents the list of roaming zones within the abstract device configuration.\n *\n * @ingroup config */\nclass RoamingZoneList: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RoamingZoneList(QObject *parent=nullptr);\n\n  /** Returns the roaming zone at the given index. */\n  RoamingZone *zone(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n#endif // ROAMINGZONE_HH\n"
  },
  {
    "path": "lib/rxgrouplist.cc",
    "content": "#include \"rxgrouplist.hh\"\n#include \"contact.hh\"\n#include \"config.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of RXGroupList\n * ********************************************************************************************* */\nRXGroupList::RXGroupList(QObject *parent)\n  : ConfigObject(parent), _contacts()\n{\n  connect(&_contacts, SIGNAL(elementModified(int)), this, SLOT(onModified()));\n  connect(&_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onModified()));\n  connect(&_contacts, SIGNAL(elementAdded(int)), this, SLOT(onModified()));\n}\n\nRXGroupList::RXGroupList(const QString &name, QObject *parent)\n  : ConfigObject(name, parent), _contacts()\n{\n  connect(&_contacts, SIGNAL(elementModified(int)), this, SLOT(onModified()));\n  connect(&_contacts, SIGNAL(elementRemoved(int)), this, SLOT(onModified()));\n  connect(&_contacts, SIGNAL(elementAdded(int)), this, SLOT(onModified()));\n}\n\nRXGroupList &\nRXGroupList::operator =(const RXGroupList &other) {\n  copy(other);\n  return *this;\n}\n\nConfigItem *\nRXGroupList::clone() const {\n  RXGroupList *lst = new RXGroupList();\n  if (! lst->copy(*this)) {\n    lst->deleteLater();\n    return nullptr;\n  }\n  return lst;\n}\n\nint\nRXGroupList::count() const {\n  return _contacts.count();\n}\n\nvoid\nRXGroupList::clear() {\n  _contacts.clear();\n  emit modified(this);\n}\n\nDMRContact *\nRXGroupList::contact(int idx) const {\n  if (idx >= _contacts.count())\n    return nullptr;\n  return _contacts.get(idx)->as<DMRContact>();\n}\n\nint\nRXGroupList::addContact(DMRContact *contact, int idx) {\n  return _contacts.add(contact, idx);\n}\n\nbool\nRXGroupList::remContact(int idx) {\n  return _contacts.del(_contacts.get(idx));\n}\n\nconst DMRContactRefList *\nRXGroupList::contacts() const {\n  return &_contacts;\n}\n\nDMRContactRefList *\nRXGroupList::contacts() {\n  return &_contacts;\n}\n\nYAML::Node\nRXGroupList::serialize(const Context &context, const ErrorStack &err) {\n  YAML::Node node = ConfigObject::serialize(context, err);\n  node.SetStyle(YAML::EmitterStyle::Flow);\n  return node;\n}\n\nvoid\nRXGroupList::onModified() {\n  emit modified(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RXGroupLists\n * ********************************************************************************************* */\nRXGroupLists::RXGroupLists(QObject *parent)\n  : ConfigObjectList(RXGroupList::staticMetaObject, parent)\n{\n  // pass...\n}\n\n\nRXGroupList *\nRXGroupLists::list(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<RXGroupList>();\n  return nullptr;\n}\n\nint\nRXGroupLists::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<RXGroupList>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nRXGroupLists::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n\n  if (! node)\n    return nullptr;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create group list: Expected object.\";\n    return nullptr;\n  }\n\n  return new RXGroupList();\n}\n\n"
  },
  {
    "path": "lib/rxgrouplist.hh",
    "content": "#ifndef RXGROUPLIST_HH\n#define RXGROUPLIST_HH\n\n#include <QObject>\n#include \"configreference.hh\"\n\nclass Config;\nclass DMRContact;\n\n\n/** Generic representation of a RX group list.\n * @ingroup conf */\nclass RXGroupList: public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"grp\")\n\n  /** The list of contacts. */\n  Q_PROPERTY(DMRContactRefList* contacts READ contacts)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit RXGroupList(QObject *parent=nullptr);\n  /** Constructor.\n   * @param name Specifies the name of the group list.\n   * @param parent @c QObject parent instance. */\n  RXGroupList(const QString &name, QObject *parent=nullptr);\n\n  /** Copy from other group list. */\n  RXGroupList &operator =(const RXGroupList &other);\n  ConfigItem *clone() const;\n\n  /** Returns the number of contacts within the group list. */\n  int count() const;\n  /** Resets & clears this group list. */\n  void clear();\n\n  /** Returns the contact at the given list index. */\n\tDMRContact *contact(int idx) const;\n  /** Adds a contact to the list. */\n  int addContact(DMRContact *contact, int idx=-1);\n  /** Removes the given contact from the list. */\n\tbool remContact(DMRContact *contact);\n  /** Removes the contact from the list at the given position. */\n\tbool remContact(int idx);\n\n  /** Returns the contact list. */\n  const DMRContactRefList *contacts() const;\n  /** Returns the contact list. */\n  DMRContactRefList *contacts();\n\npublic:\n  YAML::Node serialize(const Context &context, const ErrorStack &err=ErrorStack());\n\nprotected slots:\n  /** Internal used callback to handle list modifications. */\n  void onModified();\n\nprotected:\n  /** The list of contacts. */\n  DMRContactRefList _contacts;\n};\n\n\n/** Represents the list of RX group lists within the generic configuration.\n * @ingroup conf */\nclass RXGroupLists: public ConfigObjectList\n{\n\tQ_OBJECT\n\npublic:\n  /** Constructor. */\n\texplicit RXGroupLists(QObject *parent=nullptr);\n\n  /** Returns the group list at the given index. */\n\tRXGroupList *list(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n#endif // RXGROUPLIST_HH\n"
  },
  {
    "path": "lib/satelliteconfig.cc",
    "content": "#include \"satelliteconfig.hh\"\n\nSatelliteConfig::SatelliteConfig(QObject *parent)\n  : DFUFile{parent}\n{\n  // pass...\n}\n\n"
  },
  {
    "path": "lib/satelliteconfig.hh",
    "content": "#ifndef SATELLITECONFIG_HH\n#define SATELLITECONFIG_HH\n\n#include \"dfufile.hh\"\n\n\n// Forward declarations\nclass SatelliteDatabase;\n\n\n/** Baseclass of all satellite database configurations.\n * That is, the device specific encoding of the satellite orbitals and transponder information.\n * @ingroup conf */\nclass SatelliteConfig : public DFUFile\n{\n  Q_OBJECT\n\nprotected:\n  /** Hidden constructor. */\n  explicit SatelliteConfig(QObject *parent = nullptr);\n\npublic:\n  /** Encodes the given satellite db into the device specific satellite configuration. */\n  virtual bool encode(SatelliteDatabase *db, const ErrorStack &err) = 0;\n};\n\n#endif // SATELLITECONFIG_HH\n"
  },
  {
    "path": "lib/satellitedatabase.cc",
    "content": "#include \"satellitedatabase.hh\"\n\n#include <QRegularExpression>\n#include <QRegularExpressionMatch>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QJsonArray>\n#include <QStandardPaths>\n#include <QFile>\n#include <QDir>\n\n#include \"logger.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of Satellite\n * ********************************************************************************************* */\nSatellite::Satellite()\n  : OrbitalElement(), _name(),\n    _fmUplink(), _fmDownlink(), _fmUplinkTone(), _fmDownlinkTone(),\n    _aprsUplink(), _aprsDownlink(), _aprsUplinkTone(), _aprsDownlinkTone(), _beacon()\n{\n  // pass...\n}\n\nSatellite::Satellite(const OrbitalElement &orbit)\n  : OrbitalElement(orbit), _name(orbit.name()),\n    _fmUplink(), _fmDownlink(), _fmUplinkTone(), _fmDownlinkTone(),\n    _aprsUplink(), _aprsDownlink(), _aprsUplinkTone(), _aprsDownlinkTone(), _beacon()\n{\n  // pass...\n}\n\n\nconst QString &\nSatellite::name() const {\n  return _name;\n}\n\nvoid\nSatellite::setName(const QString &name) {\n  _name = name.simplified();\n}\n\n\nconst Frequency &\nSatellite::fmUplink() const {\n  return _fmUplink;\n}\n\nvoid\nSatellite::setFMUplink(const Frequency &f) {\n  _fmUplink = f;\n}\n\n\nconst Frequency &\nSatellite::fmDownlink() const {\n  return _fmDownlink;\n}\n\nvoid\nSatellite::setFMDownlink(const Frequency &f) {\n  _fmDownlink = f;\n}\n\n\nconst SelectiveCall &\nSatellite::fmUplinkTone() const {\n  return _fmUplinkTone;\n}\n\nvoid\nSatellite::setFMUplinkTone(const SelectiveCall &tone) {\n  _fmUplinkTone = tone;\n}\n\n\nconst SelectiveCall &\nSatellite::fmDownlinkTone() const {\n  return _fmDownlinkTone;\n}\n\nvoid\nSatellite::setFMDownlinkTone(const SelectiveCall &tone) {\n  _fmDownlinkTone = tone;\n}\n\n\nconst Frequency &\nSatellite::aprsUplink() const {\n  return _aprsUplink;\n}\n\nvoid\nSatellite::setAPRSUplink(const Frequency &f) {\n  _aprsUplink = f;\n}\n\nconst Frequency &\nSatellite::aprsDownlink() const {\n  return _aprsDownlink;\n}\n\nvoid\nSatellite::setAPRSDownlink(const Frequency &f) {\n  _aprsDownlink = f;\n}\n\n\nconst SelectiveCall &\nSatellite::aprsUplinkTone() const {\n  return _aprsUplinkTone;\n}\n\nvoid\nSatellite::setAPRSUplinkTone(const SelectiveCall &tone) {\n  _aprsUplinkTone = tone;\n}\n\nconst SelectiveCall &\nSatellite::aprsDownlinkTone() const {\n  return _aprsDownlinkTone;\n}\n\nvoid\nSatellite::setAPRSDownlinkTone(const SelectiveCall &tone) {\n  _aprsDownlinkTone = tone;\n}\n\nconst Frequency &\nSatellite::beacon() const {\n  return _beacon;\n}\n\nvoid\nSatellite::setBeacon(const Frequency &f) {\n  _beacon = f;\n}\n\n\nQJsonObject\nSatellite::toJson() const {\n  if (! isValid())\n    return QJsonObject();\n\n  QJsonObject o = QJsonObject();\n  o.insert(\"norad\", QJsonValue(qint64(id())));\n  o.insert(\"name\", name());\n\n  if (0 != fmUplink().inHz())\n    o.insert(\"fm_uplink\", fmUplink().format());\n  if (fmUplinkTone().isValid())\n    o.insert(\"fm_uplink_tone\", fmUplinkTone().format());\n\n  if (0 != fmDownlink().inHz())\n    o.insert(\"fm_downlink\", fmDownlink().format());\n  if (fmDownlinkTone().isValid())\n    o.insert(\"fm_downlink_tone\", fmDownlinkTone().format());\n\n  if (0 != aprsUplink().inHz())\n    o.insert(\"aprs_uplink\", aprsUplink().format());\n  if (aprsUplinkTone().isValid())\n    o.insert(\"aprs_uplink_tone\", aprsUplinkTone().format());\n\n  if (0 != aprsDownlink().inHz())\n    o.insert(\"aprs_downlink\", aprsDownlink().format());\n  if (aprsDownlinkTone().isValid())\n    o.insert(\"aprs_downlink_tone\", aprsDownlinkTone().format());\n\n  if (0 != beacon().inHz())\n    o.insert(\"beacon\", beacon().format());\n\n  return o;\n}\n\n\nSatellite\nSatellite::fromJson(const QJsonObject &obj, const OrbitalElementsDatabase &db) {\n  unsigned int id = obj.value(\"norad\").toInt();\n  QString name = obj.value(\"name\").toString();\n\n  if (! db.contains(id))\n    return Satellite();\n\n  Satellite sat(db.getById(id));\n  sat._name = name;\n\n  if (obj.contains(\"fm_uplink\"))\n    sat._fmUplink.parse(obj.value(\"fm_uplink\").toString());\n  if (obj.contains(\"fm_uplink_tone\"))\n    sat._fmUplinkTone = SelectiveCall::parseCTCSS(obj.value(\"fm_uplink_tone\").toString());\n  if (obj.contains(\"fm_downlink\"))\n    sat._fmDownlink.parse(obj.value(\"fm_downlink\").toString());\n  if (obj.contains(\"fm_downlink_tone\"))\n    sat._fmDownlinkTone = SelectiveCall::parseCTCSS(obj.value(\"fm_downlink_tone\").toString());\n\n  if (obj.contains(\"aprs_uplink\"))\n    sat._aprsUplink.parse(obj.value(\"aprs_uplink\").toString());\n  if (obj.contains(\"aprs_uplink_tone\"))\n    sat._aprsUplinkTone = SelectiveCall::parseCTCSS(obj.value(\"aprs_uplink_tone\").toString());\n  if (obj.contains(\"aprs_downlink\"))\n    sat._aprsDownlink.parse(obj.value(\"aprs_downlink\").toString());\n  if (obj.contains(\"aprs_downlink_tone\"))\n    sat._aprsDownlinkTone = SelectiveCall::parseCTCSS(obj.value(\"aprs_downlink_tone\").toString());\n\n  if (obj.contains(\"beacon\"))\n    sat._beacon.parse(obj.value(\"beacon\").toString());\n\n  return sat;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of SatelliteDatabase\n * ********************************************************************************************* */\nSatelliteDatabase::SatelliteDatabase(unsigned int updatePeriod, QObject *parent)\n  : QAbstractTableModel{parent}, _satellites(), _orbitalElements(false, updatePeriod),\n    _transponders(false, updatePeriod)\n{\n  connect(&_orbitalElements, &OrbitalElementsDatabase::loaded,\n          this, &SatelliteDatabase::load);\n\n  _orbitalElements.load();\n  _transponders.load();\n}\n\n\nconst OrbitalElementsDatabase  &\nSatelliteDatabase::orbitalElements() const {\n  return _orbitalElements;\n}\n\nconst TransponderDatabase  &\nSatelliteDatabase::transponders() const {\n  return _transponders;\n}\n\nOrbitalElementsDatabase  &\nSatelliteDatabase::orbitalElements() {\n  return _orbitalElements;\n}\n\nTransponderDatabase  &\nSatelliteDatabase::transponders() {\n  return _transponders;\n}\n\nunsigned int\nSatelliteDatabase::count() const {\n  return _satellites.count();\n}\n\nconst Satellite &\nSatelliteDatabase::getAt(unsigned int idx) const {\n  return _satellites[idx];\n}\n\nbool\nSatelliteDatabase::setAt(const Satellite &sat, unsigned int idx) {\n  if (idx >= count())\n    return false;\n  _satellites[idx] = sat;\n  emit dataChanged(index(idx, 0), index(idx,columnCount()));\n  return true;\n}\n\nvoid\nSatelliteDatabase::add(const Satellite &sat) {\n  if (! sat.isValid())\n    return;\n\n  beginInsertRows(QModelIndex(), _satellites.count(), _satellites.count());\n  _satellites.append(sat);\n  endInsertRows();\n}\n\n\nbool\nSatelliteDatabase::removeRows(int row, int count, const QModelIndex &parent) {\n  if ((row >= _satellites.count()) || ((row+count) > _satellites.count()))\n    return false;\n\n  if (0 == count)\n    return true;\n\n  beginRemoveRows(parent, row, row+count-1);\n  _satellites.remove(row, count);\n  endRemoveRows();\n\n  return true;\n}\n\n\nint\nSatelliteDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return _satellites.size();\n}\n\nint\nSatelliteDatabase::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return 11;\n}\n\nQVariant\nSatelliteDatabase::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::Horizontal != orientation) || (Qt::DisplayRole != role))\n    return QVariant();\n\n  switch (section) {\n  case 0: return tr(\"NORAD\");\n  case 1: return tr(\"Name\");\n  case 2: return tr(\"FM Downlink Frequency\");\n  case 3: return tr(\"FM Uplink Frequency\");\n  case 4: return tr(\"FM Downlink Tone\");\n  case 5: return tr(\"FM Uplink Tone\");\n  case 6: return tr(\"APRS Downlink Frequency\");\n  case 7: return tr(\"APRS Uplink Frequency\");\n  case 8: return tr(\"APRS Downlink Tone\");\n  case 9: return tr(\"APRS Uplink Tone\");\n  case 10: return tr(\"Beacon Frequency\");\n  }\n\n  return QVariant();\n}\n\n\nQt::ItemFlags\nSatelliteDatabase::flags(const QModelIndex &index) const {\n  Qt::ItemFlags f = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;\n\n  // Name\n  if (1 == index.column())\n    f |= Qt::ItemIsEditable;\n  // FM up/downlink frequencies\n  if ((2 == index.column()) || (3 == index.column()))\n    f |= Qt::ItemIsEditable;\n  // FM up/downlink sub tones\n  if ((4 == index.column()) || (5 == index.column()))\n    f |= Qt::ItemIsEditable;\n  // APRS up/downlink frequencies\n  if ((6 == index.column()) || (7 == index.column()))\n    f |= Qt::ItemIsEditable;\n  // APRS up/downlink sub tones\n  if ((8 == index.column()) || (9 == index.column()))\n    f |= Qt::ItemIsEditable;\n  // Beacon\n  if (10 == index.column())\n    f |= Qt::ItemIsEditable;\n\n  return f;\n}\n\n\nQVariant\nSatelliteDatabase::data(const QModelIndex &index, int role) const {\n  if (index.row() >= _satellites.count())\n    return QVariant();\n\n  if (Qt::DisplayRole == role) {\n    switch (index.column()) {\n    case 0: return _satellites.at(index.row()).id();\n    case 1: return _satellites.at(index.row()).name();\n    case 2:\n      return (0 == _satellites.at(index.row()).fmDownlink().inHz()) ?\n            tr(\"None\") : _satellites.at(index.row()).fmDownlink().format();\n    case 3:\n      return (0 == _satellites.at(index.row()).fmUplink().inHz()) ?\n            tr(\"None\") : _satellites.at(index.row()).fmUplink().format();\n    case 4: return _satellites.at(index.row()).fmDownlinkTone().format();\n    case 5: return _satellites.at(index.row()).fmUplinkTone().format();\n    case 6:\n      return (0 == _satellites.at(index.row()).aprsDownlink().inHz()) ?\n            tr(\"None\") : _satellites.at(index.row()).aprsDownlink().format();\n    case 7:\n      return (0 == _satellites.at(index.row()).aprsUplink().inHz()) ?\n            tr(\"None\") : _satellites.at(index.row()).aprsUplink().format();\n    case 8: return _satellites.at(index.row()).aprsDownlinkTone().format();\n    case 9: return _satellites.at(index.row()).aprsUplinkTone().format();\n    case 10:\n      return (0 == _satellites.at(index.row()).beacon().inHz()) ?\n            tr(\"None\") : _satellites.at(index.row()).beacon().format();\n    }\n  } else if (Qt::EditRole == role) {\n    switch (index.column()) {\n    case 0: return _satellites.at(index.row()).id();\n    case 1: return _satellites.at(index.row()).name();\n    case 2: return QVariant::fromValue(_satellites.at(index.row()).fmDownlink());\n    case 3: return QVariant::fromValue(_satellites.at(index.row()).fmUplink());\n    case 4: return QVariant::fromValue(_satellites.at(index.row()).fmDownlinkTone());\n    case 5: return QVariant::fromValue(_satellites.at(index.row()).fmUplinkTone());\n    case 6: return QVariant::fromValue(_satellites.at(index.row()).aprsDownlink());\n    case 7: return QVariant::fromValue(_satellites.at(index.row()).aprsUplink());\n    case 8: return QVariant::fromValue(_satellites.at(index.row()).aprsDownlinkTone());\n    case 9: return QVariant::fromValue(_satellites.at(index.row()).aprsUplinkTone());\n    case 10: return QVariant::fromValue(_satellites.at(index.row()).beacon());\n    }\n  }\n\n  return QVariant();\n}\n\n\nbool\nSatelliteDatabase::setData(const QModelIndex &index, const QVariant &value, int role) {\n  if (Qt::EditRole != role)\n    return false;\n  if (index.row() >= _satellites.count())\n    return false;\n\n  switch (index.column()) {\n  case 1: _satellites[index.row()].setName(value.toString().simplified()); return true;\n  case 2: _satellites[index.row()].setFMDownlink(value.value<Frequency>()); return true;\n  case 3: _satellites[index.row()].setFMUplink(value.value<Frequency>()); return true;\n  case 4: _satellites[index.row()].setFMDownlinkTone(value.value<SelectiveCall>()); return true;\n  case 5: _satellites[index.row()].setFMUplinkTone(value.value<SelectiveCall>()); return true;\n  case 6: _satellites[index.row()].setAPRSDownlink(value.value<Frequency>()); return true;\n  case 7: _satellites[index.row()].setAPRSUplink(value.value<Frequency>()); return true;\n  case 8: _satellites[index.row()].setAPRSDownlinkTone(value.value<SelectiveCall>()); return true;\n  case 9: _satellites[index.row()].setAPRSUplinkTone(value.value<SelectiveCall>()); return true;\n  case 10: _satellites[index.row()].setBeacon(value.value<Frequency>()); return true;\n  }\n\n  return false;\n}\n\n\nvoid\nSatelliteDatabase::update() {\n  _orbitalElements.download();\n  _transponders.download();\n}\n\nvoid\nSatelliteDatabase::load() {\n  QString filename = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/satellites.json\";\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    QString msg = QString(\"Cannot open satellites '%1': %2\").arg(filename).arg(file.errorString());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n  QByteArray data = file.readAll();\n  file.close();\n\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(data, &err);\n  if (doc.isEmpty()) {\n    QString msg = \"Failed to load satellites: \" + err.errorString();\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  if (! doc.isArray()) {\n    QString msg = \"Failed to load satellites: JSON document is not an array!\";\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  beginResetModel();\n\n  QJsonArray array = doc.array();\n  _satellites.clear();\n  _satellites.reserve(array.size());\n  for (int i=0; i<array.size(); i++) {\n    Satellite element = Satellite::fromJson(array.at(i).toObject(), _orbitalElements);\n    if (element.isValid()) {\n      _satellites.append(element);\n    }\n  }\n\n  // Done.\n  endResetModel();\n\n  logDebug() << \"Loaded satellites with \" << _satellites.size() << \" entries from \" << filename << \".\";\n\n  emit loaded();\n}\n\n\nbool\nSatelliteDatabase::save(const ErrorStack &err) const {\n  QJsonArray satellites;\n  for (auto sat: _satellites)\n    satellites.append(sat.toJson());\n  QJsonDocument doc(satellites);\n\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFile file(path + \"/satellites.json\");\n\n  QDir directory;\n  if ((! directory.exists(path)) && (! directory.mkpath(path))) {\n    errMsg(err) << \"Cannot create path '\" << path << \"'.\";\n    return false;\n  }\n\n  if (! file.open(QIODevice::WriteOnly)) {\n    errMsg(err) << \"Cannot save satellites at '\" << file.fileName() << \"'.\";\n    return false;\n  }\n\n  file.write(doc.toJson());\n  file.flush();\n  file.close();\n\n  return true;\n}\n"
  },
  {
    "path": "lib/satellitedatabase.hh",
    "content": "#ifndef SATELLITEDATABASE_HH\n#define SATELLITEDATABASE_HH\n\n#include \"errorstack.hh\"\n#include \"frequency.hh\"\n#include \"signaling.hh\"\n#include \"transponderdatabase.hh\"\n#include \"orbitalelementsdatabase.hh\"\n\n\n/** Extends the @c OrbitalElement to include transponder information for a satellite.\n *\n * This represents a single user-curated satellite. The orbital elements are updated automatically,\n * while the transponder information remains set by the user.\n *\n * @ingroup sat */\nclass Satellite: public OrbitalElement\n{\npublic:\n  /** Default constructor. */\n  Satellite();\n  /** Constructor from orbital element. */\n  Satellite(const OrbitalElement &orbit);\n  /** Copy constructor. */\n  Satellite(const Satellite &sat) = default;\n  /** Copy assignment. */\n  Satellite &operator =(const Satellite &other) = default;\n\n  /** Returns the name of the satellite. */\n  const QString &name() const;\n  /** Sets the name of the satellite. */\n  void setName(const QString &name);\n\n  /** Returns the FM uplink frequency. */\n  const Frequency &fmUplink() const;\n  /** Sets the FM uplink frequency. */\n  void setFMUplink(const Frequency &f);\n  /** Returns the FM downlink frequency. */\n  const Frequency &fmDownlink() const;\n  /** Sets the FM downlink frequency. */\n  void setFMDownlink(const Frequency &f);\n  /** Returns the FM uplink tone (i.e., TX tone). */\n  const SelectiveCall &fmUplinkTone() const;\n  /** Sets the FM uplink sub tone. */\n  void setFMUplinkTone(const SelectiveCall &tone);\n  /** Returns the FM downlink tone (i.e., RX tone). */\n  const SelectiveCall &fmDownlinkTone() const;\n  /** Sets the FM downlink sub tone. */\n  void setFMDownlinkTone(const SelectiveCall &tone);\n\n  /** Returns the APRS uplink frequency. */\n  const Frequency &aprsUplink() const;\n  /** Sets the APRS uplink frequency. */\n  void setAPRSUplink(const Frequency &f);\n  /** Returns the APRS downlink frequency. */\n  const Frequency &aprsDownlink() const;\n  /** Sets the APRS downlink frequency. */\n  void setAPRSDownlink(const Frequency &f);\n  /** Returns the APRS uplink tone (i.e., TX tone). */\n  const SelectiveCall &aprsUplinkTone() const;\n  /** Sets the APRS uplink sub tone. */\n  void setAPRSUplinkTone(const SelectiveCall &tone);\n  /** Returns the APRS downlink tone (i.e., RX tone). */\n  const SelectiveCall &aprsDownlinkTone() const;\n  /** Sets the APRS downlink sub tone. */\n  void setAPRSDownlinkTone(const SelectiveCall &tone);\n  /** Returns a beacon frequency. */\n  const Frequency &beacon() const;\n  /** Sets a beacon frequency. */\n  void setBeacon(const Frequency &f);\n\n  /** Serializes the satellite into a JSON object. */\n  QJsonObject toJson() const;\n\npublic:\n  /** Constructs a satellite from the given JSON object and orbital element database. */\n  static Satellite fromJson(const QJsonObject &obj, const OrbitalElementsDatabase &db);\n\nprotected:\n  /** Holds the name of the satellite. */\n  QString _name;\n  /** Holds the FM uplink frequency. */\n  Frequency _fmUplink;\n  /** Holds the FM downlink frequency. */\n  Frequency _fmDownlink;\n  /** Holds the FM uplink tone. */\n  SelectiveCall _fmUplinkTone;\n  /** Holds the FM downlink tone. */\n  SelectiveCall _fmDownlinkTone;\n  /** Holds the APRS uplink frequency. */\n  Frequency _aprsUplink;\n  /** Holds the APRS downlink frequency. */\n  Frequency _aprsDownlink;\n  /** Holds the APRS uplink tone. */\n  SelectiveCall _aprsUplinkTone;\n  /** Holds the APRS downlink tone. */\n  SelectiveCall _aprsDownlinkTone;\n  /** Holds a beacon frequency. */\n  Frequency _beacon;\n};\n\n\n\n/** A table holding all known satellites.\n * This table merges oribtal with transponder information.\n * @ingroup sat */\nclass SatelliteDatabase : public QAbstractTableModel\n{\n  Q_OBJECT\n\npublic:\n  /** Constructs a new satellite database. */\n  explicit SatelliteDatabase(unsigned int updatePeriodDays=7, QObject *parent = nullptr);\n\n  /** Returns the orbital element database. */\n  const OrbitalElementsDatabase &orbitalElements() const;\n  /** Returns the orbital element database. */\n  OrbitalElementsDatabase &orbitalElements();\n  /** Returns the transponder database. */\n  const TransponderDatabase &transponders() const;\n  /** Returns the transponder database. */\n  TransponderDatabase &transponders();\n\n  /** Returns the number of configured satellites. */\n  unsigned int count() const;\n  /** Returns the i-th satellite. */\n  const Satellite &getAt(unsigned int idx) const;\n  /** Returns the i-th satellite. */\n  bool setAt(const Satellite &sat, unsigned int idx);\n  /** Appends a satellite. */\n  void add(const Satellite &sat);\n  /** Removes one or more satellites. */\n  bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex());\n\n  /** Implements the QAbstractTableModel interface. Returns some flags for the given cell. */\n  Qt::ItemFlags flags(const QModelIndex &index) const;\n  /** Implements the QAbstractTableModel interface.\n   * Returns the number of rows in the table. That is, the number of satellites. */\n  int rowCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface. Returns the number of columns in the table. */\n  int columnCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface. Returns the header labels. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role) const;\n  /** Implements the QAbstractTableModel interface. Returns the data for the cell. */\n  QVariant data(const QModelIndex &index, int role) const;\n  /** Implements the QAbstractTableModel interface. Sets the data for a cell. */\n  bool setData(const QModelIndex &index, const QVariant &value, int role);\n\npublic slots:\n  /** Triggers a download of the orbital and transponder databases. */\n  void update();\n  /** Loads the user-curated satellite database. */\n  void load();\n  /** Saves the user-curated satellite database. */\n  bool save(const ErrorStack &err = ErrorStack()) const;\n\nsignals:\n  /** Gets emitted once the satellites has been loaded. */\n  void loaded();\n  /** Gets emitted if the loading one of the sources fails. */\n  void error(const QString &msg);\n\nprivate:\n  /** Holds all satellites sorted by their catalog number. */\n  QVector<Satellite>  _satellites;\n  /** Holds the orbital element database. */\n  OrbitalElementsDatabase _orbitalElements;\n  /** Holds the transponder database. */\n  TransponderDatabase _transponders;\n};\n\n#endif // SATELLITEDATABASE_HH\n"
  },
  {
    "path": "lib/scanlist.cc",
    "content": "#include \"scanlist.hh\"\n#include \"channel.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of ScanList\n * ********************************************************************************************* */\nScanList::ScanList(QObject *parent)\n  : ConfigObject(parent), _channels(), _primary(), _secondary(), _revert(), _tyt(nullptr)\n{\n  // Register \"selected\" channel tags for primary, secondary, revert and the channel list.\n  Context::setTag(staticMetaObject.className(), \"primary\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"secondary\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"revert\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"channels\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n}\n\nScanList::ScanList(const QString &name, QObject *parent)\n  : ConfigObject(name, parent), _channels(), _primary(), _secondary(), _revert(), _tyt(nullptr)\n{\n  // Register \"selected\" channel tags for primary, secondary, revert and the channel list.\n  Context::setTag(staticMetaObject.className(), \"primary\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"secondary\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"revert\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n  Context::setTag(staticMetaObject.className(), \"channels\", \"!selected\", QVariant::fromValue(SelectedChannel::get()));\n}\n\nScanList &\nScanList::operator =(const ScanList &other) {\n  copy(other);\n  return *this;\n}\n\nConfigItem *\nScanList::clone() const {\n  ScanList *list = new ScanList();\n  if (! list->copy(*this)) {\n    list->deleteLater();\n    return nullptr;\n  }\n  return list;\n}\n\nvoid\nScanList::clear() {\n  _name.clear();\n  _primary.clear();\n  _secondary.clear();\n  _revert.clear();\n  _channels.clear();\n  emit modified(this);\n}\n\nconst ChannelRefList *\nScanList::channels() const {\n  return &_channels;\n}\n\nChannelRefList *\nScanList::channels() {\n  return &_channels;\n}\n\nint\nScanList::count() const {\n  return _channels.count();\n}\n\nbool\nScanList::contains(Channel *channel) const {\n  return (0 <= _channels.indexOf(channel));\n}\n\nChannel *\nScanList::channel(int idx) const {\n  return _channels.get(idx)->as<Channel>();\n}\n\nint\nScanList::addChannel(Channel *channel, int idx) {\n  idx = _channels.add(channel, idx);\n  if (0 > idx)\n    return idx;\n  return idx;\n}\n\nbool\nScanList::remChannel(int idx) {\n  return _channels.del(_channels.get(idx));\n  emit modified(this);\n  return true;\n}\n\nbool\nScanList::remChannel(Channel *channel) {\n  return _channels.del(channel);\n}\n\n\nconst ChannelReference *\nScanList::primaryChannelRef() const {\n  return &_primary;\n}\n\nChannelReference *\nScanList::primaryChannelRef() {\n  return &_primary;\n}\n\nChannel *\nScanList::primaryChannel() const {\n  return _primary.as<Channel>();\n}\n\nvoid\nScanList::setPrimaryChannel(Channel *channel) {\n  _primary.set(channel);\n  emit modified(this);\n}\n\n\nconst ChannelReference *\nScanList::secondaryChannelRef() const {\n  return &_secondary;\n}\n\nChannelReference *\nScanList::secondaryChannelRef() {\n  return &_secondary;\n}\n\nChannel *\nScanList::secondaryChannel() const {\n  return _secondary.as<Channel>();\n}\n\nvoid\nScanList::setSecondaryChannel(Channel *channel) {\n  _secondary.set(channel);\n  emit modified(this);\n}\n\n\nconst ChannelReference *\nScanList::revertChannelRef() const {\n  return &_revert;\n}\n\nChannelReference *\nScanList::revertChannelRef() {\n  return &_revert;\n}\n\nChannel *\nScanList::revertChannel() const {\n  return _revert.as<Channel>();\n}\n\nvoid\nScanList::setRevertChannel(Channel *channel) {\n  _revert.set(channel);\n  emit modified(this);\n}\n\nTyTScanListExtension *\nScanList::tytScanListExtension() const {\n  return _tyt;\n}\nvoid\nScanList::setTyTScanListExtension(TyTScanListExtension *tyt) {\n  if (_tyt) {\n    _tyt->deleteLater();\n    _tyt = nullptr;\n  }\n  _tyt = tyt;\n  if (_tyt)\n    _tyt->setParent(this);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ScanLists\n * ********************************************************************************************* */\nScanLists::ScanLists(QObject *parent)\n  : ConfigObjectList(ScanList::staticMetaObject, parent)\n{\n  // pass...\n}\n\nScanList *\nScanLists::scanlist(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<ScanList>();\n  return nullptr;\n}\n\nint\nScanLists::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<ScanList>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nScanLists::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx);\n\n  if (! node)\n    return nullptr;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create scan list: Expected object.\";\n    return nullptr;\n  }\n\n  return new ScanList();\n}\n"
  },
  {
    "path": "lib/scanlist.hh",
    "content": "#ifndef SCANLIST_HH\n#define SCANLIST_HH\n\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n\n#include \"tyt_extensions.hh\"\n\nclass Channel;\n\n\n/** Generic representation of a scan list.\n * @ingroup conf */\nclass ScanList : public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"scan\")\n\n  /** The primaryRef channel. */\n  Q_PROPERTY(ChannelReference* primary READ primaryChannelRef)\n  /** The secondary channel. */\n  Q_PROPERTY(ChannelReference* secondary READ secondaryChannelRef)\n  /** The revert channel. */\n  Q_PROPERTY(ChannelReference* revert READ revertChannelRef)\n  /** The list of channels. */\n  Q_PROPERTY(ChannelRefList * channels READ channels)\n\n  /** The TyT scan-list extension. */\n  Q_PROPERTY(TyTScanListExtension* tyt READ tytScanListExtension WRITE setTyTScanListExtension)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit ScanList(QObject *parent=nullptr);\n  /** Constructs a scan list with the given name. */\n\tScanList(const QString &name, QObject *parent=nullptr);\n\n  /** Copies the given scan list. */\n  ScanList &operator= (const ScanList &other);\n  ConfigItem *clone() const;\n\n  /** Returns the number of channels within the scanlist. */\n\tint count() const;\n  /** Clears the scan list. */\n\tvoid clear();\n\n  /** Returns @c true if the given channel is part of this scanlist. */\n  bool contains(Channel *channel) const;\n  /** Returns the channel at the given index. */\n\tChannel *channel(int idx) const;\n  /** Adds a channel to the scan list. */\n  int addChannel(Channel *channel, int idx=-1);\n  /** Removes the channel at the given index. */\n\tbool remChannel(int idx);\n  /** Removes the given channel. */\n\tbool remChannel(Channel *channel);\n\n  /** Returns the channels of the scan list. */\n  const ChannelRefList *channels() const;\n  /** Returns the channels of the scan list. */\n  ChannelRefList *channels();\n\n  /** Returns the primary channel reference. */\n  const ChannelReference *primaryChannelRef() const;\n  /** Returns the primary channel reference. */\n  ChannelReference *primaryChannelRef();\n  /** Returns the priority channel. */\n  Channel *primaryChannel() const;\n  /** Sets the priority channel. */\n  void setPrimaryChannel(Channel *channel);\n\n  /** Returns the secondary channel reference. */\n  const ChannelReference *secondaryChannelRef() const;\n  /** Returns the secondary channel reference. */\n  ChannelReference *secondaryChannelRef();\n  /** Returns the secondary priority channel. */\n  Channel *secondaryChannel() const;\n  /** Sets the secondary priority channel. */\n  void setSecondaryChannel(Channel *channel);\n\n  /** Returns the revert channel reference. */\n  const ChannelReference *revertChannelRef() const;\n  /** Returns the revert channel reference. */\n  ChannelReference *revertChannelRef();\n  /** Returns the TX channel. */\n  Channel *revertChannel() const;\n  /** Sets the TX channel. */\n  void setRevertChannel(Channel *channel);\n\n  /** Returns the TyT scan-list extension instance (if set). */\n  TyTScanListExtension *tytScanListExtension() const;\n  /** Sets the TyT scan-list extension. */\n  void setTyTScanListExtension(TyTScanListExtension *tyt);\n\nprotected:\n  /** The channel list. */\n  ChannelRefList _channels;\n  /** The priority channel. */\n  ChannelReference _primary;\n  /** The secondary priority channel. */\n  ChannelReference _secondary;\n  /** The transmit channel. */\n  ChannelReference _revert;\n  /** TyT scan-list settings extension. */\n  TyTScanListExtension *_tyt;\n\n};\n\n\n/** Represents the list of scan lists.\n * @ingroup conf */\nclass ScanLists: public ConfigObjectList\n{\n\tQ_OBJECT\n\npublic:\n  /** Constructs an empty list. */\n\texplicit ScanLists(QObject *parent = nullptr);\n\n  /** Returns the scanlist at the given index. */\n\tScanList *scanlist(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n#endif // SCANLIST_HH\n"
  },
  {
    "path": "lib/signaling.cc",
    "content": "#include \"signaling.hh\"\n\n#include <QHash>\n#include <QVector>\n#include <QObject>\n#include <QRegularExpression>\n#include <QRegularExpressionMatch>\n\n\n/* ********************************************************************************************* *\n * Implementation of SelectiveCall\n * ********************************************************************************************* */\nQVector<SelectiveCall>\nSelectiveCall::_standard = {\n  SelectiveCall(),\n   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,\n  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,\n  179.9, 186.2, 192.8, 203.5, 210.7, 218.1, 225.7, 233.6, 241.8, 250.3,\n  { 23, false}, { 25, false}, { 26, false}, { 31, false}, { 32, false}, { 36, false}, { 43, false},\n  { 47, false}, { 51, false}, { 53, false}, { 54, false}, { 71, false}, { 72, false}, { 73, false},\n  { 74, false}, {114, false}, {115, false}, {116, false}, {122, false}, {125, false}, {131, false},\n  {132, false}, {134, false}, {143, false}, {145, false}, {152, false}, {155, false}, {156, false},\n  {162, false}, {165, false}, {172, false}, {174, false}, {205, false}, {212, false}, {223, false},\n  {225, false}, {226, false}, {243, false}, {244, false}, {245, false}, {246, false}, {251, false},\n  {252, false}, {255, false}, {261, false}, {263, false}, {265, false}, {266, false}, {267, false},\n  {271, false}, {274, false}, {306, false}, {311, false}, {315, false}, {325, false}, {331, false},\n  {332, false}, {343, false}, {346, false}, {351, false}, {356, false}, {364, false}, {365, false},\n  {371, false}, {411, false}, {412, false}, {413, false}, {423, false}, {431, false}, {432, false},\n  {445, false}, {446, false}, {452, false}, {454, false}, {455, false}, {462, false}, {464, false},\n  {465, false}, {466, false}, {503, false}, {506, false}, {516, false}, {523, false}, {526, false},\n  {532, false}, {546, false}, {565, false}, {606, false}, {612, false}, {624, false}, {627, false},\n  {631, false}, {632, false}, {654, false}, {662, false}, {664, false}, {703, false}, {712, false},\n  {723, false}, {731, false}, {732, false}, {734, false}, {743, false}, {754, false},\n  { 23,  true}, { 25,  true}, { 26,  true}, { 31,  true}, { 32,  true}, { 36,  true}, { 43,  true},\n  { 47,  true}, { 51,  true}, { 53,  true}, { 54,  true}, { 71,  true}, { 72,  true}, { 73,  true},\n  { 74,  true}, {114,  true}, {115,  true}, {116,  true}, {122,  true}, {125,  true}, {131,  true},\n  {132,  true}, {134,  true}, {143,  true}, {145,  true}, {152,  true}, {155,  true}, {156,  true},\n  {162,  true}, {165,  true}, {172,  true}, {174,  true}, {205,  true}, {212,  true}, {223,  true},\n  {225,  true}, {226,  true}, {243,  true}, {244,  true}, {245,  true}, {246,  true}, {251,  true},\n  {252,  true}, {255,  true}, {261,  true}, {263,  true}, {265,  true}, {266,  true}, {267,  true},\n  {271,  true}, {274,  true}, {306,  true}, {311,  true}, {315,  true}, {325,  true}, {331,  true},\n  {332,  true}, {343,  true}, {346,  true}, {351,  true}, {356,  true}, {364,  true}, {365,  true},\n  {371,  true}, {411,  true}, {412,  true}, {413,  true}, {423,  true}, {431,  true}, {432,  true},\n  {445,  true}, {446,  true}, {452,  true}, {454,  true}, {455,  true}, {462,  true}, {464,  true},\n  {465,  true}, {466,  true}, {503,  true}, {506,  true}, {516,  true}, {523,  true}, {526,  true},\n  {532,  true}, {546,  true}, {565,  true}, {606,  true}, {612,  true}, {624,  true}, {627,  true},\n  {631,  true}, {632,  true}, {654,  true}, {662,  true}, {664,  true}, {703,  true}, {712,  true},\n  {723,  true}, {731,  true}, {732,  true}, {734,  true}, {743,  true}, {754,  true}\n};\n\nSelectiveCall::SelectiveCall()\n  : type(Type::None), dcs{0,false}\n{\n  // Pass...\n}\n\nSelectiveCall::SelectiveCall(double ctcssFreq)\n  : type(Type::CTCSS), ctcss(ctcssFreq*10)\n{\n  // pass...\n}\n\nSelectiveCall::SelectiveCall(unsigned int octalDSCCode, bool inverted)\n  : type(Type::DCS), dcs{0, inverted}\n{\n  unsigned int e = 1;\n  while (octalDSCCode) {\n    dcs.code += std::min(7U, (octalDSCCode % 10)) * e;\n    e *= 8; octalDSCCode /= 10;\n  }\n}\n\nbool\nSelectiveCall::operator==(const SelectiveCall &other) const {\n  if (type != other.type)\n    return false;\n  if (Type::CTCSS == type)\n    return ctcss == other.ctcss;\n  return (dcs.code == other.dcs.code) && (dcs.inverted == other.dcs.inverted);\n}\n\nbool\nSelectiveCall::operator !=(const SelectiveCall &other) const {\n  return !(*this == other);\n}\n\nbool\nSelectiveCall::isInvalid() const {\n  return Type::None == type;\n}\n\nbool\nSelectiveCall::isValid() const {\n  return Type::None != type;\n}\n\nbool\nSelectiveCall::isCTCSS() const {\n  return Type::CTCSS == type;\n}\n\nbool\nSelectiveCall::isDCS() const {\n  return Type::DCS == type;\n}\n\ndouble\nSelectiveCall::Hz() const {\n  return double(ctcss)/10;\n}\n\nunsigned int\nSelectiveCall::mHz() const {\n  return ((unsigned int)ctcss)*100;\n}\n\nunsigned int\nSelectiveCall::binCode() const {\n  return dcs.code;\n}\n\nunsigned int\nSelectiveCall::octalCode() const {\n  unsigned int o=0, e=1, c=dcs.code;\n  while (c) {\n    o += (c%8)*e;\n    e *= 10; c/= 8;\n  }\n  return o;\n}\n\nbool\nSelectiveCall::isInverted() const {\n  return dcs.inverted;\n}\n\nQString\nSelectiveCall::format() const {\n  if (! isValid())\n    return QString();\n  if (isCTCSS())\n    return QString(\"%1 Hz\").arg(Hz(), 0, 'f', 1);\n  return QString(\"%1%2\")\n      .arg(isInverted() ? \"i\" : \"n\")\n      .arg(binCode(), 3, 8, QChar('0'));\n}\n\nSelectiveCall\nSelectiveCall::parseCTCSS(const QString &text) {\n  QRegularExpression re(R\"(([0-9]+(?:\\.[0-9]|))\\s*(?:Hz|))\");\n  QRegularExpressionMatch match = re.match(text);\n  if (! match.hasMatch())\n    return SelectiveCall();\n  return SelectiveCall(match.captured(1).toDouble());\n}\n\n\nSelectiveCall\nSelectiveCall::parseDCS(const QString &text) {\n  QRegularExpression re(R\"(([\\-iInN]?)([0-7]{1,3}))\");\n  QRegularExpressionMatch match = re.match(text);\n  if (! match.hasMatch())\n    return SelectiveCall();\n  bool inverted = false;\n  if ((\"-\" == match.captured(1)) || (\"i\" == match.captured(1)) || (\"I\" == match.captured(1)))\n    inverted = true;\n  return SelectiveCall(match.captured(2).toUInt(), inverted);\n}\n\nSelectiveCall\nSelectiveCall::fromBinaryDCS(unsigned int code, bool inverted) {\n  unsigned int o=0, e=1;\n  while (code) {\n    o += (code % 8) * e;\n    e *= 10; code /= 8;\n  }\n  return SelectiveCall(o, inverted);\n}\n\nconst QVector<SelectiveCall> &\nSelectiveCall::standard() {\n  return _standard;\n}\n\n"
  },
  {
    "path": "lib/signaling.hh",
    "content": "#ifndef SIGNALING_HH\n#define SIGNALING_HH\n\n#include <yaml-cpp/yaml.h>\n#include <QString>\n#include <QObject>\n\n\n/** Encodes a selective call.\n * This can be CTCSS sub tones or DSC codes.\n * @ingroup conf */\nstruct SelectiveCall\n{\nprotected:\n  /** Type of the subtone. */\n  enum class Type {\n    None, CTCSS, DCS\n  };\n\npublic:\n  /** Empty constructor, no selective call defined. */\n  SelectiveCall();\n  /** Constructs a CTCSS sub tone for the specified frequency in Hz. */\n  SelectiveCall(double ctcssFreq);\n  /** Constructs a DCS code for the specified ocal code and inversion. */\n  SelectiveCall(unsigned int octalDSCCode, bool inverted);\n\n  /** Comparison operator. */\n  bool operator==(const SelectiveCall &other) const;\n  /** Comparison operator. */\n  bool operator!=(const SelectiveCall &other) const;\n\n  /** Returns @c false, if a selective call is set. */\n  bool isInvalid() const;\n  /** Returns @c true, if a selective call is set. */\n  bool isValid() const;\n  /** Returns @c true, if a CTCSS sub tone is set. */\n  bool isCTCSS() const;\n  /** Returns @c true, if a DCS code is set. */\n  bool isDCS() const;\n\n  /** If a CTCSS sub tone is set, returns the frequency in Hz (floating point). */\n  double Hz() const;\n  /** If a CTCSS sub tone is set, returns the frequency in mHz (integer). */\n  unsigned int mHz() const;\n\n  /** If a DCS code is set, returns the binary code. */\n  unsigned int binCode() const;\n  /** If a DCS code is set, returns the octal code. */\n  unsigned int octalCode() const;\n  /** If a DCS code is set, returns the inversion flag. */\n  bool isInverted() const;\n\n  /** Formats the selective call. */\n  QString format() const;\n\npublic:\n  /** Parses a CTCSS frequency. */\n  static SelectiveCall parseCTCSS(const QString &text);\n  /** Parses a DCS code. */\n  static SelectiveCall parseDCS(const QString &text);\n  /** Construct from binary DCS code. */\n  static SelectiveCall fromBinaryDCS(unsigned int code, bool inverted);\n  /** Returns a vector of standard selective calls. */\n  static const QVector<SelectiveCall> &standard();\n\nprotected:\n  /// Specifies the selective call type.\n  Type type;\n  union {\n    /// CTCSS frequency in 0.1Hz\n    uint16_t ctcss;\n    struct {\n      /// Binary DCS code\n      uint16_t code;\n      /// If @c true, the code is inverted.\n      bool inverted;\n    } dcs;\n  };\n\nprotected:\n  /** Fixed table of standard values. */\n  static QVector<SelectiveCall> _standard;\n};\n\nQ_DECLARE_METATYPE(SelectiveCall)\n\n\nnamespace YAML\n{\n  /** Implements the conversion to and from YAML::Node. */\n  template<>\n  struct convert<SelectiveCall>\n  {\n    /** Serializes the selective call. */\n    static Node encode(const SelectiveCall& rhs) {\n      Node node;\n      if (rhs.isCTCSS())\n        node[\"ctcss\"] = rhs.format().toStdString();\n      else if (rhs.isDCS())\n        node[\"dcs\"] = rhs.format().toStdString();\n      return node;\n    }\n\n    /** Parses the selective call. */\n    static bool decode(const Node& node, SelectiveCall& rhs) {\n      if (node.IsNull()) {\n        rhs = SelectiveCall();\n        return true;\n      }\n\n      if ((! node.IsMap()) || (1 != node.size()))\n        return false;\n\n      if (node[\"ctcss\"])\n        rhs = SelectiveCall::parseCTCSS(QString::fromStdString(node[\"ctcss\"].as<std::string>()));\n\n      if (node[\"dcs\"])\n        rhs = SelectiveCall::parseDCS(QString::fromStdString(node[\"dcs\"].as<std::string>()));\n\n      return rhs.isValid();\n    }\n  };\n}\n\n#endif // SIGNALING_HH\n"
  },
  {
    "path": "lib/smsextension.cc",
    "content": "#include \"smsextension.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of SMSTemplate\n * ********************************************************************************************* */\nSMSTemplate::SMSTemplate(QObject *parent)\n  : ConfigObject{parent}, _message()\n{\n  // pass...\n}\n\nConfigItem *\nSMSTemplate::clone() const {\n  ConfigItem *item = new SMSTemplate();\n  if (! item->copy(*this)) {\n    delete item;\n    return nullptr;\n  }\n  return item;\n}\n\nconst QString &\nSMSTemplate::message() const {\n  return _message;\n}\n\nvoid\nSMSTemplate::setMessage(const QString message) {\n  if (_message == message)\n    return;\n  _message = message;\n  emit modified(this);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of SMSTemplates\n * ********************************************************************************************* */\nSMSTemplates::SMSTemplates(QObject *parent)\n  : ConfigObjectList(SMSTemplate::staticMetaObject, parent)\n{\n  // pass...\n}\n\nConfigItem *\nSMSTemplates::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err);\n  return new SMSTemplate();\n}\n\nSMSTemplate *\nSMSTemplates::message(unsigned int i) const {\n  return get(i)->as<SMSTemplate>();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioSettingsExtension\n * ********************************************************************************************* */\nSMSExtension::SMSExtension(QObject *parent)\n  : ConfigExtension{parent}, _format(Format::Motorola), _smsTemplates(new SMSTemplates(this))\n{\n  // pass...\n}\n\nSMSExtension::Format\nSMSExtension::format() const {\n  return _format;\n}\nvoid\nSMSExtension::setFormat(Format format) {\n  if (_format == format)\n    return;\n  _format = format;\n  emit modified(this);\n}\n\nConfigItem *\nSMSExtension::clone() const {\n  ConfigItem *item = new SMSExtension();\n  if (! item->copy(*this)) {\n    delete item;\n    return nullptr;\n  }\n  return item;\n}\n\nSMSTemplates *\nSMSExtension::smsTemplates() const {\n  return _smsTemplates;\n}\n\n"
  },
  {
    "path": "lib/smsextension.hh",
    "content": "#ifndef SMSEXTENSION_HH\n#define SMSEXTENSION_HH\n\n#include \"configobject.hh\"\n#include \"interval.hh\"\n\n\n/** Represents a SMS message template (pre defined message).\n * Instances of this class are held in the @c SMSExtension. */\nclass SMSTemplate: public ConfigObject\n{\n  Q_OBJECT\n\n  /** Specifies the prefix for every ID assigned to every message during serialization. */\n  Q_CLASSINFO(\"IdPrefix\", \"sms\")\n\n  /** The message text. */\n  Q_PROPERTY(QString message READ message WRITE setMessage)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit SMSTemplate(QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the message text. */\n  const QString &message() const;\n  /** Sets the message text. */\n  void setMessage(const QString message);\n\nprotected:\n  /** Holds the message text. */\n  QString _message;\n};\n\n\n/** Just a list, holding the SMS templates. */\nclass SMSTemplates: public ConfigObjectList\n{\n  Q_OBJECT\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE SMSTemplates(QObject *parent=nullptr);\n\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Returns the i-th message. */\n  SMSTemplate *message(unsigned int i) const;\n};\n\n\n/** Extension collecting all settings associated with SMS messages. */\nclass SMSExtension : public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The format for the SMS. */\n  Q_PROPERTY(Format format READ format WRITE setFormat)\n  /** All predefined SMS messages. */\n  Q_PROPERTY(SMSTemplates *templates READ smsTemplates)\n\npublic:\n  /** Possible SMS formats, DMR is usually a good idea. */\n  enum class Format {\n    Motorola, Hytera, DMR\n  };\n  Q_ENUM(Format)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit SMSExtension(QObject *parent = nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the SMS format setting. */\n  Format format() const;\n  /** Sets the SMS format. */\n  void setFormat(Format format);\n\n  /** Returns a weak reference to the list of SMS templates. */\n  SMSTemplates *smsTemplates() const;\n\nprotected:\n  /** Holds the SMS format. */\n  Format _format;\n  /** Owns a reference to the list of SMS templates. */\n  SMSTemplates *_smsTemplates;\n};\n\n#endif // SMSEXTENSION_HH\n"
  },
  {
    "path": "lib/talkgroupdatabase.cc",
    "content": "#include \"talkgroupdatabase.hh\"\n#include <QStandardPaths>\n#include <QFileInfo>\n#include \"logger.hh\"\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QNetworkReply>\n#include <QDir>\n\n\n/* ********************************************************************************************* *\n * Implementation of TalkGroupDatabase::TalkGroup\n * ********************************************************************************************* */\nTalkGroupDatabase::TalkGroup::TalkGroup()\n  : id(0), name()\n{\n  // pass...\n}\n\nTalkGroupDatabase::TalkGroup::TalkGroup(const QString &name, unsigned number)\n{\n  this->id   = number;\n  this->name = name;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of TalkGroupDatabase\n * ********************************************************************************************* */\nTalkGroupDatabase::TalkGroupDatabase(unsigned updatePeriodDays, QObject *parent)\n  : QAbstractTableModel(parent), _talkgroups(), _network()\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(downloadFinished(QNetworkReply*)));\n\n  if ((! load()) || (updatePeriodDays < dbAge()))\n    download();\n}\n\nqint64\nTalkGroupDatabase::count() const {\n  return _talkgroups.count();\n}\n\nunsigned\nTalkGroupDatabase::dbAge() const {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/talkgroups.json\";\n  QFileInfo info(path);\n  if (! info.exists())\n    return -1;\n  return info.lastModified().daysTo(QDateTime::currentDateTime());\n}\n\nTalkGroupDatabase::TalkGroup\nTalkGroupDatabase::talkgroup(int index) const {\n  if ((0 > index) || (index >= count()))\n    return TalkGroup();\n  return _talkgroups[index];\n}\n\nvoid\nTalkGroupDatabase::download() {\n  QUrl url(\"https://api.brandmeister.network/v2/talkgroup/\");\n  QNetworkRequest request(url);\n  _network.get(request);\n}\n\nvoid\nTalkGroupDatabase::downloadFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    QString msg = QString(\"Cannot download user database: %1\").arg(reply->errorString());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFile file(path+\"/talkgroups.json\");\n  QDir directory;\n  if ((! directory.exists(path)) && (!directory.mkpath(path))) {\n    QString msg = QString(\"Cannot create path '%1'.\").arg(path);\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n  if (! file.open(QIODevice::WriteOnly)) {\n    QString msg = QString(\"Cannot save user database at '%1'.\").arg(path+\"/talkgroups.json\");\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  file.write(reply->readAll());\n  file.flush();\n  file.close();\n\n  load();\n  reply->deleteLater();\n}\n\nbool\nTalkGroupDatabase::load() {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  return load(path+\"/talkgroups.json\");\n}\n\nbool\nTalkGroupDatabase::load(const QString &filename) {\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    QString msg = QString(\"Cannot open talk group list '%1': \").arg(filename).arg(file.errorString());\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  QByteArray data = file.readAll();\n  file.close();\n\n  QJsonDocument doc = QJsonDocument::fromJson(data);\n  if (! doc.isObject()) {\n    QString msg = \"Failed to load talk groups: JSON document is not an object!\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  beginResetModel();\n  QJsonObject tgs = doc.object();\n  _talkgroups.clear();\n  _talkgroups.reserve(tgs.count());\n  for (QJsonObject::const_iterator tg = tgs.begin(); tg!=tgs.end(); tg++) {\n    _talkgroups.append(TalkGroup(tg.value().toString(), tg.key().toUInt()));\n  }\n  // Sort repeater w.r.t. their IDs\n  std::stable_sort(_talkgroups.begin(), _talkgroups.end(),\n                   [](const TalkGroup &a, const TalkGroup &b){ return a.id < b.id; });\n  // Done.\n  endResetModel();\n\n  logDebug() << \"Loaded talk group database with \" << _talkgroups.size()\n             << \" entries from \" << filename << \".\";\n\n  emit loaded();\n  return true;\n}\n\n\nint\nTalkGroupDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent)\n  return _talkgroups.count();\n}\n\nint\nTalkGroupDatabase::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent)\n  return 2;\n}\n\nQVariant\nTalkGroupDatabase::data(const QModelIndex &index, int role) const {\n  if ((Qt::EditRole != role) && ((Qt::DisplayRole != role)))\n    return QVariant();\n\n  if (index.row() >= _talkgroups.size())\n    return QVariant();\n\n  if (0 == index.column()) {\n    // Call\n    if (Qt::DisplayRole == role) {\n      return tr(\"%1 (%2)\").arg(_talkgroups[index.row()].name).arg(_talkgroups[index.row()].name);\n    } else {\n      return _talkgroups[index.row()].name;\n    }\n  } else if (1 == index.column()) {\n    // ID\n    return _talkgroups[index.row()].id;\n  }\n\n  return QVariant();\n}\n\n\n"
  },
  {
    "path": "lib/talkgroupdatabase.hh",
    "content": "#ifndef TALKGROUPDATABASE_HH\n#define TALKGROUPDATABASE_HH\n\n#include <QAbstractTableModel>\n#include <QNetworkAccessManager>\n\n/** Downloads, periodically updates and provides a list of talk group IDs and their names.\n *\n * @ingroup utils */\nclass TalkGroupDatabase : public QAbstractTableModel\n{\n  Q_OBJECT\n\n  /** A talk group entry in the database. */\n  class TalkGroup {\n  public:\n    /** Empty constructor. */\n    TalkGroup();\n    /** Constructor form name and DMR ID. */\n    TalkGroup(const QString &name, unsigned number);\n    /** The DMR ID of the talk group. */\n    unsigned id;\n    /** The Name of the talk group. */\n    QString name;\n  };\n\npublic:\n  /** Constructs a talk group database.\n   * @param updatePeriodDays Specifies the update period of the DB in days.\n   * @param parent Specifies the QObject parent. */\n  TalkGroupDatabase(unsigned updatePeriodDays=30, QObject *parent=nullptr);\n\n  /** Returns the number of talk groups. */\n  qint64 count() const;\n  /** Returns the age of the database in days. */\n  unsigned dbAge() const;\n\n  /** Returns the talk group entry at the given index. */\n  TalkGroup talkgroup(int index) const;\n\n  /** Loads all entries from the downloaded talk group db. */\n  bool load();\n  /** Loads all entries from the talk group db at the specified location. */\n  bool load(const QString &filename);\n\n  /** Implements the QAbstractTableModel interface, returns the number of rows (number of entries). */\n  int rowCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface, returns the number of columns. */\n  int columnCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface, return the entry data. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n\nsignals:\n  /** Gets emitted once the talk group database has been loaded. */\n  void loaded();\n  /** Gets emitted if the loading of the talk group database fails. */\n  void error(const QString &msg);\n\npublic slots:\n  /** Starts the download of the talk group database. */\n  void download();\n\nprivate slots:\n  /** Gets called whenever the download is complete. */\n  void downloadFinished(QNetworkReply *reply);\n\nprotected:\n  /** Holds all talk groups as id->name table. */\n  QVector<TalkGroup>    _talkgroups;\n  /** The network access used for downloading. */\n  QNetworkAccessManager _network;\n};\n\n#endif // TALKGROUPDATABASE_HH\n"
  },
  {
    "path": "lib/tonesettings.cc",
    "content": "#include \"tonesettings.hh\"\n\nToneSettings::ToneSettings(QObject *parent)\n  : ConfigItem(parent), _silent(false), _keyTone(Level::null()), _smsTone(true), _ringtone(true),\n    _bootTone(false), _bootMelody(new Melody(100, this)),\n    _talkPermit(Channel::Type::None),\n    _callStart(Channel::Type::Digital), _callStartMelody(new Melody(100, this)),\n    _callEnd(Channel::Type::None), _callEndMelody(new Melody(100, this)),\n    _channelIdle(Channel::Type::All), _channelIdleMelody(new Melody(100, this)),\n    _callReset(true), _callResetMelody(new Melody(100, this))\n{\n  connect(_bootMelody, &Melody::modified, this, &ToneSettings::modified);\n  connect(_callStartMelody, &Melody::modified, this, &ToneSettings::modified);\n  connect(_callEndMelody, &Melody::modified, this, &ToneSettings::modified);\n  connect(_channelIdleMelody, &Melody::modified, this, &ToneSettings::modified);\n  connect(_callResetMelody, &Melody::modified, this, &ToneSettings::modified);\n}\n\nConfigItem *\nToneSettings::clone() const {\n  auto obj = new ToneSettings();\n  if (! obj->copy(*this)) {\n    delete obj;\n    return nullptr;\n  }\n  return obj;\n}\n\n\nbool\nToneSettings::silent() const {\n  return _silent;\n}\n\nvoid\nToneSettings::enableSilent(bool enable) {\n  if (_silent == enable)\n    return;\n  _silent = enable;\n  emit modified(this);\n}\n\n\nbool\nToneSettings::keyToneEnabled() const {\n  return _keyTone.isFinite();\n}\n\nLevel\nToneSettings::keyToneVolume() const {\n  return _keyTone;\n}\n\nvoid\nToneSettings::setKeyToneVolume(Level volume) {\n  if (_keyTone == volume)\n    return;\n  _keyTone = volume;\n  emit modified(this);\n}\n\nvoid\nToneSettings::disableKeyTone() {\n  setKeyToneVolume(Level::null());\n}\n\n\nbool\nToneSettings::smsToneEnabled() const {\n  return _smsTone;\n}\n\nvoid\nToneSettings::enableSMSTone(bool enabled) {\n  if (_smsTone == enabled)\n    return;\n  _smsTone = enabled;\n  emit modified(this);\n}\n\n\nbool\nToneSettings::ringtoneEnabled() const {\n  return _ringtone;\n}\n\nvoid\nToneSettings::enableRingtone(bool enabled) {\n  if (_ringtone == enabled)\n    return;\n  _ringtone = enabled;\n  emit modified(this);\n}\n\n\nbool\nToneSettings::bootToneEnabled() const {\n  return _bootTone;\n}\n\nvoid\nToneSettings::enableBootTone(bool enabled) {\n  if (_bootTone == enabled)\n    return;\n  _bootTone = enabled;\n  emit modified(this);\n}\n\nMelody *\nToneSettings::bootMelody() const {\n  return _bootMelody;\n}\n\n\nChannel::Types\nToneSettings::talkPermit() const {\n  return _talkPermit;\n}\n\nvoid\nToneSettings::setTalkPermit(Channel::Types type) {\n  if (_talkPermit == type)\n    return;\n  _talkPermit = type;\n  emit modified(this);\n}\n\n\nChannel::Types\nToneSettings::callStart() const {\n  return _callStart;\n}\n\nvoid\nToneSettings::setCallStart(Channel::Types type) {\n  if (_callStart == type)\n    return;\n  _callStart = type;\n  emit modified(this);\n}\n\nMelody *\nToneSettings::callStartMelody() const {\n  return _callStartMelody;\n}\n\n\nChannel::Types\nToneSettings::callEnd() const {\n  return _callEnd;\n}\n\nvoid\nToneSettings::setCallEnd(Channel::Types type) {\n  if (_callEnd == type)\n    return;\n  _callEnd = type;\n  emit modified(this);\n}\n\nMelody *\nToneSettings::callEndMelody() const {\n  return _callEndMelody;\n}\n\n\nChannel::Types\nToneSettings::channelIdle() const {\n  return _channelIdle;\n}\n\nvoid\nToneSettings::setChannelIdle(Channel::Types type) {\n  if (_channelIdle == type)\n    return;\n  _channelIdle = type;\n  emit modified(this);\n}\n\nMelody *\nToneSettings::channelIdleMelody() const {\n  return _channelIdleMelody;\n}\n\n\nbool\nToneSettings::callResetEnabled() const {\n  return _callReset;\n}\n\nvoid\nToneSettings::enableCallReset(bool enabled) {\n  if (_callReset == enabled)\n    return;\n  _callReset = enabled;\n  emit modified(this);\n}\n\nMelody *\nToneSettings::callResetMelody() const {\n  return _callResetMelody;\n}"
  },
  {
    "path": "lib/tonesettings.hh",
    "content": "#ifndef TONESETTINGS_HH\n#define TONESETTINGS_HH\n\n#include \"configobject.hh\"\n#include \"level.hh\"\n#include \"channel.hh\"\n\n\nclass ToneSettings : public ConfigItem\n{\n  Q_OBJECT\n\n  /** Disables all tones. */\n  Q_PROPERTY(bool silent READ silent WRITE enableSilent)\n  /** Key tone volume. */\n  Q_PROPERTY(Level keyTone READ keyToneVolume WRITE setKeyToneVolume)\n\n  /** Enables notification tone for SMS reception. */\n  Q_PROPERTY(bool smsTone READ smsToneEnabled WRITE enableSMSTone)\n  /** Enables ringtones. */\n  Q_PROPERTY(bool ringtone READ ringtoneEnabled WRITE enableRingtone);\n  /** Enables boot melody. */\n  Q_PROPERTY(bool bootTone READ bootToneEnabled WRITE enableBootTone)\n  /** Enables boot melody. */\n  Q_PROPERTY(Melody * bootMelody READ bootMelody)\n  /** Enables talk-permit tone for different channel types.\n   * This tone sounds, whenever the PTT is pressed and the admit criterion is met. */\n  Q_PROPERTY(Channel::Types talkPermit READ talkPermit WRITE setTalkPermit)\n  /** Enables call-start tone for different channel types. */\n  Q_PROPERTY(Channel::Types callStart READ callStart WRITE setCallStart)\n  /** The call-start melody. */\n  Q_PROPERTY(Melody * callStartMelody READ callStartMelody)\n  /** Enables call-end tone for different channel types. */\n  Q_PROPERTY(Channel::Types callEnd READ callEnd WRITE setCallEnd)\n  /** The call-end melody. */\n  Q_PROPERTY(Melody * callEndMelody READ callEndMelody)\n  /** Enables channel-idle tone for different channel types.\n   * This tone sounds, once the channel becomes free after a call. */\n  Q_PROPERTY(Channel::Types channelIdle READ channelIdle WRITE setChannelIdle)\n  /** The channel-idle melody. */\n  Q_PROPERTY(Melody * channelIdleMelody READ channelIdleMelody)\n  /** Enables call reset tone.\n   * This tone sounds, once the (private or group-call) hang-time passed.\n   * This affects only DMR and M17 channels. */\n  Q_PROPERTY(bool callReset READ callResetEnabled WRITE enableCallReset)\n  /** The call-reset melody. */\n  Q_PROPERTY(Melody * callResetMelody READ callResetMelody)\n\npublic:\n  /** Default constructor. */\n  explicit ToneSettings(QObject *parent = nullptr);\n\n  ConfigItem *clone() const override;\n\n  /** Returns @c true if all tones are disabled. */\n  bool silent() const;\n  /** Disables all tones. */\n  void enableSilent(bool enable);\n\n  /** Returns @c true, if the key-tones are enabled. */\n  bool keyToneEnabled() const;\n  /** Returns the key tone volume. */\n  Level keyToneVolume() const;\n  /** Sets key tone volume. */\n  void setKeyToneVolume(Level volume);\n  /** Disables key-tones. */\n  void disableKeyTone();\n\n  /** Returns @c true, if the SMS tone is enabled. */\n  bool smsToneEnabled() const;\n  /** Enables/disables SMS tone. */\n  void enableSMSTone(bool enable);\n\n  /** Returns @c true, if the ringtone is enabled. */\n  bool ringtoneEnabled() const;\n  /** Enables ringtone. */\n  void enableRingtone(bool enable);\n\n  /** Returns @c true, if the boot-tone is enabled. */\n  bool bootToneEnabled() const;\n  /** Enables boot-tone. */\n  void enableBootTone(bool enable);\n  /** Returns a reference to the boot melody. */\n  Melody *bootMelody() const;\n\n  /** Returns channel types, for which the talk-permit tone is enabled. */\n  Channel::Types talkPermit() const;\n  /** Sets channel types, for which the talk-permit tone is enabled. */\n  void setTalkPermit(Channel::Types permit);\n\n  /** Returns channel types, for which the call-start tone is enabled. */\n  Channel::Types callStart() const;\n  /** Sets channel types, for which the call-start tone is enabled. */\n  void setCallStart(Channel::Types permit);\n  /** Returns a reference to the call-start melody. */\n  Melody *callStartMelody() const;\n\n  /** Returns channel types, for which the call-end tone is enabled. */\n  Channel::Types callEnd() const;\n  /** Sets channel types, for which the call-end tone is enabled. */\n  void setCallEnd(Channel::Types permit);\n  /** Returns a reference to the call-end melody. */\n  Melody *callEndMelody() const;\n\n  /** Returns channel types, for which the channel-idle tone is enabled. */\n  Channel::Types channelIdle() const;\n  /** Sets channel types, for which the channel-idle tone is enabled. */\n  void setChannelIdle(Channel::Types permit);\n  /** Returns a reference to the channel-idle melody. */\n  Melody *channelIdleMelody() const;\n\n  /** Returns @c true, if the call-reset tone is enabled. */\n  bool callResetEnabled() const;\n  /** Enables call-reset tone. */\n  void enableCallReset(bool enable);\n  /** Returns a reference to the call-reset melody. */\n  Melody *callResetMelody() const;\n\nprotected:\n  /** If @c true, all tones are disabled. */\n  bool _silent;\n  /** The key tone volume. */\n  Level _keyTone;\n  /** Enables SMS tones. */\n  bool _smsTone;\n  /** Enables ringtones. */\n  bool _ringtone;\n  /** Enables boot-tone. */\n  bool _bootTone;\n  /** The boot melody. */\n  Melody *_bootMelody;\n  /** Enables talk-permit tones for several channel types. */\n  Channel::Types _talkPermit;\n  /** Enables call-start tones for several channel types. */\n  Channel::Types _callStart;\n  Melody *_callStartMelody;             ///< Call melody.\n  /** Enables call-end tones for several channel types. */\n  Channel::Types _callEnd;\n  Melody *_callEndMelody;          ///< Call end melody.\n  /** Enables channel-idle tones for several channel types. */\n  Channel::Types _channelIdle;\n  Melody *_channelIdleMelody;             ///< Idle melody.\n  /** Enables call-reset tones. */\n  bool _callReset;\n  Melody *_callResetMelody;            ///< Reset melody.\n};\n\n\n\n#endif //TONESETTINGS_HH\n"
  },
  {
    "path": "lib/transferflags.cc",
    "content": "#include \"transferflags.hh\"\n\nTransferFlags::TransferFlags()\n  : _blocking(false), _updateDeviceClock(false)\n{\n  // pass...\n}\n\nTransferFlags::TransferFlags(bool blocking, bool updateDeviceClock)\n  : _blocking(blocking), _updateDeviceClock(updateDeviceClock)\n{\n  // pass...\n}\n\n\nbool\nTransferFlags::blocking() const {\n  return _blocking;\n}\n\nvoid\nTransferFlags::setBlocking(bool enable) {\n  _blocking = enable;\n}\n\n\nbool\nTransferFlags::updateDeviceClock() const {\n  return _updateDeviceClock;\n}\n\nvoid\nTransferFlags::setUpdateDeviceClock(bool enable) {\n  _updateDeviceClock = enable;\n}\n\n\n\n"
  },
  {
    "path": "lib/transferflags.hh",
    "content": "#ifndef TRANSFERFLAGS_HH\n#define TRANSFERFLAGS_HH\n\n\n/** Controls the transfer of codeplugs, callsign DBs etc to the device. */\nclass TransferFlags\n{\npublic:\n  /** Default constructor. */\n  TransferFlags();\n  /** Constructor. */\n  TransferFlags(bool blocking, bool updateDeviceClock);\n\n  /** Returns @c true if the transfer is blocking. */\n  bool blocking() const;\n  /** Set if transfer is blocking. */\n  void setBlocking(bool enable);\n\n  /** Returns @c true if the device clock gets updated during transfer. */\n  bool updateDeviceClock() const;\n  /** Sets if the device clock gets updated during the transfer. */\n  void setUpdateDeviceClock(bool enable);\n\nprotected:\n  /** If @c true, the transfer is blocking. */\n  bool _blocking;\n  /** If @c true, the device clock gets updated during the transfer. */\n  bool _updateDeviceClock;\n};\n\n\n#endif // TRANSFERFLAGS_HH\n"
  },
  {
    "path": "lib/transponderdatabase.cc",
    "content": "#include \"transponderdatabase.hh\"\n\n#include <QStandardPaths>\n#include <QFile>\n#include <QDir>\n#include <QNetworkRequest>\n#include <QNetworkReply>\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QJsonObject>\n\n#include \"logger.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of Transponder\n * ********************************************************************************************* */\nTransponder::Transponder()\n  : _satellite(0), _type(Type::Transmitter), _mode(Mode::CW), _name(), _downlink(), _uplink()\n{\n  // pass...\n}\n\nunsigned int\nTransponder::satellite() const {\n  return _satellite;\n}\n\nbool\nTransponder::isValid() const {\n  return 0 != _satellite;\n}\n\nTransponder::Type\nTransponder::type() const {\n  return _type;\n}\n\nTransponder::Mode\nTransponder::mode() const {\n  return _mode;\n}\n\nconst QString &\nTransponder::name() const {\n  return _name;\n}\n\nconst Frequency &\nTransponder::uplink() const {\n  return _uplink;\n}\n\nconst Frequency &\nTransponder::downlink() const {\n  return _downlink;\n}\n\n\nTransponder\nTransponder::fromSATNOGS(const QJsonObject &obj) {\n  Transponder t;\n  t._satellite = obj.value(\"norad_cat_id\").toInt();\n  t._name = obj.value(\"description\").toString();\n  if ((! obj.contains(\"alive\")) || (! obj.value(\"alive\").toBool())) {\n    //logInfo() << \"Skip transponder '\" << t._name << \"': no alive flag set or false.\";\n    return Transponder();\n  }\n  if ((! obj.contains(\"mode\")) || obj.value(\"mode\").isNull()) {\n    //logInfo() << \"Skip transponder '\" << t._name << \"': no mode set.\";\n    return Transponder();\n  }\n  if ((! obj.contains(\"downlink_low\")) || (obj.value(\"downlink_low\").isNull())) {\n    //logInfo() << \"Skip transponder '\" << t._name << \"': no downlink frequency given.\";\n    return Transponder();\n  }\n\n  QString tn = obj.value(\"type\").toString();\n  if ((\"Transmitter\" == tn) || (\"Beacon\" == tn)) {\n    t._type = Type::Transmitter;\n  } else if (\"Transceiver\" == tn) {\n    t._type = Type::Transponder;\n  } else {\n    //logInfo() << \"Skip transponder '\" << t._name << \"': unknown type '\" << tn << \"'.\";\n    return Transponder();\n  }\n\n  QString mode = obj.value(\"mode\").toString();\n  if (\"FM\" == mode) {\n    t._mode = Mode::FM;\n  } else if (\"AFSK\" == mode) {\n    t._mode = Mode::APRS;\n  } else if (\"CW\" == mode) {\n    t._mode = Mode::CW;\n  } else if (\"BPSK\" == mode) {\n    t._mode = Mode::BPSK;\n  } else {\n    //logInfo() << \"Skip transponder '\" << t._name << \"': unknown mode '\" << mode << \"'.\";\n    return Transponder();\n  }\n\n  t._downlink = Frequency::fromHz(obj.value(\"downlink_low\").toInt());\n  if (obj.contains(\"uplink_low\") && (! obj.value(\"uplink_low\").isNull()))\n    t._uplink = Frequency::fromHz(obj.value(\"uplink_low\").toInt());\n\n  return t;\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of TransponderDatabase\n * ********************************************************************************************* */\nTransponderDatabase::TransponderDatabase(bool autoLoad, unsigned int updatePeriod, QObject *parent)\n  : QAbstractTableModel{parent}, _updatePeriod(updatePeriod), _transponders(), _network()\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(downloadFinished(QNetworkReply*)));\n\n  if (autoLoad) load();\n}\n\nconst Transponder &\nTransponderDatabase::getAt(unsigned int idx) const {\n  return _transponders[idx];\n}\n\nint\nTransponderDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return _transponders.size();\n}\n\nint\nTransponderDatabase::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return 5;\n}\n\nQVariant\nTransponderDatabase::data(const QModelIndex &index, int role) const {\n  if (index.row() >= _transponders.size())\n    return QVariant();\n\n  if (Qt::DisplayRole == role) {\n    if (0 == index.column()) {\n      switch (_transponders.at(index.row()).type()) {\n      case Transponder::Type::Transmitter: return \"Transmitter\";\n      case Transponder::Type::Transponder: return \"Transponder\";\n      }\n    } else if (1 == index.column()) {\n      switch (_transponders.at(index.row()).mode()) {\n      case Transponder::Mode::CW: return \"CW\";\n      case Transponder::Mode::BPSK: return \"BPSK\";\n      case Transponder::Mode::FM: return \"FM\";\n      case Transponder::Mode::APRS: return \"APRS\";\n      }\n    } else if (2 == index.column()) {\n      return _transponders.at(index.row()).name();\n    } else if (3 == index.column()) {\n      return _transponders.at(index.row()).uplink().format();\n    } else if (4 == index.column()) {\n      return _transponders.at(index.row()).downlink().format();\n    }\n  }\n\n  return QVariant();\n}\n\n\nTransponderDatabase::const_iterator\nTransponderDatabase::begin() const {\n  return _transponders.begin();\n}\n\nTransponderDatabase::const_iterator\nTransponderDatabase::end() const {\n  return _transponders.end();\n}\n\n\nunsigned\nTransponderDatabase::dbAge() const {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/transponders.json\";\n  QFileInfo info(path);\n  if (! info.exists())\n    return -1;\n  return info.lastModified().daysTo(QDateTime::currentDateTime());\n}\n\n\nvoid\nTransponderDatabase::load() {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/transponders.json\";\n  if ((! load(path)) || (_updatePeriod < dbAge()))\n    download();\n}\n\n\nbool\nTransponderDatabase::load(const QString &filename) {\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    QString msg = QString(\"Cannot open transponders '%1': %2\").arg(filename).arg(file.errorString());\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  QByteArray data = file.readAll();\n  file.close();\n\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(data, &err);\n  if (doc.isEmpty()) {\n    QString msg = \"Failed to load transponders: \" + err.errorString();\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  if (! doc.isArray()) {\n    QString msg = \"Failed to load transponders: JSON document is not an array!\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  beginResetModel();\n\n  QJsonArray array = doc.array();\n  _transponders.clear();\n  _transponders.reserve(array.size());\n  for (int i=0; i<array.size(); i++) {\n    auto tranponder = Transponder::fromSATNOGS(array.at(i).toObject());\n    if (tranponder.isValid()) {\n      _transponders.append(tranponder);\n    }\n  }\n  // Sort repeater w.r.t. their sat ID\n  std::stable_sort(_transponders.begin(), _transponders.end(),\n                   [](const Transponder &a, const Transponder &b){ return a.satellite() < b.satellite(); });\n\n  // Done.\n  endResetModel();\n\n  logDebug() << \"Loaded transponder with \" << _transponders.size() << \" entries from \" << filename << \".\";\n\n  emit loaded();\n  return true;\n}\n\n\nvoid\nTransponderDatabase::download() {\n  QUrl url(\"https://db.satnogs.org/api/transmitters/?format=json\");\n  QNetworkRequest request(url);\n  _network.get(request);\n}\n\nvoid\nTransponderDatabase::downloadFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    QString msg = QString(\"Cannot download transponders: %1\").arg(reply->errorString());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFile file(path+\"/transponders.json\");\n\n  QDir directory;\n  if ((! directory.exists(path)) && (!directory.mkpath(path))) {\n    QString msg = QString(\"Cannot create path '%1'.\").arg(path);\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  if (! file.open(QIODevice::WriteOnly)) {\n    QString msg = QString(\"Cannot save transponders at '%1'.\").arg(file.fileName());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  file.write(reply->readAll());\n  file.flush();\n  file.close();\n\n  load();\n  reply->deleteLater();\n}\n\n\n"
  },
  {
    "path": "lib/transponderdatabase.hh",
    "content": "#ifndef TRANSPONDERDATABASE_HH\n#define TRANSPONDERDATABASE_HH\n\n#include <QAbstractTableModel>\n#include <QNetworkAccessManager>\n\n#include \"frequency.hh\"\n\n\n/** Represents a single transponder of a satellite. */\nstruct Transponder\n{\n\npublic:\n  /** Possible transponder types. */\n  enum class Type {\n    Transponder,  ///< Proper transponder (repeater).\n    Transmitter   ///< Just a transmitter (beacon).\n  };\n\n  /** Possible transponder modes. */\n  enum class Mode {\n    FM,   ///< Plain FM.\n    CW,   ///< Simple CW.\n    APRS, ///< AFSK APRS.\n    BPSK  ///< BPSK.\n  };\n\npublic:\n  /** Default constructor. */\n  Transponder();\n\n  /** Returns @c true, if the transponder is valid.\n   * I.e., it is associated with a satellite. */\n  bool isValid() const;\n\n  /** Returns the NORAD id of the associated satellite. */\n  unsigned int satellite() const;\n  /** Returns the transponder type. */\n  Type type() const;\n  /** Returns the transponder mode. */\n  Mode mode() const;\n\n  /** Returns a descriptive name of the transponder. */\n  const QString &name() const;\n\n  /** Returns the uplink frequency, if there is one.\n   * An upload frequency is usually missing, if the type is @c Type::Transmitter. */\n  const Frequency &uplink() const;\n  /** Returns the downlink frequency. */\n  const Frequency &downlink() const;\n\npublic:\n  /** Parses a transponder from the given SatNOGS JSON object. */\n  static Transponder fromSATNOGS(const QJsonObject &obj);\n\nprotected:\n  /** Holds the NORAD id of the satellite. */\n  unsigned int _satellite;\n  /** Holds the transponder type. */\n  Type _type;\n  /** Holds the transponder mode. */\n  Mode _mode;\n  /** Holds the name. */\n  QString _name;\n  /** Holds the downlink frequency. */\n  Frequency _downlink;\n  /** Holds the uplink frequency. */\n  Frequency _uplink;\n};\n\n\n\n/** Implements the database of all known transponder.\n * @ingroup sat */\nclass TransponderDatabase : public QAbstractTableModel\n{\n  Q_OBJECT\n\npublic:\n  /** Just a const iterator over all transponder. */\n  typedef QVector<Transponder>::const_iterator const_iterator;\n\npublic:\n  /** Constructor.\n   * @param autoLoad If @c true, the transponder information gets downloaded and loaded\n   *        automatically.\n   * @param updatePeriod Specifies the maximum age of the cache in days.\n   * @param parent The QObject parent. */\n  explicit TransponderDatabase(bool autoLoad, unsigned int updatePeriod = 7, QObject *parent = nullptr);\n\n  /** The current age of the cache. */\n  unsigned int dbAge() const;\n\n  /** Returns the i-th transponder. */\n  const Transponder &getAt(unsigned int idx) const;\n\n  /** Implements the QAbstractTableModel interface.\n   * Returns the number of rows in the table. That is, the number of transponder. */\n  int rowCount(const QModelIndex &parent) const;\n  /** Implements the QAbstractTableModel interface. Returns the number of columns in the table. */\n  int columnCount(const QModelIndex &parent) const;\n  /** Implements the QAbstractTableModel interface. Returns the data for the cell. */\n  QVariant data(const QModelIndex &index, int role) const;\n\n  /** Returns an iterator, pointing at the first transponder. */\n  const_iterator begin() const;\n  /** Returns an iterator, pointing right after the last transponder. */\n  const_iterator end() const;\n\npublic slots:\n  /** Downloads and loads all transponder information. */\n  void load();\n\nsignals:\n  /** Gets emitted once the transponder has been loaded. */\n  void loaded();\n  /** Gets emitted if the loading one of the sources fails. */\n  void error(const QString &msg);\n\npublic slots:\n  /** Starts the download of the transponder. */\n  void download();\n\nprivate slots:\n  /** Gets called whenever the transponder download is complete. */\n  void downloadFinished(QNetworkReply *reply);\n\nprotected:\n  /** Loads the transponder information from the given file. */\n  bool load(const QString &filename);\n\nprivate:\n  /** The update period of the transponders in days. */\n  unsigned int _updatePeriod;\n  /** Holds all transponder sorted by the catalog number of their sats. */\n  QVector<Transponder>  _transponders;\n  /** The network access used for downloading. */\n  QNetworkAccessManager _network;\n\n};\n\n#endif // TRANSPONDERDATABASE_HH\n"
  },
  {
    "path": "lib/tyt_callsigndb.cc",
    "content": "#include \"tyt_callsigndb.hh\"\n#include <QtEndian>\n\n#include \"utils.hh\"\n\n\n#define MAX_CALLSIGNS              122197LL  // Maximum number of callsings in DB\n\n#define ADDR_CALLSIGN_INDEX      0x00200000  // Start of callsign database\n#define NUM_INDEX_ENTRIES              4096\n#define INDEX_ENTRY_SIZE         0x00000004  // Size of a index entry\n\n#define ADDR_CALLSIGNS           0x00204003  // Start of callsign entries\n#define CALLSIGN_ENTRY_SIZE      0x00000078  // Size of a call-sign entry\n\n\n/* ********************************************************************************************* *\n * Implementation of TyTCallsignDB::IndexElement\n * ********************************************************************************************* */\nTyTCallsignDB::IndexElement::IndexElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCallsignDB::IndexElement::IndexElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0003 + NUM_INDEX_ENTRIES*INDEX_ENTRY_SIZE)\n{\n  // pass...\n}\n\nTyTCallsignDB::IndexElement::~IndexElement() {\n  // pass...\n}\n\nvoid\nTyTCallsignDB::IndexElement::clear() {\n  setNumEntries(0);\n  for (int i=0; i<NUM_INDEX_ENTRIES; i++)\n    Entry(_data+0x03 + i*INDEX_ENTRY_SIZE).clear();\n}\n\nvoid\nTyTCallsignDB::IndexElement::setNumEntries(unsigned n) {\n  uint8_t *ptr = (_data + 0x0000);\n  ptr[0] = ((n>>16) & 0xff);\n  ptr[1] = ((n>> 8) & 0xff);\n  ptr[2] = ((n>> 0) & 0xff);\n}\n\nvoid\nTyTCallsignDB::IndexElement::setIndexEntry(unsigned n, unsigned id, unsigned index) {\n  Entry(_data+0x03 + n*INDEX_ENTRY_SIZE).set(id, index);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of TyTCallsignDB::IndexElement::Entry\n * ********************************************************************************************* */\nTyTCallsignDB::IndexElement::Entry::Entry(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCallsignDB::IndexElement::Entry::Entry(uint8_t *ptr)\n  : Codeplug::Element(ptr, INDEX_ENTRY_SIZE)\n{\n  // pass...\n}\n\nTyTCallsignDB::IndexElement::Entry::~Entry() {\n  // pass...\n}\n\nvoid\nTyTCallsignDB::IndexElement::Entry::clear() {\n  memset(_data, 0xff, INDEX_ENTRY_SIZE);\n}\n\nvoid\nTyTCallsignDB::IndexElement::Entry::set(unsigned id, unsigned index) {\n  _data[0] = id>>16;\n  _data[1] = ((id>>8)&0xf0) | ((index>>16) & 0xf);\n  _data[2] = index>>8;\n  _data[3] = index;\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of TyTCallsignDB::EntryElement\n * ********************************************************************************************* */\nTyTCallsignDB::EntryElement::EntryElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCallsignDB::EntryElement::EntryElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, CALLSIGN_ENTRY_SIZE)\n{\n  // pass...\n}\n\nTyTCallsignDB::EntryElement::~EntryElement() {\n  // pass...\n}\n\nvoid\nTyTCallsignDB::EntryElement::clear() {\n  memset(_data, 0xff, CALLSIGN_ENTRY_SIZE);\n}\n\nvoid\nTyTCallsignDB::EntryElement::set(const UserDatabase::User &user) {\n  // Set id\n  *((uint32_t *)(_data + 0x0000)) = qToLittleEndian(user.id);\n  _data[3] = 0xff;\n\n  // Set call\n  encode_ascii(_data + 0x0004, user.call, 16);\n\n  // Set name\n  QString name = user.name;\n  if ((! user.surname.isEmpty()) && (100 >= name.length() + 1 + user.surname.size()))\n    name += \" \" + user.surname;\n  if ((! user.city.isEmpty()) && (100 >= name.length() + 2 + user.city.size()))\n    name += \", \" + user.city;\n  if ((! user.state.isEmpty()) && (100 >= name.length() + 2 + user.state.size()))\n    name += \", \" + user.state;\n  if ((! user.country.isEmpty()) && (100 >= name.length() + 2 + user.country.size()))\n    name += \", \" + user.country;\n  if ((! user.comment.isEmpty()) && (100 >= name.length() + 2 + user.comment.size()))\n    name += \". \" + user.comment;\n\n  encode_ascii(_data + 0x0014, name, 100);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of TyTCallsignDB\n * ********************************************************************************************* */\nTyTCallsignDB::TyTCallsignDB(QObject *parent)\n  : CallsignDB(parent)\n{\n  // allocate and clear DB memory\n  addImage(\"TYT Callsign database.\");\n}\n\nTyTCallsignDB::~TyTCallsignDB() {\n  // pass...\n}\n\nbool\nTyTCallsignDB::encode(UserDatabase *db, const Flags &selection, const ErrorStack &err) {\n  Q_UNUSED(err)\n\n  // Allocate space for callsign db\n  size_t n = std::min(MAX_CALLSIGNS, db->count());\n  if (selection.hasCountLimit())\n    n = std::min(n, selection.countLimit());\n  allocate(n);\n\n  // Clear DB index\n  clearIndex();\n\n  // Select n users and sort them in ascending order of their IDs\n  QVector<UserDatabase::User> users;\n  for (unsigned i=0; i<n; i++)\n    users.append(db->user(i));\n  std::sort(users.begin(), users.end(),\n            [](const UserDatabase::User &a, const UserDatabase::User &b) { return a.id < b.id; });\n\n  // Store number of entries\n  setNumEntries(n);\n\n  // First index entry\n  int  j = 0;\n  setIndexEntry(j++, users[0].id, 1);\n  unsigned cidh = (users[0].id >> 12);\n\n  // Store users and update index\n  for (unsigned i=0; i<n; i++) {\n    setEntry(i, users[i]);\n    unsigned idh = (users[i].id >> 12);\n    if (idh != cidh) {\n      setIndexEntry(j++,users[i].id, i+1);\n      cidh = idh;\n    }\n  }\n\n  return true;\n}\n\nvoid\nTyTCallsignDB::allocate(unsigned n) {\n  n = std::min(n, unsigned(MAX_CALLSIGNS));\n  qint64 size = align_size(0x0003 + INDEX_ENTRY_SIZE*NUM_INDEX_ENTRIES + CALLSIGN_ENTRY_SIZE*n, 1024);\n\n  // allocate & clear memory\n  if (0 == image(0).numElements())\n    this->image(0).addElement(ADDR_CALLSIGN_INDEX, size);\n  memset(data(ADDR_CALLSIGN_INDEX), 0xff, size);\n}\n\nvoid\nTyTCallsignDB::clearIndex() {\n  IndexElement(data(ADDR_CALLSIGN_INDEX)).clear();\n}\n\nvoid\nTyTCallsignDB::setNumEntries(unsigned n) {\n  IndexElement(data(ADDR_CALLSIGN_INDEX)).setNumEntries(n);\n}\n\nvoid\nTyTCallsignDB::setIndexEntry(unsigned n, unsigned id, unsigned index) {\n  IndexElement(data(ADDR_CALLSIGN_INDEX)).setIndexEntry(n, id, index);\n}\n\nvoid\nTyTCallsignDB::setEntry(unsigned n, const UserDatabase::User &user) {\n  // Get pointer to entry\n  EntryElement(data(ADDR_CALLSIGNS + n*CALLSIGN_ENTRY_SIZE)).set(user);\n}\n"
  },
  {
    "path": "lib/tyt_callsigndb.hh",
    "content": "#ifndef TYTCALLSIGNDB_HH\n#define TYTCALLSIGNDB_HH\n\n#include \"codeplug.hh\"\n#include \"callsigndb.hh\"\n#include \"userdatabase.hh\"\n\n/** Base class for all call-sign DBs of TyT/Retevis devices.\n *\n * @section tytcdb Callsign database structure\n * <table>\n *  <tr><th>Start</th>    <th>End</th>      <th>Size</th>     <th>Content</th></tr>\n *  <tr><th colspan=\"4\">Callsign database 0x0200000-0x1000000</th></tr>\n *  <tr><td>0x200000</td> <td>0x204004</td> <td>0x04004</td>  <td>Callsign database index table, see @c TyTCallsignDB::IndexEntryElement</td></tr>\n *  <tr><td>0x204004</td> <td>0xffffdc</td> <td>0xdfbfd8</td> <td>max 122197 callsign database entries, see @c TyTCallsignDB::EntryElement. </td></tr>\n *  <tr><td>0xffffdc</td> <td>0x1000000</td> <td>0x00025</td> <td>Padding, filled with @c 0xff.</td></tr>\n * </table>\n\n * @ingroup tyt */\nclass TyTCallsignDB : public CallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Represents a search index over the complete call-sign database.\n   *\n   * Memory layout of encoded call-sign/user database:\n   * @verbinclude tytcallsigndbindex.txt\n   */\n  class IndexElement: public Codeplug::Element\n  {\n  public:\n    /** Represents an index entry, a pair of DMR ID and call-sign DB index.\n     *\n     * Memory layout of encoded call-sign/user database index entry:\n     * @verbinclude tytcallsigndbindexentry.txt\n     */\n    class Entry: public Codeplug::Element\n    {\n    protected:\n      /** Hidden constructor. */\n      Entry(uint8_t *ptr, size_t size);\n\n    public:\n      /** Constructor. */\n      explicit Entry(uint8_t *ptr);\n      /** Destructor. */\n      virtual ~Entry();\n\n      void clear();\n\n      /** Sets the index entry. */\n      virtual void set(unsigned id, unsigned index);\n    };\n\n  protected:\n    /** Hidden constructor. */\n    IndexElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit IndexElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~IndexElement();\n\n    void clear();\n\n    /** Sets the number of entries in the DB. */\n    virtual void setNumEntries(unsigned n);\n    /** Sets the given index entry. */\n    virtual void setIndexEntry(unsigned n, unsigned id, unsigned index);\n  };\n\n  /** Represents an entry within the call-sign database.\n   * The call-sign DB entries must be ordered by their DMR IDs.\n   *\n   * Memory layout of encoded call-sign/user database index entry:\n   * @verbinclude tytcallsigndbentry.txt\n   */\n  class EntryElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EntryElement(uint8_t *ptr, size_t size);\n  public:\n    /** Constructor. */\n    explicit EntryElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~EntryElement();\n\n    void clear();\n\n    /** Encodes the given user. */\n    virtual void set(const UserDatabase::User &user);\n  };\n\nprotected:\n  /** Hidden constructor. Use one of the device specific call-sign DB classes. */\n  explicit TyTCallsignDB(QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~TyTCallsignDB();\n\n  bool encode(UserDatabase *db, const Flags &selection,const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Allocates required space for index and @c n call-signs. */\n  virtual void allocate(unsigned n);\n\n  /** Clears the call-sign DB index. */\n  virtual void clearIndex();\n  /** Sets the number of entries in the DB. */\n  virtual void setNumEntries(unsigned n);\n  /** Sets the given index entry. */\n  virtual void setIndexEntry(unsigned n, unsigned id, unsigned index);\n  /** Sets a given call-sign entry. */\n  virtual void setEntry(unsigned n, const UserDatabase::User &user);\n};\n\n#endif // TYTCALLSIGNDB_HH\n"
  },
  {
    "path": "lib/tyt_codeplug.cc",
    "content": "#include \"tyt_codeplug.hh\"\n#include \"config.hh\"\n#include \"utils.hh\"\n#include \"channel.hh\"\n#include \"gpssystem.hh\"\n#include \"config.h\"\n#include \"logger.hh\"\n#include \"tyt_extensions.hh\"\n#include \"encryptionextension.hh\"\n#include \"commercial_extension.hh\"\n#include \"intermediaterepresentation.hh\"\n\n#include <QTimeZone>\n#include <QtEndian>\n#include <QChar>\n\n#define SETTINGS_SIZE     0x000090\n#define MENUSETTINGS_SIZE 0x000010\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::ChannelElement\n * ******************************************************************************************** */\nTyTCodeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, size())\n{\n  // pass...\n}\n\nTyTCodeplug::ChannelElement::~ChannelElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::ChannelElement::isValid() const {\n  return Element::isValid() && QChar::isPrint(getUInt16_le(Offset::name()));\n}\n\nvoid\nTyTCodeplug::ChannelElement::clear() {\n  Element::clear();\n\n  setMode(MODE_ANALOG);\n  setBandwidth(FMChannel::Bandwidth::Narrow);\n  enableAutoScan(0);\n  setBit(0, 1); setBit(0,2);\n  enableLoneWorker(false);\n  enableTalkaround(false);\n  enableRXOnly(false);\n  setTimeSlot(DMRChannel::TimeSlot::TS1);\n  setColorCode(1);\n  setPrivacyIndex(0);\n  setPrivacyType(PRIV_NONE);\n  enablePrivateCallConfirm(false);\n  enableDataCallConfirm(false);\n  setRXRefFrequency(TyTChannelExtension::RefFrequency::Low);\n  clearBit(3,2);\n  enableEmergencyAlarmACK(false);\n  clearBit(3,4); setBit(3,5); setBit(3,6);\n  enableDisplayPTTId(true);\n  setTXRefFrequency(TyTChannelExtension::RefFrequency::Low);\n  setBit(4,2); clearBit(4,3);\n  enableVOX(false);\n  setBit(4,5);\n  setAdmitCriterion(ADMIT_ALWAYS);\n  clearBit(5,0);\n  setContactIndex(0);\n  resetTXTimeOut();\n  clearBit(8,6);\n  setTXTimeOutRekeyDelay(0);\n  setEmergencySystemIndex(0);\n  setScanListIndex(0);\n  setGroupListIndex(0);\n  setPositioningSystemIndex(0);\n  for (uint8_t i=0; i<8; i++)\n    setDTMFDecode(i, false);\n  setRXFrequency(Frequency::fromMHz(400));\n  setTXFrequency(Frequency::fromMHz(400));\n  setRXSignaling(SelectiveCall());\n  setTXSignaling(SelectiveCall());\n  setRXSignalingSystemIndex(0);\n  setTXSignalingSystemIndex(0);\n  setBit(30,2); setBit(30,3); setBit(30,4); setBit(30,5); setBit(30,6); setBit(30,7);\n  enableTXGPSInfo(true);\n  enableRXGPSInfo(true);\n  setBit(31, 5); setBit(31, 6); setBit(31, 7);\n  memset((_data+32), 0x00, sizeof(32));\n}\n\n\nTyTCodeplug::ChannelElement::Mode\nTyTCodeplug::ChannelElement::mode() const {\n  return TyTCodeplug::ChannelElement::Mode(getUInt2(Offset::mode()));\n}\nvoid\nTyTCodeplug::ChannelElement::setMode(Mode mode) {\n  setUInt2(Offset::mode(), uint8_t(mode));\n}\n\nFMChannel::Bandwidth\nTyTCodeplug::ChannelElement::bandwidth() const {\n  if (0 == getUInt2(Offset::bandwidth()))\n    return FMChannel::Bandwidth::Narrow;\n  return FMChannel::Bandwidth::Wide;\n}\nvoid\nTyTCodeplug::ChannelElement::setBandwidth(FMChannel::Bandwidth bw) {\n  if (FMChannel::Bandwidth::Narrow == bw)\n    setUInt2(Offset::bandwidth(), BW_12_5_KHZ);\n  else\n    setUInt2(Offset::bandwidth(), BW_25_KHZ);\n}\n\nbool\nTyTCodeplug::ChannelElement::autoScan() const {\n  return getBit(Offset::autoscan());\n}\nvoid\nTyTCodeplug::ChannelElement::enableAutoScan(bool enable) {\n  setBit(Offset::autoscan(), enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::loneWorker() const {\n  return getBit(Offset::loneworker());\n}\nvoid\nTyTCodeplug::ChannelElement::enableLoneWorker(bool enable) {\n  setBit(Offset::loneworker(), enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::talkaround() const {\n  return ! getBit(Offset::talkaround());\n}\nvoid\nTyTCodeplug::ChannelElement::enableTalkaround(bool enable) {\n  setBit(Offset::talkaround(), !enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::rxOnly() const {\n  return getBit(Offset::rxonly());\n}\nvoid\nTyTCodeplug::ChannelElement::enableRXOnly(bool enable) {\n  setBit(Offset::rxonly(), enable);\n}\n\nDMRChannel::TimeSlot\nTyTCodeplug::ChannelElement::timeSlot() const {\n  if (2 == getUInt2(Offset::timeslot()))\n    return DMRChannel::TimeSlot::TS2;\n  return DMRChannel::TimeSlot::TS1;\n}\nvoid\nTyTCodeplug::ChannelElement::setTimeSlot(DMRChannel::TimeSlot ts) {\n  if (DMRChannel::TimeSlot::TS1 == ts)\n    setUInt2(Offset::timeslot(), 1);\n  else\n    setUInt2(Offset::timeslot(),2);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::colorCode() const {\n  return getUInt4(Offset::colorcode());\n}\nvoid\nTyTCodeplug::ChannelElement::setColorCode(uint8_t cc) {\n  cc = std::min(uint8_t(16), cc);\n  setUInt4(Offset::colorcode(), cc);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::privacyIndex() const {\n  return getUInt4(Offset::privacyIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setPrivacyIndex(uint8_t idx) {\n  setUInt4(Offset::privacyIndex(), idx);\n}\n\nTyTCodeplug::ChannelElement::PrivacyType\nTyTCodeplug::ChannelElement::privacyType() const {\n  return TyTCodeplug::ChannelElement::PrivacyType(getUInt2(Offset::privacyType()));\n}\nvoid\nTyTCodeplug::ChannelElement::setPrivacyType(TyTCodeplug::ChannelElement::PrivacyType type) {\n  setUInt2(Offset::privacyType(), uint8_t(type));\n}\n\nbool\nTyTCodeplug::ChannelElement::privateCallConfirm() const {\n  return getBit(Offset::privateCallConfirm());\n}\nvoid\nTyTCodeplug::ChannelElement::enablePrivateCallConfirm(bool enable) {\n  setBit(Offset::privateCallConfirm(), enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::dataCallConfirm() const {\n  return getBit(Offset::dataCallConfirm());\n}\nvoid\nTyTCodeplug::ChannelElement::enableDataCallConfirm(bool enable) {\n  setBit(Offset::dataCallConfirm(), enable);\n}\n\nTyTChannelExtension::RefFrequency\nTyTCodeplug::ChannelElement::rxRefFrequency() const {\n  return TyTChannelExtension::RefFrequency(getUInt2(Offset::rxRefFrequency()));\n}\nvoid\nTyTCodeplug::ChannelElement::setRXRefFrequency(TyTChannelExtension::RefFrequency ref) {\n  setUInt2(Offset::rxRefFrequency(), uint8_t(ref));\n}\n\nbool\nTyTCodeplug::ChannelElement::emergencyAlarmACK() const {\n  return getBit(Offset::emergencyAlarmACK());\n}\nvoid\nTyTCodeplug::ChannelElement::enableEmergencyAlarmACK(bool enable) {\n  setBit(Offset::emergencyAlarmACK(), enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::displayPTTId() const {\n  return ! getBit(Offset::displayPTTId());\n}\nvoid\nTyTCodeplug::ChannelElement::enableDisplayPTTId(bool enable) {\n  setBit(Offset::displayPTTId(), !enable);\n}\n\nTyTChannelExtension::RefFrequency\nTyTCodeplug::ChannelElement::txRefFrequency() const {\n  return TyTChannelExtension::RefFrequency(getUInt2(Offset::txRefFrequency()));\n}\nvoid\nTyTCodeplug::ChannelElement::setTXRefFrequency(TyTChannelExtension::RefFrequency ref) {\n  setUInt2(Offset::txRefFrequency(), uint8_t(ref));\n}\n\nbool\nTyTCodeplug::ChannelElement::vox() const {\n  return getBit(Offset::vox());\n}\nvoid\nTyTCodeplug::ChannelElement::enableVOX(bool enable) {\n  setBit(Offset::vox(), enable);\n}\n\nTyTCodeplug::ChannelElement::Admit\nTyTCodeplug::ChannelElement::admitCriterion() const {\n  return TyTCodeplug::ChannelElement::Admit(getUInt2(Offset::admitCriterion()));\n}\nvoid\nTyTCodeplug::ChannelElement::setAdmitCriterion(TyTCodeplug::ChannelElement::Admit admit) {\n  setUInt2(Offset::admitCriterion(), uint8_t(admit));\n}\n\nuint16_t\nTyTCodeplug::ChannelElement::contactIndex() const {\n  return getUInt16_le(Offset::contactIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setContactIndex(uint16_t idx) {\n  setUInt16_le(Offset::contactIndex(), idx);\n}\n\nbool\nTyTCodeplug::ChannelElement::txTimeOutDisabled() const {\n  return 0 == getUInt6(Offset::txTimeOut());\n}\nInterval\nTyTCodeplug::ChannelElement::txTimeOut() const {\n  return Interval::fromSeconds(getUInt6(Offset::txTimeOut())*15);\n}\nvoid\nTyTCodeplug::ChannelElement::setTXTimeOut(const Interval &tot) {\n  return setUInt6(Offset::txTimeOut(), tot.seconds()/15);\n}\nvoid\nTyTCodeplug::ChannelElement::resetTXTimeOut() {\n  setUInt6(Offset::txTimeOut(), 0);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::txTimeOutRekeyDelay() const {\n  return getUInt8(Offset::txTimeOutRekeyDelay());\n}\nvoid\nTyTCodeplug::ChannelElement::setTXTimeOutRekeyDelay(uint8_t delay) {\n  return setUInt8(Offset::txTimeOutRekeyDelay(), delay);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::emergencySystemIndex() const {\n  return getUInt8(Offset::emergencySystemIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setEmergencySystemIndex(uint8_t delay) {\n  return setUInt8(Offset::emergencySystemIndex(), delay);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::scanListIndex() const {\n  return getUInt8(Offset::scanListIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setScanListIndex(uint8_t idx) {\n  return setUInt8(Offset::scanListIndex(), idx);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::groupListIndex() const {\n  return getUInt8(Offset::groupListIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setGroupListIndex(uint8_t idx) {\n  return setUInt8(Offset::groupListIndex(), idx);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::positioningSystemIndex() const {\n  return getUInt8(Offset::positioningSystemIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setPositioningSystemIndex(uint8_t idx) {\n  return setUInt8(Offset::positioningSystemIndex(), idx);\n}\n\nbool\nTyTCodeplug::ChannelElement::dtmfDecode(uint8_t idx) const {\n  return getBit(Offset::dtmfDecode(), idx);\n}\nvoid\nTyTCodeplug::ChannelElement::setDTMFDecode(uint8_t idx, bool enable) {\n  setBit(Offset::dtmfDecode(), idx, enable);\n}\n\nFrequency\nTyTCodeplug::ChannelElement::rxFrequency() const {\n  return Frequency::fromHz(getBCD8_le(Offset::rxFrequency())*10);\n}\nvoid\nTyTCodeplug::ChannelElement::setRXFrequency(const Frequency &freq_Hz) {\n  return setBCD8_le(Offset::rxFrequency(), freq_Hz.inHz()/10);\n}\n\nFrequency\nTyTCodeplug::ChannelElement::txFrequency() const {\n  return Frequency::fromHz(getBCD8_le(Offset::txFrequency())*10);\n}\nvoid\nTyTCodeplug::ChannelElement::setTXFrequency(const Frequency &freq_Hz) {\n  return setBCD8_le(Offset::txFrequency(), freq_Hz.inHz()/10);\n}\n\nSelectiveCall\nTyTCodeplug::ChannelElement::rxSignaling() const {\n  return decode_ctcss_tone_table(getUInt16_le(24));\n}\nvoid\nTyTCodeplug::ChannelElement::setRXSignaling(const SelectiveCall &code) {\n  setUInt16_le(24, encode_ctcss_tone_table(code));\n}\n\nSelectiveCall\nTyTCodeplug::ChannelElement::txSignaling() const {\n  return decode_ctcss_tone_table(getUInt16_le(26));\n}\nvoid\nTyTCodeplug::ChannelElement::setTXSignaling(const SelectiveCall &code) {\n  setUInt16_le(26, encode_ctcss_tone_table(code));\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::rxSignalingSystemIndex() const {\n  return getUInt8(Offset::rxSignalingSystemIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setRXSignalingSystemIndex(uint8_t idx) {\n  setUInt8(Offset::rxSignalingSystemIndex(), idx);\n}\n\nuint8_t\nTyTCodeplug::ChannelElement::txSignalingSystemIndex() const {\n  return getUInt8(Offset::txSignalingSystemIndex());\n}\nvoid\nTyTCodeplug::ChannelElement::setTXSignalingSystemIndex(uint8_t idx) {\n  setUInt8(Offset::txSignalingSystemIndex(), idx);\n}\n\nbool\nTyTCodeplug::ChannelElement::txGPSInfo() const {\n  return ! getBit(Offset::txGPSInfo());\n}\nvoid\nTyTCodeplug::ChannelElement::enableTXGPSInfo(bool enable) {\n  setBit(Offset::txGPSInfo(), !enable);\n}\n\nbool\nTyTCodeplug::ChannelElement::rxGPSInfo() const {\n  return !getBit(Offset::rxGPSInfo());\n}\nvoid\nTyTCodeplug::ChannelElement::enableRXGPSInfo(bool enable) {\n  setBit(Offset::rxGPSInfo(), !enable);\n}\n\nQString\nTyTCodeplug::ChannelElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nTyTCodeplug::ChannelElement::setName(const QString &name) {\n  return writeUnicode(Offset::name(), name, Limit::nameLength(), 0x0000);\n}\n\nChannel *\nTyTCodeplug::ChannelElement::toChannelObj(const ErrorStack &err) const {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid channel.\";\n    return nullptr;\n  }\n\n  Channel *ch = nullptr;\n  TyTChannelExtension *ex = new TyTChannelExtension();\n\n  // decode power setting\n  if (MODE_ANALOG == mode()) {\n    FMChannel::Admit admit_crit;\n    switch(admitCriterion()) {\n      case ADMIT_ALWAYS: admit_crit = FMChannel::Admit::Always; break;\n      case ADMIT_TONE: admit_crit = FMChannel::Admit::Tone; break;\n      case ADMIT_CH_FREE: admit_crit = FMChannel::Admit::Free; break;\n      default: admit_crit = FMChannel::Admit::Free; break;\n    }\n    FMChannel *ach = new FMChannel();\n    ach->setAdmit(admit_crit);\n    ach->setSquelchDefault();\n    ach->setRXTone(rxSignaling());\n    ach->setTXTone(txSignaling());\n    ach->setBandwidth(bandwidth());\n    ach->extended()->enableTalkaround(talkaround());\n    // Apply analog channel extension settings\n    ex->enableDisplayPTTId(displayPTTId());\n    ch = ach;\n  } else if (MODE_DIGITAL == mode()) {\n    DMRChannel::Admit admit_crit;\n    switch(admitCriterion()) {\n      case ADMIT_ALWAYS: admit_crit = DMRChannel::Admit::Always; break;\n      case ADMIT_CH_FREE: admit_crit = DMRChannel::Admit::Free; break;\n      case ADMIT_COLOR: admit_crit = DMRChannel::Admit::ColorCode; break;\n      default: admit_crit = DMRChannel::Admit::Free; break;\n    }\n    DMRChannel *dch = new DMRChannel();\n    dch->setAdmit(admit_crit);\n    dch->setColorCode(colorCode());\n    dch->setTimeSlot(timeSlot());\n    dch->extended()->enablePrivateCallConfirm(privateCallConfirm());\n    dch->extended()->enableDataConfirm(dataCallConfirm());\n    dch->extended()->enableLoneWorker(loneWorker());\n    dch->extended()->enableTalkaround(talkaround());\n\n    // Apply digital channel extension settings\n    ex->enableEmergencyAlarmConfirmed(emergencyAlarmACK());\n    // If encryption is enabled, Add commercial extension to channel if needed\n    // the key will be linked later\n    if ((PRIV_NONE != privacyType()) && (nullptr == dch->commercialExtension()))\n      dch->setCommercialExtension(new CommercialChannelExtension());\n\n    // done\n    ch = dch;\n  } else {\n    errMsg(err) << \"Cannot decode channel. Channel type \" << mode() << \" unknown!\";\n    delete ex;\n    return nullptr;\n  }\n\n  // Common settings\n  ch->setName(name());\n  ch->setRXFrequency(rxFrequency());\n  ch->setTXFrequency(txFrequency());\n  ch->setTimeout(txTimeOut());\n  ch->setRXOnly(rxOnly());\n  // Power setting must be overridden by specialized class\n  ch->setDefaultPower();\n  if (vox())\n    ch->setVOXDefault();\n  else\n    ch->disableVOX();\n\n  // Apply common channel settings\n  ex->enableAutoScan(autoScan());\n  ex->setRXRefFrequency(rxRefFrequency());\n  ex->setTXRefFrequency(txRefFrequency());\n  ch->setTyTChannelExtension(ex);\n\n  return ch;\n}\n\nbool\nTyTCodeplug::ChannelElement::linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err) const\n{\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link an invalid channel.\";\n    return false;\n  }\n\n  if (scanListIndex() && ctx.has<ScanList>(scanListIndex())) {\n    c->setScanList(ctx.get<ScanList>(scanListIndex()));\n  }\n\n  if (MODE_ANALOG == mode()) {\n    // Nothing further to link for analog channels.\n    return true;\n  } else if ((MODE_DIGITAL == mode()) && (c->is<DMRChannel>())){\n    DMRChannel *dc = c->as<DMRChannel>();\n    if (contactIndex() && ctx.has<DMRContact>(contactIndex())) {\n      dc->setContact(ctx.get<DMRContact>(contactIndex()));\n    }\n    if (groupListIndex() && ctx.has<RXGroupList>(groupListIndex())) {\n      dc->setGroupList(ctx.get<RXGroupList>(groupListIndex()));\n    }\n    if (positioningSystemIndex() && ctx.has<DMRAPRSSystem>(positioningSystemIndex())) {\n      dc->setAPRS(ctx.get<DMRAPRSSystem>(positioningSystemIndex()));\n    }\n\n    // Link encryption key if defined\n    if (PRIV_NONE != privacyType()) {\n      if (nullptr == dc->commercialExtension()) {\n        errMsg(err) << \"Cannot link encryption key: No commercial extension set.\";\n        return false;\n      }\n      if (PRIV_BASIC == privacyType()) {\n        if (! ctx.has<BasicEncryptionKey>(privacyIndex())) {\n          errMsg(err) << \"Cannot link encryption key: No basic key with index \" << privacyIndex()\n                      << \" defined.\";\n          return false;\n        }\n        dc->commercialExtension()->setEncryptionKey(ctx.get<BasicEncryptionKey>(privacyIndex()));\n      } else if (PRIV_ENHANCED == privacyType()) {\n        if (! ctx.has<AESEncryptionKey>(privacyIndex())) {\n          errMsg(err) << \"Cannot link encryption key: No AES (enhances) key with index \"\n                      << privacyIndex() << \" defined.\";\n          return false;\n        }\n        dc->commercialExtension()->setEncryptionKey(ctx.get<AESEncryptionKey>(privacyIndex()));\n      } else {\n        errMsg(err) << \"Unknown encryption key type \" << privacyType() << \".\";\n        return false;\n      }\n    }\n\n    return true;\n  }\n\n  errMsg(err) << \"Cannot link channel '\" << c->name()\n              << \"' invalid channel type \" << mode() << \".\";\n\n  return false;\n}\n\nvoid\nTyTCodeplug::ChannelElement::fromChannelObj(const Channel *chan, Context &ctx) {\n  setName(chan->name());\n  setRXFrequency(chan->rxFrequency());\n  setTXFrequency(chan->txFrequency());\n  enableRXOnly(chan->rxOnly());\n  if (chan->defaultTimeout())\n    setTXTimeOut(ctx.config()->settings()->tot());\n  else\n    setTXTimeOut(chan->timeout());\n  if (chan->scanList())\n    setScanListIndex(ctx.index(chan->scanList()));\n  else\n    setScanListIndex(0);\n  // Enable vox\n  bool defaultVOXEnabled = (chan->defaultVOX() && ctx.config()->settings()->audio()->voxEnabled());\n  bool channelVOXEnabled = (! (chan->voxDisabled()||chan->defaultVOX()));\n  enableVOX(defaultVOXEnabled || channelVOXEnabled);\n  // power setting must be set by specialized element\n\n  if (chan->is<const DMRChannel>()) {\n    const DMRChannel *dchan = chan->as<const DMRChannel>();\n    setMode(MODE_DIGITAL);\n    switch (dchan->admit()) {\n    case DMRChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break;\n    case DMRChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break;\n    case DMRChannel::Admit::ColorCode: setAdmitCriterion(ADMIT_COLOR); break;\n    }\n    setColorCode(dchan->colorCode());\n    setTimeSlot(dchan->timeSlot());\n    if (dchan->groupList())\n      setGroupListIndex(ctx.index(dchan->groupList()));\n    else\n      setGroupListIndex(0);\n    if (dchan->contact())\n      setContactIndex(ctx.index(dchan->contact()));\n    setBandwidth(FMChannel::Bandwidth::Narrow);\n    setRXSignaling(SelectiveCall());\n    setTXSignaling(SelectiveCall());\n    enablePrivateCallConfirm(dchan->extended()->privateCallConfirm());\n    enableDataCallConfirm(dchan->extended()->dataConfirm());\n    enableLoneWorker(dchan->extended()->loneWorker());\n    enableTalkaround(dchan->extended()->talkaround());\n\n    if (dchan->aprs() && dchan->aprs()->is<DMRAPRSSystem>()) {\n      setPositioningSystemIndex(ctx.index(dchan->aprs()->as<DMRAPRSSystem>()));\n      enableTXGPSInfo(true); enableRXGPSInfo(false);\n    }\n    if (chan->tytChannelExtension()) {\n      enableEmergencyAlarmACK(chan->tytChannelExtension()->emergencyAlarmConfirmed());\n    }\n    // Link encryption key if set\n    if (dchan->commercialExtension() && dchan->commercialExtension()->encryptionKey()) {\n      // Check for index\n      if (0 > ctx.index(dchan->commercialExtension()->encryptionKey())) {\n        logError() << \"Cannot encode encryption key '\"\n                   << dchan->commercialExtension()->encryptionKey()->name()\n                   << \"': Not indexed.\";\n      } else if (dchan->commercialExtension()->encryptionKey()->is<BasicEncryptionKey>()) {\n        setPrivacyType(PRIV_BASIC);\n        setPrivacyIndex(ctx.index(dchan->commercialExtension()->encryptionKey()));\n      } else if (dchan->commercialExtension()->encryptionKey()->is<AESEncryptionKey>()) {\n        setPrivacyType(PRIV_ENHANCED);\n        setPrivacyIndex(ctx.index(dchan->commercialExtension()->encryptionKey()));\n      } else {\n        logInfo() << \"Ignore unknown encryption key type \"\n                  << dchan->commercialExtension()->encryptionKey()->metaObject()->className()\n                  << \" for DMR channel.\";\n      }\n    }\n  } else if (chan->is<FMChannel>()) {\n    const FMChannel *achan = chan->as<const FMChannel>();\n    setMode(MODE_ANALOG);\n    setBandwidth(achan->bandwidth());\n    // Squelch must be set by specialized element\n    switch (achan->admit()) {\n    case FMChannel::Admit::Always: setAdmitCriterion(ADMIT_ALWAYS); break;\n    case FMChannel::Admit::Free: setAdmitCriterion(ADMIT_CH_FREE); break;\n    case FMChannel::Admit::Tone: setAdmitCriterion(ADMIT_TONE); break;\n    }\n    setRXSignaling(achan->rxTone());\n    setTXSignaling(achan->txTone());\n    setGroupListIndex(0);\n    setContactIndex(0);\n    enableTalkaround(achan->extended()->talkaround());\n\n    if (chan->tytChannelExtension()) {\n      enableDisplayPTTId(chan->tytChannelExtension()->displayPTTId());\n    }\n  }\n\n  // Apply channel extension (if set)\n  if (chan->tytChannelExtension()) {\n    enableAutoScan(chan->tytChannelExtension()->autoScan());\n    setRXRefFrequency(chan->tytChannelExtension()->rxRefFrequency());\n    setTXRefFrequency(chan->tytChannelExtension()->txRefFrequency());\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::ContactElement\n * ******************************************************************************************** */\nTyTCodeplug::ContactElement::ContactElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::ContactElement::ContactElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, size())\n{\n  // pass...\n}\n\nTyTCodeplug::ContactElement::~ContactElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::ContactElement::isValid() const {\n  return Element::isValid() && (0 != getUInt2(Offset::callType()))\n      && (0x0000 != getUInt16_be(Offset::name())) && (0xffff != getUInt16_be(Offset::name()));\n}\n\nvoid\nTyTCodeplug::ContactElement::clear() {\n  memset(_data, 0xff, 3); // clear DMR ID\n  setUInt2(Offset::callType(), 0);      // type=0\n  setBit({3,2}, 0); setBit({3,3}, 0); setBit({3,4}, 0); // unused = 0\n  enableRingTone(false);\n  setBit({3,6}, 1); setBit({3,7}, 1); // unknown = 1\n  memset(_data+Offset::name(), 0x00, 2*Limit::nameLength());\n}\n\nuint32_t\nTyTCodeplug::ContactElement::dmrId() const {\n  return getUInt24_le(Offset::dmrId());\n}\n\nvoid\nTyTCodeplug::ContactElement::setDMRId(uint32_t id) {\n  setUInt24_le(Offset::dmrId(), id);\n}\n\nbool\nTyTCodeplug::ContactElement::ringTone() const {\n  return getBit(Offset::ringTone());\n}\nvoid\nTyTCodeplug::ContactElement::enableRingTone(bool enable) {\n  setBit(Offset::ringTone(), enable);\n}\n\nDMRContact::Type TyTCodeplug::ContactElement::callType() const {\n  switch((CallType)getUInt2(Offset::callType())) {\n  case CallType::GroupCall: return DMRContact::GroupCall;\n  case CallType::PrivateCall: return DMRContact::PrivateCall;\n  case CallType::AllCall: return DMRContact::AllCall;\n  default:\n    break;\n  }\n  return DMRContact::PrivateCall;\n}\nvoid\nTyTCodeplug::ContactElement::setCallType(DMRContact::Type type) {\n  switch (type) {\n  case DMRContact::GroupCall:\n    setUInt2(Offset::callType(), (unsigned int)CallType::GroupCall);\n    break;\n  case DMRContact::PrivateCall:\n    setUInt2(Offset::callType(), (unsigned int)CallType::PrivateCall);\n    break;\n  case DMRContact::AllCall:\n    setUInt2(Offset::callType(), (unsigned int)CallType::AllCall);\n    break;\n  }\n}\n\nQString\nTyTCodeplug::ContactElement::name() const {\n  return readUnicode(Offset::name(), Limit::nameLength(), 0x0000);\n}\nvoid\nTyTCodeplug::ContactElement::setName(const QString &nm) {\n  writeUnicode(Offset::name(), nm, Limit::nameLength(), 0x0000);\n}\n\nDMRContact *\nTyTCodeplug::ContactElement::toContactObj() const {\n  return new DMRContact(callType(), name(), dmrId(), ringTone());\n}\n\nbool\nTyTCodeplug::ContactElement::fromContactObj(const DMRContact *cont) {\n  if (nullptr == cont)\n    return false;\n\n  setDMRId(cont->number());\n  setName(cont->name());\n  setCallType(cont->type());\n  enableRingTone(cont->ring());\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::ZoneElement\n * ******************************************************************************************** */\nTyTCodeplug::ZoneElement::ZoneElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::ZoneElement::ZoneElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0040)\n{\n  // pass...\n}\n\nTyTCodeplug::ZoneElement::~ZoneElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::ZoneElement::isValid() const {\n  return Element::isValid() && (0x0000 != getUInt16_be(0)) && (0xffff != getUInt16_be(0));\n}\n\nvoid\nTyTCodeplug::ZoneElement::clear() {\n  memset(_data, 0x00, 0x40);\n}\n\nQString\nTyTCodeplug::ZoneElement::name() const {\n  return readUnicode(0, 16);\n}\nvoid\nTyTCodeplug::ZoneElement::setName(const QString &name) {\n  writeUnicode(0, name, 16);\n}\n\nbool\nTyTCodeplug::ZoneElement::hasMemberIndex(unsigned n) const {\n  return 0 != memberIndex(n);\n}\nuint16_t\nTyTCodeplug::ZoneElement::memberIndex(unsigned n) const {\n  return getUInt16_le(0x20 + n*sizeof(uint16_t));\n}\nvoid\nTyTCodeplug::ZoneElement::setMemberIndex(unsigned n, uint16_t idx) {\n  setUInt16_le(0x20 + n*sizeof(uint16_t), idx);\n}\n\nbool\nTyTCodeplug::ZoneElement::fromZoneObj(const Zone *zone, Context &ctx) {\n  setName(zone->name());\n  for (int i=0; i<16; i++) {\n    if (i < zone->A()->count()) {\n      Channel *ch = zone->A()->get(i)->as<Channel>();\n      int idx = ctx.index(ch);\n      setMemberIndex(i, idx);\n    } else\n      setMemberIndex(i, 0);\n  }\n  return true;\n}\n\nZone *\nTyTCodeplug::ZoneElement::toZoneObj() const {\n  if (!isValid())\n    return nullptr;\n  return new Zone(name());\n}\n\nbool\nTyTCodeplug::ZoneElement::linkZone(Zone *zone, Context &ctx) const {\n  if (! isValid())\n    return false;\n\n  for (int i=0; ((i<16) && hasMemberIndex(i)); i++) {\n    if (! ctx.has<Channel>(memberIndex(i))) {\n      logWarn() << \"Cannot link channel with index \" << memberIndex(i)\n                << \" to zone '\" << zone->name() << \"': channel not defined.\";\n      continue;\n    }\n    zone->A()->add(ctx.get<Channel>(memberIndex(i)));\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::GroupListElement\n * ******************************************************************************************** */\nTyTCodeplug::GroupListElement::GroupListElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::GroupListElement::GroupListElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0060)\n{\n  // pass...\n}\n\nTyTCodeplug::GroupListElement::~GroupListElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::GroupListElement::isValid() const {\n  return Element::isValid() && (0x0000 != getUInt16_be(0));\n}\n\nvoid\nTyTCodeplug::GroupListElement::clear() {\n  memset(_data, 0x00, 0x60);\n}\n\nQString\nTyTCodeplug::GroupListElement::name() const {\n  return readUnicode(0, 16);\n}\n\nvoid\nTyTCodeplug::GroupListElement::setName(const QString &nm) {\n  writeUnicode(0, nm, 16);\n}\n\nuint16_t\nTyTCodeplug::GroupListElement::memberIndex(unsigned n) const {\n  return getUInt16_le(0x20 + n*2);\n}\n\nvoid\nTyTCodeplug::GroupListElement::setMemberIndex(unsigned n, uint16_t idx) {\n  setUInt16_le(0x20 + 2*n, idx);\n}\n\nbool\nTyTCodeplug::GroupListElement::fromGroupListObj(const RXGroupList *lst, Context &ctx) {\n  setName(lst->name());\n\n  int j=0;\n  // Iterate over all 32 entries in the codeplug\n  for (int i=0; i<32; i++) {\n    // Skip non-private-call entries\n    while((lst->count() > j) && (DMRContact::GroupCall != lst->contact(j)->type())) {\n      logWarn() << \"Contact '\" << lst->contact(i)->name() << \"' in group list '\" << lst->name()\n                << \"' is not a group call. Skip entry.\";\n      j++;\n    }\n    if (lst->count() > j) {\n      setMemberIndex(i, ctx.index(lst->contact(j)));\n      j++;\n    } else {\n      // clear entry\n      setMemberIndex(i, 0);\n    }\n  }\n  return true;\n}\n\nRXGroupList *\nTyTCodeplug::GroupListElement::toGroupListObj(Context &ctx) {\n  Q_UNUSED(ctx)\n  if (! isValid())\n    return nullptr;\n  return new RXGroupList(name());\n}\n\nbool\nTyTCodeplug::GroupListElement::linkGroupListObj(RXGroupList *lst, Context &ctx) {\n  if (! isValid())\n    return false;\n\n  for (int i=0; (i<32) && memberIndex(i); i++) {\n    if (! ctx.has<DMRContact>(memberIndex(i))) {\n      logWarn() << \"Cannot link contact \" << memberIndex(i) << \" to group list '\"\n                << name() << \"': Invalid contact index. Ignored.\";\n      continue;\n    }\n    //logDebug() << \"Add contact idx=\" << memberIndex(i) << \" to group list \" << lst->name() << \".\";\n    if (0 > lst->addContact(ctx.get<DMRContact>(memberIndex(i)))) {\n      logWarn() << \"Cannot add contact '\" << ctx.get<DMRContact>(memberIndex(i))->name()\n                << \"' at idx=\" << memberIndex(i) << \".\";\n      continue;\n    }\n  }\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::ScanListElement\n * ******************************************************************************************** */\nTyTCodeplug::ScanListElement::ScanListElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::ScanListElement::ScanListElement(uint8_t *ptr)\n  : Element(ptr, 0x0068)\n{\n  // pass...\n}\n\nTyTCodeplug::ScanListElement::~ScanListElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::ScanListElement::isValid() const {\n  return Element::isValid() && (0x0000 != getUInt16_be(0));\n}\n\nvoid\nTyTCodeplug::ScanListElement::clear() {\n  memset(_data, 0, 2*16);\n  setPriorityChannel1Index(0xffff);\n  setPriorityChannel2Index(0xffff);\n  setTXChannelIndex(0xffff);\n  setUInt8(0x26, 0xf1);\n  setHoldTime(500);\n  setPrioritySampleTime(2000);\n  setUInt8(0x29, 0xff);\n  memset(_data+0x2a, 0, 2*31);\n}\n\nQString\nTyTCodeplug::ScanListElement::name() const {\n  return readUnicode(0, 16);\n}\n\nvoid\nTyTCodeplug::ScanListElement::setName(const QString &name) {\n  writeUnicode(0, name, 16);\n}\n\nuint16_t\nTyTCodeplug::ScanListElement::priorityChannel1Index() const {\n  return getUInt16_le(0x20);\n}\n\nvoid\nTyTCodeplug::ScanListElement::setPriorityChannel1Index(uint16_t idx) {\n  setUInt16_le(0x20, idx);\n}\n\nuint16_t\nTyTCodeplug::ScanListElement::priorityChannel2Index() const {\n  return getUInt16_le(0x22);\n}\n\nvoid\nTyTCodeplug::ScanListElement::setPriorityChannel2Index(uint16_t idx) {\n  setUInt16_le(0x22, idx);\n}\n\nuint16_t\nTyTCodeplug::ScanListElement::txChannelIndex() const {\n  return getUInt16_le(0x24);\n}\n\nvoid\nTyTCodeplug::ScanListElement::setTXChannelIndex(uint16_t idx) {\n  setUInt16_le(0x24, idx);\n}\n\nunsigned\nTyTCodeplug::ScanListElement::holdTime() const {\n  return unsigned(getUInt8(0x27))*25;\n}\n\nvoid\nTyTCodeplug::ScanListElement::setHoldTime(unsigned time) {\n  setUInt8(0x27, time/25);\n}\n\nunsigned\nTyTCodeplug::ScanListElement::prioritySampleTime() const {\n  return unsigned(getUInt8(0x28))*250;\n}\n\nvoid\nTyTCodeplug::ScanListElement::setPrioritySampleTime(unsigned time) {\n  setUInt8(0x28, time/250);\n}\n\nuint16_t\nTyTCodeplug::ScanListElement::memberIndex(unsigned n) const {\n  return getUInt16_le(0x2a + 2*n);\n}\n\nvoid\nTyTCodeplug::ScanListElement::setMemberIndex(unsigned n, uint16_t idx) {\n  setUInt16_le(0x2a + 2*n, idx);\n}\n\nbool\nTyTCodeplug::ScanListElement::fromScanListObj(const ScanList *lst, Context &ctx) {\n  // Set name\n  setName(lst->name());\n\n  // Set priority channel 1\n  if (lst->primaryChannel() && (SelectedChannel::get() == lst->primaryChannel()))\n    setPriorityChannel1Index(0);\n  else if (lst->primaryChannel())\n    setPriorityChannel1Index(ctx.index(lst->primaryChannel()));\n  else\n    setPriorityChannel1Index(0xffff);\n\n  // Set priority channel 2\n  if (lst->secondaryChannel() && (SelectedChannel::get() == lst->secondaryChannel()))\n    setPriorityChannel2Index(0);\n  else if (lst->secondaryChannel())\n    setPriorityChannel2Index(ctx.index(lst->secondaryChannel()));\n  else\n    setPriorityChannel2Index(0xffff);\n\n  // Set transmit channel\n  if (lst->revertChannel() && (SelectedChannel::get() == lst->revertChannel()))\n    setTXChannelIndex(0);\n  else if (lst->revertChannel())\n    setTXChannelIndex(ctx.index(lst->revertChannel()));\n  else\n    setTXChannelIndex(0xffff);\n\n  for (int i=0, j=0; i<31;) {\n    if (j >= lst->count()) {\n      setMemberIndex(i++, 0);\n    } else if (SelectedChannel::get() == lst->channel(j)) {\n      logInfo() << \"Cannot encode '\" << lst->channel(j) << \"' for UV390: skip.\";\n      j++;\n    } else {\n      setMemberIndex(i++, ctx.index(lst->channel(j++)));\n    }\n  }\n\n  if (TyTScanListExtension *ex = lst->tytScanListExtension()) {\n    setHoldTime(ex->holdTime());\n    setPrioritySampleTime(ex->prioritySampleTime());\n  }\n\n  return true;\n}\n\nScanList *\nTyTCodeplug::ScanListElement::toScanListObj(Context &ctx) {\n  Q_UNUSED(ctx)\n  if (! isValid())\n    return nullptr;\n\n  ScanList *lst = new ScanList(name());\n\n  TyTScanListExtension *ex = new TyTScanListExtension();\n  lst->setTyTScanListExtension(ex);\n  ex->setHoldTime(holdTime());\n  ex->setPrioritySampleTime(prioritySampleTime());\n\n  return lst;\n}\n\nbool\nTyTCodeplug::ScanListElement::linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err) {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot link invalid scanlist.\";\n    return false;\n  }\n\n  if (0 == priorityChannel1Index())\n    lst->setPrimaryChannel(SelectedChannel::get());\n  else if (ctx.has<Channel>(priorityChannel1Index()))\n    lst->setPrimaryChannel(ctx.get<Channel>(priorityChannel1Index()));\n  else if (0xffff == priorityChannel1Index())\n    lst->setPrimaryChannel(nullptr);\n  else\n    logWarn() << \"Cannot decode reference to priority channel index \" << priorityChannel1Index()\n                 << \" in scan list '\" << name() << \"'.\";\n\n  if (0 == priorityChannel2Index())\n    lst->setSecondaryChannel(SelectedChannel::get());\n  else if (ctx.has<Channel>(priorityChannel2Index()))\n    lst->setSecondaryChannel(ctx.get<Channel>(priorityChannel2Index()));\n  else if (0xffff == priorityChannel2Index())\n    lst->setSecondaryChannel(nullptr);\n  else\n    logWarn() << \"Cannot decode reference to secondary priority channel index \" << priorityChannel2Index()\n              << \" in scan list '\" << name() << \"'.\";\n\n  if (0 == txChannelIndex())\n    lst->setRevertChannel(SelectedChannel::get());\n  else if (ctx.has<Channel>(txChannelIndex()))\n    lst->setRevertChannel(ctx.get<Channel>(txChannelIndex()));\n  else if (0xffff == txChannelIndex())\n    lst->setRevertChannel(nullptr);\n  else\n    logWarn() << \"Cannot decode reference to transmit channel index \" << txChannelIndex()\n                << \" in scan list '\" << name() << \"'.\";\n\n  for (int i=0; ((i<31) && memberIndex(i)); i++) {\n    if (! ctx.has<Channel>(memberIndex(i))) {\n      errMsg(err) << \"Cannot link scanlist to channel idx \" << memberIndex(i)\n                  << \". Unknown channel index.\";\n      return false;\n    }\n    lst->addChannel(ctx.get<Channel>(memberIndex(i)));\n  }\n\n  return true;\n\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nTyTCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : Element(ptr, SETTINGS_SIZE)\n{\n  // pass...\n}\n\nTyTCodeplug::GeneralSettingsElement::~GeneralSettingsElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::GeneralSettingsElement::clear() {\n  setIntroLine1(\"\");\n  setIntroLine2(\"\");\n  memset(_data+0x26, 0xff, 0x1a);\n\n  setBit(0x40,0, 0); setBit(0,1, 1);\n  disableAllLEDs(false);\n  setBit(0x40,3, 1);\n  setMonitorType(TyTSettingsExtension::MonitorType::Open);\n  setUInt3(0x40, 5, 7);\n\n  setSavePreamble(true);\n  setSaveModeRX(true);\n  disableAllTones(false);\n  setBit(0x41,3, 1);\n  setChFreeIndicationTone(true);\n  enablePasswdAndLock(false);\n  enableTalkPermitToneDigital(false);\n  enableTalkPermitToneAnalog(false);\n\n  setBit(0x42,0, 0); setBit(0x41,1, 1); setBit(0x42,2, 0); setBit(0x42,3, 1);\n  setBootDisplay(BootSettings::BootDisplay::Logo);\n  setBit(0x42,5, 1); setBit(0x42,6, 1); setBit(0x42,7, 1);\n\n  setUInt8(0x43, 0xff);\n\n  setDMRId(0); setUInt8(0x47,0);\n\n  setTXPreambleDuration(Interval::fromMilliseconds(600));\n  setGroupCallHangTime(Interval::fromMilliseconds(3000));\n  setPrivateCallHangTime(Interval::fromMilliseconds(4000));\n  setVOXSesitivity(Level::fromValue(3));\n  setUInt8(0x4c, 0x00); setUInt8(0x4d, 0x00);\n  setLowBatteryInterval(120);\n  setCallAlertToneDuration(0);\n  setLoneWorkerResponseTime(1);\n  setLoneWorkerReminderTime(10);\n  setUInt8(0x52, 0x00);\n  setScanDigitalHangTime(1000);\n  setScanAnalogHangTime(1000);\n\n  setBacklightTime(10);\n  setUInt6(0x55, 2, 0);\n  keypadLockTimeSetManual();\n  setUInt8(0x57, 0xff);\n\n  setPowerOnPassword(0);\n  radioProgPasswordDisable();\n  pcProgPasswordDisable();\n  setUInt32_le(0x68, 0xffffffff);\n  setUInt32_le(0x6c, 0xffffffff);\n\n  setRadioName(\"\");\n}\n\nQString\nTyTCodeplug::GeneralSettingsElement::introLine1() const {\n  return readUnicode(0, 10);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setIntroLine1(const QString line) {\n  writeUnicode(0, line, 10);\n}\n\nQString\nTyTCodeplug::GeneralSettingsElement::introLine2() const {\n  return readUnicode(0x14, 10);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setIntroLine2(const QString line) {\n  writeUnicode(0x14, line, 10);\n}\n\nTyTSettingsExtension::MonitorType\nTyTCodeplug::GeneralSettingsElement::monitorType() const {\n  return getBit(0x40,4) ? TyTSettingsExtension::MonitorType::Open : TyTSettingsExtension::MonitorType::Silent;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setMonitorType(TyTSettingsExtension::MonitorType type) {\n  setBit(0x40,4, TyTSettingsExtension::MonitorType::Open==type);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::allLEDsDisabled() const {\n  return ! getBit(0x40,2);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::disableAllLEDs(bool disable) {\n  setBit(0x40,2, !disable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::savePreamble() const {\n  return getBit(0x41,0);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setSavePreamble(bool enable) {\n  setBit(0x41,0, enable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::saveModeRX() const {\n  return getBit(0x41,1);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setSaveModeRX(bool enable) {\n  setBit(0x41,1, enable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::allTonesDisabled() const {\n  return ! getBit(0x41,2);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::disableAllTones(bool disable) {\n  setBit(0x41,2, !disable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::chFreeIndicationTone() const {\n  return !getBit(0x41,4);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setChFreeIndicationTone(bool enable) {\n  setBit(0x41,4, !enable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::passwdAndLock() const {\n  return ! getBit(0x41,5);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::enablePasswdAndLock(bool enable) {\n  setBit(0x41,5, !enable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::talkPermitToneDigital() const {\n  return getBit(0x41,6);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::enableTalkPermitToneDigital(bool enable) {\n  setBit(0x41,6, enable);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::talkPermitToneAnalog() const {\n  return getBit(0x41,7);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::enableTalkPermitToneAnalog(bool enable) {\n  setBit(0x41,7, enable);\n}\n\n\nBootSettings::BootDisplay\nTyTCodeplug::GeneralSettingsElement::bootDisplay() const {\n  return getBit(Offset::bootImageEnabled())\n    ? BootSettings::BootDisplay::Logo\n    : BootSettings::BootDisplay::Text;\n}\n\nvoid\nTyTCodeplug::GeneralSettingsElement::setBootDisplay(BootSettings::BootDisplay mode) {\n  switch (mode) {\n  case BootSettings::BootDisplay::Text: setBit(Offset::bootImageEnabled(), false); break;\n  case BootSettings::BootDisplay::Logo:\n  case BootSettings::BootDisplay::Image: setBit(Offset::bootImageEnabled(), true); break;\n  }\n}\n\n\nuint32_t\nTyTCodeplug::GeneralSettingsElement::dmrId() const {\n  return getUInt24_le(0x44);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setDMRId(uint32_t id) {\n  setUInt24_le(0x44, id);\n}\n\nInterval\nTyTCodeplug::GeneralSettingsElement::txPreambleDuration() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x48))*60);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setTXPreambleDuration(const Interval &dur) {\n  auto ms = std::min(8640ULL, dur.milliseconds());\n  setUInt8(0x48, ms/60);\n}\n\nInterval\nTyTCodeplug::GeneralSettingsElement::groupCallHangTime() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x49))*100);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setGroupCallHangTime(const Interval &dur) {\n  auto ms = std::min(7000ULL, dur.milliseconds());\n  setUInt8(0x49, ms/100);\n}\n\nInterval\nTyTCodeplug::GeneralSettingsElement::privateCallHangTime() const {\n  return Interval::fromMilliseconds(unsigned(getUInt8(0x4a))*100);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setPrivateCallHangTime(const Interval &dur) {\n  auto ms = std::min(7000ULL, dur.milliseconds());\n  setUInt8(0x4a, ms/100);\n}\n\nLevel\nTyTCodeplug::GeneralSettingsElement::voxSesitivity() const {\n  return Level::fromValue(getUInt8(0x4b));\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setVOXSesitivity(Level level) {\n  setUInt8(0x4b, level.mapTo(Limit::vox()));\n}\n\nunsigned\nTyTCodeplug::GeneralSettingsElement::lowBatteryInterval() const {\n  return unsigned(getUInt8(0x4e))*5;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setLowBatteryInterval(unsigned dur) {\n  dur = std::min(635U, dur);\n  setUInt8(0x4e, dur/5);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::callAlertToneIsContinuous() const {\n  return 0==getUInt8(0x4f);\n}\nunsigned\nTyTCodeplug::GeneralSettingsElement::callAlertToneDuration() const {\n  return unsigned(getUInt8(0x4f))*5;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setCallAlertToneDuration(unsigned dur) {\n  dur = std::min(1200U, dur);\n  setUInt8(0x4f, dur/5);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setCallAlertToneContinuous() {\n  setUInt8(0x4f, 0);\n}\n\nunsigned\nTyTCodeplug::GeneralSettingsElement::loneWorkerResponseTime() const {\n  return getUInt8(0x50);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setLoneWorkerResponseTime(unsigned dur) {\n  setUInt8(0x50, dur);\n}\n\nunsigned\nTyTCodeplug::GeneralSettingsElement::loneWorkerReminderTime() const {\n  return getUInt8(0x51);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setLoneWorkerReminderTime(unsigned dur) {\n  setUInt8(0x51, dur);\n}\n\nunsigned\nTyTCodeplug::GeneralSettingsElement::scanDigitalHangTime() const {\n  return unsigned(getUInt8(0x53))*100;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setScanDigitalHangTime(unsigned dur) {\n  dur = std::min(10000U, dur);\n  setUInt8(0x53, dur/100);\n}\n\nunsigned\nTyTCodeplug::GeneralSettingsElement::scanAnalogHangTime() const {\n  return unsigned(getUInt8(0x54))*100;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setScanAnalogHangTime(unsigned dur) {\n  dur = std::min(10000U, dur);\n  setUInt8(0x54, dur/100);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::backlightIsAlways() const {\n  return 0 == getUInt2(0x55, 0);\n}\nunsigned\nTyTCodeplug::GeneralSettingsElement::backlightTime() const {\n  return getUInt2(0x55, 0)*5;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setBacklightTime(unsigned sec) {\n  sec = std::min(15U, sec);\n  setUInt2(0x55, 0, sec/5);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::backlightTimeSetAlways() {\n  setUInt2(0x55, 0, 0);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::keypadLockIsManual() const {\n  return 0xff == getUInt8(0x56);\n}\nunsigned\nTyTCodeplug::GeneralSettingsElement::keypadLockTime() const {\n  return unsigned(getUInt8(0x56))*5;\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setKeypadLockTime(unsigned sec) {\n  sec = std::max(1U, std::min(15U, sec));\n  setUInt8(0x56, sec/5);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::keypadLockTimeSetManual() {\n  setUInt8(0x56, 0xff);\n}\n\nuint32_t\nTyTCodeplug::GeneralSettingsElement::powerOnPassword() const {\n  return getBCD8_le(0x58);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setPowerOnPassword(uint32_t pass) {\n  setBCD8_le(0x58, pass);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::radioProgPasswordEnabled() const {\n  return 0xffffffff != getUInt32_le(0x5c);\n}\nuint32_t\nTyTCodeplug::GeneralSettingsElement::radioProgPassword() const {\n  return getBCD8_le(0x5c);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setRadioProgPassword(uint32_t passwd) {\n  setBCD8_le(0x5c, passwd);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::radioProgPasswordDisable() {\n  setUInt32_le(0x5c, 0xffffffff);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::pcProgPasswordEnabled() const {\n  return (0xff != getUInt8(0x60));\n}\nQString\nTyTCodeplug::GeneralSettingsElement::pcProgPassword() const {\n  return readASCII(0x60, 8, 0xff);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setPCProgPassword(const QString &pass) {\n  writeASCII(0x60, pass, 8, 0xff);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::pcProgPasswordDisable() {\n  memset(_data+0x60, 0xff, 8);\n}\n\nQString\nTyTCodeplug::GeneralSettingsElement::radioName() const {\n  return readUnicode(0x70, 16);\n}\nvoid\nTyTCodeplug::GeneralSettingsElement::setRadioName(const QString &name) {\n  writeUnicode(0x70, name, 16);\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::fromConfig(const Config *config) {\n  if (config->settings()->defaultIdRef()->isNull())\n    return false;\n  setRadioName(config->settings()->defaultIdRef()->as<DMRRadioID>()->name());\n  setDMRId(config->settings()->defaultIdRef()->as<DMRRadioID>()->number());\n\n  setIntroLine1(config->settings()->boot()->message1());\n  setIntroLine2(config->settings()->boot()->message2());\n  setVOXSesitivity(config->settings()->audio()->vox());\n\n  setBootDisplay(config->settings()->boot()->bootDisplay());\n  if (! config->settings()->boot()->bootPasswordEnabled())\n    setPowerOnPassword(0);\n  else\n    setPowerOnPassword(config->settings()->boot()->bootPassword().toUInt());\n\n  // apply tone settings\n  disableAllTones(config->settings()->tone()->silent());\n  enableTalkPermitToneDigital(config->settings()->tone()->talkPermit().testFlag(Channel::Type::DMR));\n  enableTalkPermitToneAnalog(config->settings()->tone()->talkPermit().testFlag(Channel::Type::FM));\n  setChFreeIndicationTone(config->settings()->tone()->channelIdle().testFlags(Channel::Type::DMR|Channel::Type::FM));\n\n  setPrivateCallHangTime(config->settings()->dmr()->privateCallHangTime());\n  setGroupCallHangTime(config->settings()->dmr()->groupCallHangTime());\n  setTXPreambleDuration(config->settings()->dmr()->preamble());\n\n  if (TyTSettingsExtension *ex = config->settings()->tytExtension()) {\n    setMonitorType(ex->monitorType());\n    disableAllLEDs(ex->allLEDsDisabled());\n    enablePasswdAndLock(ex->passwordAndLock());\n    setSaveModeRX(ex->powerSaveMode());\n    setSavePreamble(ex->wakeupPreamble());\n    setLowBatteryInterval(ex->lowBatteryWarnInterval());\n    if (ex->callAlertToneContinuous())\n      setCallAlertToneContinuous();\n    else\n      setCallAlertToneDuration(ex->callAlertToneDuration());\n    setLoneWorkerResponseTime(ex->loneWorkerResponseTime());\n    setLoneWorkerReminderTime(ex->loneWorkerReminderTime());\n    setScanDigitalHangTime(ex->digitalScanHangTime());\n    setScanAnalogHangTime(ex->analogScanHangTime());\n    if (ex->backlightAlwaysOn())\n      backlightTimeSetAlways();\n    else\n      setBacklightTime(ex->backlightDuration());\n    if (ex->keypadLockManual())\n      keypadLockTimeSetManual();\n    else\n      setKeypadLockTime(ex->keypadLockTime());\n    if (! ex->radioProgPasswordEnabled())\n      radioProgPasswordDisable();\n    else\n      setRadioProgPassword(ex->radioProgPassword());\n    setPCProgPassword(ex->pcProgPassword());\n  }\n\n  return true;\n}\n\nbool\nTyTCodeplug::GeneralSettingsElement::updateConfig(Config *config) {\n  int idx = config->radioIDs()->add(new DMRRadioID(radioName(), dmrId()));\n  if (0 <= idx) {\n    config->settings()->defaultIdRef()->set(\n      config->radioIDs()->get(idx)->as<DMRRadioID>());\n  } else {\n    logError() << \"Cannot add radio DMR ID & cannot set default ID.\";\n    return false;\n  }\n\n  // Store boot settings\n  config->settings()->boot()->setBootDisplay(bootDisplay());\n  config->settings()->boot()->setMessage1(introLine1());\n  config->settings()->boot()->setMessage2(introLine2());\n  config->settings()->boot()->enableBootPassword(0 != powerOnPassword());\n  config->settings()->boot()->setBootPassword(QString::number(powerOnPassword()));\n\n  // Store audio settings\n  config->settings()->audio()->setVox(voxSesitivity());\n\n  // Store tone settings\n  config->settings()->tone()->enableSilent(allTonesDisabled());\n  config->settings()->tone()->setTalkPermit(\n    (talkPermitToneDigital() ? Channel::Type::DMR : Channel::Type::None) |\n    (talkPermitToneAnalog() ? Channel::Type::FM : Channel::Type::None));\n  config->settings()->tone()->setChannelIdle(\n    chFreeIndicationTone() ? (Channel::Type::DMR|Channel::Type::FM) : Channel::Type::None);\n\n  config->settings()->dmr()->setPrivateCallHangTime(privateCallHangTime());\n  config->settings()->dmr()->setGroupCallHangTime(groupCallHangTime());\n  config->settings()->dmr()->setPreamble(txPreambleDuration());\n\n  // apply extension\n  TyTSettingsExtension *ex = new TyTSettingsExtension();\n  config->settings()->setTyTExtension(ex);\n\n  ex->setMonitorType(monitorType());\n  ex->disableAllLEDs(allLEDsDisabled());\n  ex->enablePasswordAndLock(passwdAndLock());\n  ex->enablePowerSaveMode(saveModeRX());\n  ex->enableWakeupPreamble(savePreamble());\n  ex->setLowBatteryWarnInterval(lowBatteryInterval());\n  ex->enableCallAlertToneContinuous(callAlertToneIsContinuous());\n  if (! callAlertToneIsContinuous())\n    ex->setCallAlertToneDuration(callAlertToneDuration());\n  ex->setLoneWorkerResponseTime(loneWorkerResponseTime());\n  ex->setLoneWorkerReminderTime(loneWorkerReminderTime());\n  ex->setDigitalScanHangTime(scanDigitalHangTime());\n  ex->setAnalogScanHangTime(scanAnalogHangTime());\n  ex->enableBacklightAlwaysOn(backlightIsAlways());\n  if (! backlightIsAlways())\n    ex->setBacklightDuration(backlightTime());\n  ex->enableKeypadLockManual(keypadLockIsManual());\n  if (! keypadLockIsManual())\n    ex->setKeypadLockTime(keypadLockTime());\n  ex->enableRadioProgPassword(radioProgPasswordEnabled());\n  if (radioProgPasswordEnabled())\n    ex->setRadioProgPassword(ex->radioProgPassword());\n  ex->setPCProgPassword(pcProgPassword());\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::TimestampElement\n * ******************************************************************************************** */\nTyTCodeplug::TimestampElement::TimestampElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::TimestampElement::TimestampElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0c)\n{\n  // pass...\n}\n\nTyTCodeplug::TimestampElement::~TimestampElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::TimestampElement::clear() {\n  setUInt8(0x00, 0);\n  setTimestamp(QDateTime::currentDateTime());\n  setUInt32_be(0x08, 0x00010300);\n}\n\nQDateTime\nTyTCodeplug::TimestampElement::timestamp() const {\n  return QDateTime(QDate(getBCD4_le(0x01), getBCD2(0x03), getBCD2(0x04)),\n                   QTime(getBCD2(0x05), getBCD2(0x06), getBCD2(0x07)));\n}\nvoid\nTyTCodeplug::TimestampElement::setTimestamp(const QDateTime &ts) {\n  setBCD4_le(0x01, ts.date().year());\n  setBCD2(0x03, ts.date().month());\n  setBCD2(0x04, ts.date().day());\n  setBCD2(0x05, ts.time().hour());\n  setBCD2(0x06, ts.time().minute());\n  setBCD2(0x07, ts.time().second());\n}\n\nQString\nTyTCodeplug::TimestampElement::cpsVersion() const {\n  const char table[] = \"0123456789:;<=>?\";\n\n  QString v;\n  v.append(table[std::min(uint8_t(15), getUInt8(0x08))]);\n  v.append(table[std::min(uint8_t(15), getUInt8(0x09))]);\n  v.append(\".\");\n  v.append(table[std::min(uint8_t(15), getUInt8(0x0a))]);\n  v.append(table[std::min(uint8_t(15), getUInt8(0x0b))]);\n\n  return v;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::GPSSystemElement\n * ******************************************************************************************** */\nTyTCodeplug::GPSSystemElement::GPSSystemElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::GPSSystemElement::GPSSystemElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x10)\n{\n  // pass...\n}\n\nTyTCodeplug::GPSSystemElement::~GPSSystemElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::GPSSystemElement::isValid() const {\n  return Element::isValid() && (! repeatIntervalDisabled()) &&\n      (!destinationContactDisabled());\n}\n\nvoid\nTyTCodeplug::GPSSystemElement::clear() {\n  setRevertChannelIndex(0xffff);\n  disableRepeatInterval();\n  setUInt8(0x03, 0xff);\n  disableDestinationContact();\n  memset(_data + 0x06, 0xff, 10);\n}\n\nbool\nTyTCodeplug::GPSSystemElement::revertChannelIsSelected() const {\n  return 0 == getUInt16_le(0x00);\n}\nuint16_t\nTyTCodeplug::GPSSystemElement::revertChannelIndex() const {\n  return getUInt16_le(0x00);\n}\nvoid\nTyTCodeplug::GPSSystemElement::setRevertChannelIndex(uint16_t idx) {\n  setUInt16_le(0x00, idx);\n}\nvoid\nTyTCodeplug::GPSSystemElement::setRevertChannelSelected() {\n  setUInt16_le(0x00, 0);\n}\n\n\nbool\nTyTCodeplug::GPSSystemElement::repeatIntervalDisabled() const {\n  return 0 == getUInt8(0x02);\n}\n\nInterval TyTCodeplug::GPSSystemElement::repeatInterval() const {\n  return Interval::fromSeconds(unsigned(getUInt8(0x02))*30);\n}\n\nvoid\nTyTCodeplug::GPSSystemElement::setRepeatInterval(const Interval &dur) {\n  if (! dur.isFinite())\n    disableRepeatInterval();\n  else\n    setUInt8(0x02, dur.seconds()/30);\n}\n\nvoid\nTyTCodeplug::GPSSystemElement::disableRepeatInterval() {\n  setUInt8(0x02, 0);\n}\n\n\nbool\nTyTCodeplug::GPSSystemElement::destinationContactDisabled() const {\n  return 0 == getUInt16_le(0x04);\n}\nuint16_t\nTyTCodeplug::GPSSystemElement::destinationContactIndex() const {\n  return getUInt16_le(0x04);\n}\nvoid\nTyTCodeplug::GPSSystemElement::setDestinationContactIndex(uint16_t idx) {\n  setUInt16_le(0x04, idx);\n}\nvoid\nTyTCodeplug::GPSSystemElement::disableDestinationContact() {\n  setUInt16_le(0x04, 0);\n}\n\nbool\nTyTCodeplug::GPSSystemElement::fromGPSSystemObj(DMRAPRSSystem *sys, Context &ctx) {\n  clear();\n  if (sys->hasContact())\n    setDestinationContactIndex(ctx.index(sys->contact()));\n  if (sys->hasRevertChannel())\n    setRevertChannelIndex(ctx.index(sys->revertChannel()));\n  else\n    setRevertChannelSelected();\n  setRepeatInterval(sys->period());\n  return true;\n}\n\nDMRAPRSSystem *\nTyTCodeplug::GPSSystemElement::toGPSSystemObj() {\n  return new DMRAPRSSystem(\"GPS System\", nullptr, nullptr, repeatInterval());\n}\n\nbool\nTyTCodeplug::GPSSystemElement::linkGPSSystemObj(DMRAPRSSystem *sys, Context &ctx) {\n  if (! isValid())\n    return false;\n\n  if ((! destinationContactDisabled()) && (ctx.has<DMRContact>(destinationContactIndex())))\n    sys->setContact(ctx.get<DMRContact>(destinationContactIndex()));\n\n  if (revertChannelIsSelected())\n    sys->resetRevertChannel();\n  else if (ctx.has<Channel>(revertChannelIndex()) && ctx.get<Channel>(revertChannelIndex())->is<DMRChannel>())\n    sys->setRevertChannel(ctx.get<Channel>(revertChannelIndex())->as<DMRChannel>());\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::MenuSettingsElement\n * ******************************************************************************************** */\nTyTCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr)\n  : Element(ptr, MENUSETTINGS_SIZE)\n{\n  // pass...\n}\n\nTyTCodeplug::MenuSettingsElement::~MenuSettingsElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::MenuSettingsElement::clear() {\n  setMenuHangtime(0);\n\n  enableTextMessage(true);\n  enableCallAlert(true);\n  enableContactEditing(true);\n  enableManualDial(true);\n  enableRemoteRadioCheck(false);\n  enableRemoteMonitor(false);\n  enableRemoteRadioEnable(false);\n  enableRemoteRadioDisable(false);\n\n  setBit(0x02, 0, false);\n  enableScan(true);\n  enableScanListEditing(true);\n  enableCallLogMissed(true);\n  enableCallLogAnswered(true);\n  enableCallLogOutgoing(true);\n  enableTalkaround(false);\n  enableAlertTone(true);\n\n  enablePower(true);\n  enableBacklight(true);\n  enableBootScreen(true);\n  enableKeypadLock(true);\n  enableLEDIndicator(true);\n  enableSquelch(true);\n  setBit(0x03, 6, false);\n  enableVOX(true);\n\n  enablePassword(false);\n  enableDisplayMode(true);\n  enableRadioProgramming(true);\n  setBit(0x04, 3, true);\n  setBit(0x04, 1, true);\n  setBit(0x04, 5, true);\n  setBit(0x04, 6, true);\n  setBit(0x04, 7, true);\n  setUInt8(0x05, 0xfb);\n  setUInt8(0x06, 0xff);\n\n  memset(_data+0x07, 0xff, 9);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::menuHangtimeIsInfinite() const {\n  return 0 == menuHangtime();\n}\nunsigned\nTyTCodeplug::MenuSettingsElement::menuHangtime() const {\n  return getUInt8(0x00);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::setMenuHangtime(unsigned sec) {\n  setUInt8(0x00, sec);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::infiniteMenuHangtime() {\n  setUInt8(0x00, 0x00);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::textMessage() const {\n  return getBit(0x01, 0);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableTextMessage(bool enable) {\n  setBit(0x01, 0, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::callAlert() const {\n  return getBit(0x01, 1);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableCallAlert(bool enable) {\n  setBit(0x01, 1, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::contactEditing() const {\n  return getBit(0x01, 2);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableContactEditing(bool enable) {\n  setBit(0x01, 2, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::manualDial() const {\n  return getBit(0x01, 3);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableManualDial(bool enable) {\n  setBit(0x01, 3, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::remoteRadioCheck() const {\n  return getBit(0x01, 4);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableRemoteRadioCheck(bool enable) {\n  setBit(0x01, 4, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::remoteMonitor() const {\n  return getBit(0x01, 5);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableRemoteMonitor(bool enable) {\n  setBit(0x01, 5, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::remoteRadioEnable() const {\n  return getBit(0x01, 6);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableRemoteRadioEnable(bool enable) {\n  setBit(0x01, 6, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::remoteRadioDisable() const {\n  return getBit(0x01, 7);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableRemoteRadioDisable(bool enable) {\n  setBit(0x01, 7, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::scan() const {\n  return getBit(0x02, 1);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableScan(bool enable) {\n  setBit(0x02, 1, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::scanListEditing() const {\n  return getBit(0x02, 2);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableScanListEditing(bool enable) {\n  setBit(0x02, 2, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::callLogMissed() const {\n  return getBit(0x02, 3);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableCallLogMissed(bool enable) {\n  setBit(0x02, 3, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::callLogAnswered() const {\n  return getBit(0x02, 4);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableCallLogAnswered(bool enable) {\n  setBit(0x02, 4, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::callLogOutgoing() const {\n  return getBit(0x02, 5);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableCallLogOutgoing(bool enable) {\n  setBit(0x02, 5, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::talkaround() const {\n  return getBit(0x02, 6);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableTalkaround(bool enable) {\n  setBit(0x02, 6, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::alertTone() const {\n  return getBit(0x02, 7);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableAlertTone(bool enable) {\n  setBit(0x02, 7, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::power() const {\n  return getBit(0x03, 0);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enablePower(bool enable) {\n  setBit(0x03, 0, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::backlight() const {\n  return getBit(0x03, 1);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableBacklight(bool enable) {\n  setBit(0x03, 1, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::bootScreen() const {\n  return getBit(0x03, 2);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableBootScreen(bool enable) {\n  setBit(0x03, 2, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::keypadLock() const {\n  return getBit(0x03, 3);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableKeypadLock(bool enable) {\n  setBit(0x03, 3, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::ledIndicator() const {\n  return getBit(0x03, 4);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableLEDIndicator(bool enable) {\n  setBit(0x03, 4, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::squelch() const {\n  return getBit(0x03, 5);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableSquelch(bool enable) {\n  setBit(0x03, 5, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::vox() const {\n  return getBit(0x03, 7);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableVOX(bool enable) {\n  setBit(0x03, 7, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::password() const {\n  return getBit(0x04, 0);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enablePassword(bool enable) {\n  setBit(0x04, 0, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::displayMode() const {\n  return getBit(0x04, 1);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableDisplayMode(bool enable) {\n  setBit(0x04, 1, enable);\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::radioProgramming() const {\n  return ! getBit(0x04, 2);\n}\nvoid\nTyTCodeplug::MenuSettingsElement::enableRadioProgramming(bool enable) {\n  setBit(0x04, 2, !enable);\n}\n\n\nbool\nTyTCodeplug::MenuSettingsElement::fromConfig(const Config *config) {\n  if (nullptr == config->tytExtension())\n    return true;\n\n  TyTMenuSettings *ex = config->tytExtension()->menuSettings();\n  if (ex->hangtimeIsInfinite())\n    infiniteMenuHangtime();\n  else\n    setMenuHangtime(ex->hangTime());\n  enableTextMessage(ex->textMessage());\n  enableCallAlert(ex->callAlert());\n  enableContactEditing(ex->contactEditing());\n  enableManualDial(ex->manualDial());\n  enableRemoteRadioCheck(ex->remoteRadioCheck());\n  enableRemoteMonitor(ex->remoteMonitor());\n  enableRemoteRadioEnable(ex->remoteRadioEnable());\n  enableRemoteRadioDisable(ex->remoteRadioDisable());\n  enableScan(ex->scan());\n  enableScanListEditing(ex->scanListEditing());\n  enableCallLogMissed(ex->callLogMissed());\n  enableCallLogAnswered(ex->callLogAnswered());\n  enableCallLogOutgoing(ex->callLogOutgoing());\n  enableTalkaround(ex->talkaround());\n  enableAlertTone(ex->alertTone());\n  enablePower(ex->power());\n  enableBacklight(ex->backlight());\n  enableBootScreen(ex->bootScreen());\n  enableKeypadLock(ex->keypadLock());\n  enableLEDIndicator(ex->ledIndicator());\n  enableSquelch(ex->squelch());\n  enableVOX(ex->vox());\n  enablePassword(ex->password());\n  enableDisplayMode(ex->displayMode());\n  enableRadioProgramming(ex->radioProgramming());\n\n  return true;\n}\n\nbool\nTyTCodeplug::MenuSettingsElement::updateConfig(Config *config) {\n  TyTConfigExtension *ext = config->tytExtension();\n  if (nullptr == ext) {\n    ext = new TyTConfigExtension(config);\n    config->setTyTExtension(ext);\n  }\n\n  TyTMenuSettings *ex = ext->menuSettings();\n  if (menuHangtimeIsInfinite())\n    ex->setHangtimeInfinite(true);\n  else\n    ex->setHangTime(menuHangtime());\n  ex->enableTextMessage(textMessage());\n  ex->enableCallAlert(callAlert());\n  ex->enableContactEditing(contactEditing());\n  ex->enableManualDial(manualDial());\n  ex->enableRemoteRadioCheck(remoteRadioCheck());\n  ex->enableRemoteMonitor(remoteMonitor());\n  ex->enableRemoteRadioEnable(remoteRadioEnable());\n  ex->enableRemoteRadioDisable(remoteRadioDisable());\n  ex->enableScan(scan());\n  ex->enableScanListEditing(scanListEditing());\n  ex->enableCallLogMissed(callLogMissed());\n  ex->enableCallLogAnswered(callLogAnswered());\n  ex->enableCallLogOutgoing(callLogOutgoing());\n  ex->enableTalkaround(talkaround());\n  ex->enableAlertTone(alertTone());\n  ex->enablePower(power());\n  ex->enableBacklight(backlight());\n  ex->enableBootScreen(bootScreen());\n  ex->enableKeypadLock(keypadLock());\n  ex->enableLEDIndicator(ledIndicator());\n  ex->enableSquelch(squelch());\n  ex->enableVOX(vox());\n  ex->enablePassword(password());\n  ex->enableDisplayMode(displayMode());\n  ex->enableRadioProgramming(radioProgramming());\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::ButtonSettingsElement\n * ******************************************************************************************** */\nTyTCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonSettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x14) // <- size excluding one-touch settings\n{\n  // pass...\n}\n\nTyTCodeplug::ButtonSettingsElement::~ButtonSettingsElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::ButtonSettingsElement::clear() {\n  setUInt16_le(0x00, 0);\n  setSideButton1Short(ButtonAction::Disabled);\n  setSideButton1Long(ButtonAction::Tone1750Hz);\n  setSideButton2Short(ButtonAction::MonitorToggle);\n  setSideButton2Long(ButtonAction::Disabled);\n  memset(_data+0x06, 0x00, 10);\n  setUInt8(0x10, 1);\n  setLongPressDuration(1000);\n  setUInt16_le(0x12, 0xffff);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nTyTCodeplug::ButtonSettingsElement::sideButton1Short() const {\n  return ButtonAction(getUInt8(0x02));\n}\nvoid\nTyTCodeplug::ButtonSettingsElement::setSideButton1Short(ButtonAction action) {\n  setUInt8(0x02, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nTyTCodeplug::ButtonSettingsElement::sideButton1Long() const {\n  return ButtonAction(getUInt8(0x03));\n}\nvoid\nTyTCodeplug::ButtonSettingsElement::setSideButton1Long(ButtonAction action) {\n  setUInt8(0x03, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nTyTCodeplug::ButtonSettingsElement::sideButton2Short() const {\n  return ButtonAction(getUInt8(0x04));\n}\nvoid\nTyTCodeplug::ButtonSettingsElement::setSideButton2Short(ButtonAction action) {\n  setUInt8(0x04, action);\n}\n\nTyTCodeplug::ButtonSettingsElement::ButtonAction\nTyTCodeplug::ButtonSettingsElement::sideButton2Long() const {\n  return ButtonAction(getUInt8(0x05));\n}\nvoid\nTyTCodeplug::ButtonSettingsElement::setSideButton2Long(ButtonAction action) {\n  setUInt8(0x05, action);\n}\n\nunsigned\nTyTCodeplug::ButtonSettingsElement::longPressDuration() const {\n  return unsigned(getUInt8(0x11))*250;\n}\nvoid\nTyTCodeplug::ButtonSettingsElement::setLongPressDuration(unsigned ms) {\n  setUInt8(0x11, ms/250);\n}\n\nbool\nTyTCodeplug::ButtonSettingsElement::fromConfig(const Config *config) {\n  // Skip if not defined\n  if (nullptr == config->tytExtension())\n    return true;\n\n  setSideButton1Short(config->tytExtension()->buttonSettings()->sideButton1Short());\n  setSideButton1Long(config->tytExtension()->buttonSettings()->sideButton1Long());\n  setSideButton2Short(config->tytExtension()->buttonSettings()->sideButton2Short());\n  setSideButton2Long(config->tytExtension()->buttonSettings()->sideButton2Long());\n  setLongPressDuration(config->tytExtension()->buttonSettings()->longPressDuration());\n\n  return true;\n}\n\nbool\nTyTCodeplug::ButtonSettingsElement::updateConfig(Config *config) {\n  TyTConfigExtension *ext = config->tytExtension();\n  if (nullptr == ext) {\n    ext = new TyTConfigExtension(config);\n    config->setTyTExtension(ext);\n  }\n\n  ext->buttonSettings()->setSideButton1Short(sideButton1Short());\n  ext->buttonSettings()->setSideButton1Long(sideButton1Long());\n  ext->buttonSettings()->setSideButton2Short(sideButton2Short());\n  ext->buttonSettings()->setSideButton2Long(sideButton2Long());\n  ext->buttonSettings()->setLongPressDuration(longPressDuration());\n\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::OneTouchSettingElement\n * ******************************************************************************************** */\nTyTCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::OneTouchSettingElement::OneTouchSettingElement(uint8_t *ptr)\n  : Element(ptr, 0x04)\n{\n  // pass....\n}\n\nTyTCodeplug::OneTouchSettingElement::~OneTouchSettingElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::OneTouchSettingElement::isValid() const {\n  return Element::isValid() && (Disabled != actionType());\n}\n\nvoid\nTyTCodeplug::OneTouchSettingElement::clear() {\n  setAction(CALL);\n  setActionType(Disabled);\n  setUInt2(0x00, 6, 3);\n  setMessageIndex(0);\n  setContactIndex(0);\n}\n\nTyTCodeplug::OneTouchSettingElement::Action\nTyTCodeplug::OneTouchSettingElement::action() const {\n  return Action(getUInt4(0x00, 0));\n}\nvoid\nTyTCodeplug::OneTouchSettingElement::setAction(Action action) {\n  setUInt4(0x00, 0, action);\n}\n\nTyTCodeplug::OneTouchSettingElement::Type\nTyTCodeplug::OneTouchSettingElement::actionType() const {\n  return Type(getUInt2(0x00, 4));\n}\nvoid\nTyTCodeplug::OneTouchSettingElement::setActionType(Type type) {\n  setUInt2(0x00, 4, type);\n}\n\nuint8_t\nTyTCodeplug::OneTouchSettingElement::messageIndex() const {\n  return getUInt8(0x01);\n}\nvoid\nTyTCodeplug::OneTouchSettingElement::setMessageIndex(uint8_t idx) {\n  setUInt8(0x01, idx);\n}\n\nuint16_t\nTyTCodeplug::OneTouchSettingElement::contactIndex() const {\n  return getUInt16_le(0x02);\n}\nvoid\nTyTCodeplug::OneTouchSettingElement::setContactIndex(uint16_t idx) {\n  setUInt16_le(0x02, idx);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::EmergencySettingsElement\n * ******************************************************************************************** */\nTyTCodeplug::EmergencySettingsElement::EmergencySettingsElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::EmergencySettingsElement::EmergencySettingsElement(uint8_t *ptr)\n  : Element(ptr, 0x10)\n{\n  // pass...\n}\n\nTyTCodeplug::EmergencySettingsElement::~EmergencySettingsElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::EmergencySettingsElement::clear() {\n  setUInt5(0x00, 3, 0b11111);\n  enableRadioDisable(true);\n  enableRemoteMonitor(false);\n  enableEmergencyRemoteMonitor(true);\n  setRemoteMonitorDuration(10);\n  setTXTimeOut(125);\n  setMessageLimit(2);\n  memset(_data+0x04, 0xff, 12);\n}\n\nbool\nTyTCodeplug::EmergencySettingsElement::emergencyRemoteMonitor() const {\n  return getBit(0x00, 2);\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::enableEmergencyRemoteMonitor(bool enable) {\n  setBit(0x00, 2, enable);\n}\n\nbool\nTyTCodeplug::EmergencySettingsElement::remoteMonitor() const {\n  return getBit(0x00, 1);\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::enableRemoteMonitor(bool enable) {\n  setBit(0x00, 1, enable);\n}\n\nbool\nTyTCodeplug::EmergencySettingsElement::radioDisable() const {\n  return getBit(0x00, 0);\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::enableRadioDisable(bool enable) {\n  setBit(0x00, 0, enable);\n}\n\nunsigned\nTyTCodeplug::EmergencySettingsElement::remoteMonitorDuration() const {\n  return unsigned(getUInt8(0x01))*10;\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::setRemoteMonitorDuration(unsigned sec) {\n  setUInt8(0x01, sec/10);\n}\n\nunsigned\nTyTCodeplug::EmergencySettingsElement::txTimeOut() const {\n  return unsigned(getUInt8(0x02))*25;\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::setTXTimeOut(unsigned ms) {\n  setUInt8(0x02, ms/25);\n}\n\nunsigned\nTyTCodeplug::EmergencySettingsElement::messageLimit() const {\n  return unsigned(getUInt8(0x03));\n}\nvoid\nTyTCodeplug::EmergencySettingsElement::setMessageLimit(unsigned limit) {\n  setUInt8(0x03, limit);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::EmergencySystemElement\n * ******************************************************************************************** */\nTyTCodeplug::EmergencySystemElement::EmergencySystemElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::EmergencySystemElement::EmergencySystemElement(uint8_t *ptr)\n  : Element(ptr, 0x28)\n{\n  // pass...\n}\n\nTyTCodeplug::EmergencySystemElement::~EmergencySystemElement() {\n  // pass...\n}\n\nbool\nTyTCodeplug::EmergencySystemElement::isValid() const {\n  return Element::isValid() && (0 != revertChannelIndex());\n}\n\nvoid\nTyTCodeplug::EmergencySystemElement::clear() {\n  setName(\"\");\n  setAlarmType(DISABLED);\n  setUInt2(0x00, 2, 3);\n  setAlarmMode(ALARM);\n  setUInt2(0x00, 6, 1);\n  setImpoliteRetries(15);\n  setPoliteRetries(5);\n  setHotMICDuration(100);\n  setRevertChannelIndex(0);\n  setUInt16_le(0x26, 0xffff);\n}\n\nQString\nTyTCodeplug::EmergencySystemElement::name() const {\n  return readUnicode(0x00, 16);\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setName(const QString &nm) {\n  writeUnicode(0x00, nm, 16);\n}\n\nTyTCodeplug::EmergencySystemElement::AlarmType\nTyTCodeplug::EmergencySystemElement::alarmType() const {\n  return AlarmType(getUInt2(0x20, 0));\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setAlarmType(AlarmType type) {\n  setUInt2(0x20, 0, type);\n}\n\nTyTCodeplug::EmergencySystemElement::AlarmMode\nTyTCodeplug::EmergencySystemElement::alarmMode() const {\n  return AlarmMode(getUInt2(0x20, 4));\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setAlarmMode(AlarmMode mode) {\n  setUInt2(0x20, 4, mode);\n}\n\nunsigned\nTyTCodeplug::EmergencySystemElement::impoliteRetries() const {\n  return getUInt8(0x21);\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setImpoliteRetries(unsigned num) {\n  setUInt8(0x21, num);\n}\n\nunsigned\nTyTCodeplug::EmergencySystemElement::politeRetries() const {\n  return getUInt8(0x22);\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setPoliteRetries(unsigned num) {\n  setUInt8(0x22, num);\n}\n\nunsigned\nTyTCodeplug::EmergencySystemElement::hotMICDuration() const {\n  return unsigned(getUInt8(0x23))*10;\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setHotMICDuration(unsigned dur) {\n  setUInt8(0x23, dur/10);\n}\n\nbool\nTyTCodeplug::EmergencySystemElement::revertChannelIsSelected() const {\n  return 0xffff == getUInt16_le(0x24);\n}\nuint16_t\nTyTCodeplug::EmergencySystemElement::revertChannelIndex() const {\n  return getUInt16_le(0x24);\n}\nvoid\nTyTCodeplug::EmergencySystemElement::setRevertChannelIndex(uint16_t idx) {\n  setUInt16_le(0x24, idx);\n}\nvoid\nTyTCodeplug::EmergencySystemElement::revertChannelSelected() {\n  setUInt16_le(0x24, 0xffff);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::EncryptionElement\n * ******************************************************************************************** */\nTyTCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::EncryptionElement::EncryptionElement(uint8_t *ptr)\n  : Element(ptr, size())\n{\n  // pass...\n}\n\nTyTCodeplug::EncryptionElement::~EncryptionElement() {\n  // pass...\n}\n\nvoid\nTyTCodeplug::EncryptionElement::clear() {\n  memset(_data, 0xff, size());\n}\n\nbool\nTyTCodeplug::EncryptionElement::isEnhancedKeySet(unsigned n) const {\n  QByteArray key = enhancedKey(n);\n  for (unsigned int i=1; i<Offset::betweenAdvancedKeys(); i++) {\n    if (key[0] != key[i])\n      return true;\n  }\n  return ('\\xff' != key[0]) && ('\\x00' != key[0]);\n}\nQByteArray\nTyTCodeplug::EncryptionElement::enhancedKey(unsigned n) const {\n  QByteArray dest(Offset::betweenAdvancedKeys(), 0);\n  const uint8_t *src = _data + Offset::advancedKeys() + n*Offset::betweenAdvancedKeys();\n  for (unsigned i=0,j=(Offset::betweenAdvancedKeys()-1); i<Offset::betweenAdvancedKeys(); i++,j--)\n    dest[i] = src[j];\n  return dest;\n}\nvoid\nTyTCodeplug::EncryptionElement::setEnhancedKey(unsigned n, const QByteArray &key) {\n  if (Offset::betweenAdvancedKeys() != key.size())\n    return;\n  uint8_t *dest = _data + Offset::advancedKeys() + n*Offset::betweenAdvancedKeys();\n  for (unsigned i=0,j=(Offset::betweenAdvancedKeys()-1); i<Offset::betweenAdvancedKeys(); i++,j--)\n    dest[i] = key[j];\n}\n\nbool\nTyTCodeplug::EncryptionElement::isBasicKeySet(unsigned n) const {\n  QByteArray key = basicKey(n);\n  for (unsigned int i=1; i<Offset::betweenBasicKeys(); i++)\n    if (key[0] != key[i])\n      return true;\n  return ('\\xff'!=key[0]) && ('\\x00' != key[0]);\n}\nQByteArray\nTyTCodeplug::EncryptionElement::basicKey(unsigned n) const {\n  QByteArray dest(Offset::betweenBasicKeys(), 0);\n  const uint8_t *src = _data + Offset::basicKeys() + n*Offset::betweenBasicKeys();\n  for (unsigned i=0,j=(Offset::betweenBasicKeys()-1); i<Offset::betweenBasicKeys(); i++,j--)\n    dest[i] = src[j];\n  return dest;\n}\nvoid\nTyTCodeplug::EncryptionElement::setBasicKey(unsigned n, const QByteArray &key) {\n  if (Offset::betweenBasicKeys() != key.size())\n    return;\n\n  uint8_t *dest = _data + Offset::basicKeys() + n*Offset::betweenBasicKeys();\n  for (unsigned i=0,j=(Offset::betweenBasicKeys()-1); i<Offset::betweenBasicKeys(); i++,j--)\n    dest[i] = key[j];\n}\n\nbool\nTyTCodeplug::EncryptionElement::fromCommercialExt(CommercialExtension *encr, Context &ctx) {\n  Q_UNUSED(encr)\n  // Clear all keys\n  clear();\n\n  // Encode each key type separately\n  for (unsigned int i=0; i<ctx.count<BasicEncryptionKey>() && i<Limit::basicKeys(); i++)\n    setBasicKey(i, ctx.get<BasicEncryptionKey>(i)->key());\n  for (unsigned int i=0; i<ctx.count<AESEncryptionKey>() && i<Limit::advancedKeys(); i++)\n    setEnhancedKey(i, ctx.get<AESEncryptionKey>(i)->key());\n\n  return true;\n}\n\nbool\nTyTCodeplug::EncryptionElement::updateCommercialExt(Context &ctx) {\n  CommercialExtension *ext = ctx.config()->commercialExtension();\n\n  for (unsigned i=0; i<Limit::advancedKeys(); i++) {\n    if (! isEnhancedKeySet(i))\n      continue;\n    AESEncryptionKey *key = new AESEncryptionKey();\n    key->setName(QString(\"Enhanced Key %1\").arg(i));\n    ctx.add(key,i); // 0-based key indices\n    key->fromHex(enhancedKey(i).toHex());\n    ext->encryptionKeys()->add(key);\n  }\n\n  for (unsigned i=0; i<Limit::basicKeys(); i++) {\n    if (! isBasicKeySet(i))\n      continue;\n    BasicEncryptionKey *key = new BasicEncryptionKey();\n    key->setName(QString(\"Basic Key %1\").arg(i));\n    ctx.add(key,i); // 0-based key indices\n    key->fromHex(basicKey(i).toHex());\n    ext->encryptionKeys()->add(key);\n  }\n\n  return ext;\n}\n\nbool\nTyTCodeplug::EncryptionElement::linkCommercialExt(CommercialExtension *ext, Context &ctx) {\n  Q_UNUSED(ext); Q_UNUSED(ctx);\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::MessageElement\n * ******************************************************************************************** */\nTyTCodeplug::MessageElement::MessageElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::MessageElement::MessageElement(uint8_t *ptr)\n  : Element(ptr, MessageElement::size())\n{\n  // pass...\n}\n\nvoid\nTyTCodeplug::MessageElement::clear() {\n  memset(_data, 0xffff, size());\n}\n\nbool\nTyTCodeplug::MessageElement::isValid() const {\n  return Element::isValid() && (0xffff != getUInt16_le(0)) && (0x0000 != getUInt16_le(0));\n}\n\nQString\nTyTCodeplug::MessageElement::text() const {\n  return readUnicode(0, Limit::length(), 0x0000);\n}\nvoid\nTyTCodeplug::MessageElement::setText(const QString &text) {\n  writeUnicode(0, text, Limit::length(), 0x0000);\n}\n\nbool\nTyTCodeplug::MessageElement::encode(SMSTemplate *sms, const ErrorStack &err) {\n  Q_UNUSED(err);\n\n  setText(sms->message());\n\n  return true;\n}\n\nSMSTemplate *\nTyTCodeplug::MessageElement::decode(const ErrorStack &err) {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid SMS message.\";\n    return nullptr;\n  }\n\n  SMSTemplate *sms = new SMSTemplate();\n  sms->setName(\"Message\");\n  sms->setMessage(text());\n  return sms;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug::MessageBankElement\n * ******************************************************************************************** */\nTyTCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr, size_t size)\n  : Element(ptr, size)\n{\n  // pass...\n}\n\nTyTCodeplug::MessageBankElement::MessageBankElement(uint8_t *ptr)\n  : Element(ptr, MessageBankElement::size())\n{\n  // pass...\n}\n\nvoid\nTyTCodeplug::MessageBankElement::clear() {\n  for (unsigned int i=0; i<Limit::messages(); i++)\n    message(i).clear();\n}\n\nTyTCodeplug::MessageElement\nTyTCodeplug::MessageBankElement::message(unsigned int i) const {\n  i = std::min(i, Limit::messages()-1);\n  return _data + i*MessageElement::size();\n}\n\nbool\nTyTCodeplug::MessageBankElement::encode(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  Q_UNUSED(flags);\n\n  unsigned int count = ctx.config()->smsExtension()->smsTemplates()->count();\n  count = std::min(count, Limit::messages());\n  for (unsigned int i=0; i<count; i++) {\n    if (! message(i).encode(ctx.config()->smsExtension()->smsTemplates()->message(i), err)) {\n      errMsg(err) << \"Cannot encode\" << i+1 << \"-th preset message.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nTyTCodeplug::MessageBankElement::decode(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::messages(); i++) {\n    MessageElement msg = message(i);\n    if (!msg.isValid())\n      continue;\n    SMSTemplate *sms = msg.decode(err);\n    if (nullptr == sms) {\n      errMsg(err) << \"Cannot decode \" << i+1 << \"-th preset message.\";\n      return false;\n    }\n    ctx.config()->smsExtension()->smsTemplates()->add(sms);\n  }\n  return true;\n}\n\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTCodeplug\n * ******************************************************************************************** */\nTyTCodeplug::TyTCodeplug(QObject *parent)\n  : Codeplug(parent)\n{\n  // pass...\n}\n\nTyTCodeplug::~TyTCodeplug() {\n  // pass...\n}\n\nbool\nTyTCodeplug::index(Config *config, Context &ctx, const ErrorStack &err) const {\n  Q_UNUSED(err)\n\n  // All indices as 1-based. That is, the first channel gets index 1.\n\n  // Map radio IDs\n  for (int i=0; i<config->radioIDs()->count(); i++) {\n    if (config->radioIDs()->get(i)->is<DMRRadioID>())\n      ctx.add(config->radioIDs()->get(i)->as<DMRRadioID>(), i+1);\n  }\n\n  // Map digital and DTMF contacts\n  for (int i=0, d=0, a=0; i<config->contacts()->count(); i++) {\n    Contact *contact = config->contacts()->contact(i);\n    if (contact->is<DMRContact>()) {\n      ctx.add(contact->as<DMRContact>(), d+1); d++;\n    } else if (contact->is<DTMFContact>()) {\n      ctx.add(contact->as<DTMFContact>(), a+1); a++;\n    } else {\n      logInfo() << \"Cannot index contact '\" << contact->name()\n                << \"'. Contact type '\" << contact->metaObject()->className()\n                << \"' not supported by or implemented for TyT devices.\";\n    }\n  }\n\n  // Map rx group lists\n  for (int i=0; i<config->rxGroupLists()->count(); i++)\n    ctx.add(config->rxGroupLists()->list(i), i+1);\n\n  // Map channels\n  for (int i=0, c=0; i<config->channelList()->count(); i++) {\n    Channel *channel = config->channelList()->channel(i);\n    if (channel->is<FMChannel>() || channel->is<DMRChannel>()) {\n      ctx.add(channel, c+1); c++;\n    } else {\n      logInfo() << \"Cannot index channel '\" << channel->name()\n                << \"'. Channel type '\" << channel->metaObject()->className()\n                << \"' not supported by or implemented for TyT devices.\";\n    }\n  }\n\n  // Map zones\n  for (int i=0; i<config->zones()->count(); i++)\n    ctx.add(config->zones()->zone(i), i+1);\n\n  // Map scan lists\n  for (int i=0; i<config->scanlists()->count(); i++)\n    ctx.add(config->scanlists()->scanlist(i), i+1);\n\n  // Map DMR APRS systems\n  for (int i=0,a=0,d=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<DMRAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<DMRAPRSSystem>(), d+1); d++;\n    } else if (config->posSystems()->system(i)->is<FMAPRSSystem>()) {\n      ctx.add(config->posSystems()->system(i)->as<FMAPRSSystem>(), a+1); a++;\n    }\n  }\n\n  // Map roaming\n  for (int i=0; i<config->roamingZones()->count(); i++)\n    ctx.add(config->roamingZones()->zone(i), i+1);\n\n  // Index basic (DMR) and AES keys\n  if (CommercialExtension *ext = config->commercialExtension()) {\n    unsigned int basicIndex = 0, aesIndex = 0;\n    for (int i=0; i<ext->encryptionKeys()->count(); i++) {\n      if (ext->encryptionKeys()->key(i)->is<BasicEncryptionKey>()) {\n        ctx.add(ext->encryptionKeys()->key(i)->as<BasicEncryptionKey>(), basicIndex++);\n      } else if (ext->encryptionKeys()->key(i)->is<AESEncryptionKey>()) {\n        ctx.add(ext->encryptionKeys()->key(i)->as<AESEncryptionKey>(), aesIndex++);\n      }\n    }\n  }\n\n  return true;\n}\n\nvoid\nTyTCodeplug::clear()\n{\n  // Clear timestamp\n  this->clearTimestamp();\n  // Clear general config\n  this->clearGeneralSettings();\n  // Clear menu settings\n  this->clearMenuSettings();\n  // Clear button settings\n  this->clearButtonSettings();\n  // Clear text messages\n  this->clearTextMessages();\n  // Clear privacy keys\n  this->clearPrivacyKeys();\n  // Clear emergency systems\n  this->clearEmergencySystems();\n  // Clear RX group lists\n  this->clearGroupLists();\n  // Clear zones & zone extensions\n  this->clearZones();\n  // Clear scan lists;\n  this->clearScanLists();\n  // Clear GPS systems\n  this->clearPositioningSystems();\n  // Clear channels\n  this->clearChannels();\n  // Clear contacts\n  this->clearContacts();\n}\n\n\nConfig *\nTyTCodeplug::preprocess(Config *config, const ErrorStack &err) const {\n  Config *intermediate = Codeplug::preprocess(config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot pre-process codeplug for TyT device.\";\n    return nullptr;\n  }\n\n  // Remove all AM & M17 channels\n  ObjectFilterVisitor amFilter{AMChannel::staticMetaObject, M17Channel::staticMetaObject};\n  if (! amFilter.process(intermediate, err)) {\n    errMsg(err) << \"Remove AM & M17 channels.\";\n    delete intermediate;\n    return nullptr;\n  }\n\n  return intermediate;\n}\n\n\nbool\nTyTCodeplug::encode(Config *config, const Flags &flags, const ErrorStack &err) {\n  // Check if default DMR id is set.\n  if (config->settings()->defaultIdRef()->isNull()) {\n    errMsg(err) << \"Cannot encode TyT codeplug: No default radio ID specified.\";\n    return false;\n  }\n\n  // Create index<->object table.\n  Context ctx(config);\n\n  if (! index(config, ctx))\n    return false;\n\n  return this->encodeElements(flags, ctx);\n}\n\nbool\nTyTCodeplug::decode(Config *config, const ErrorStack &err) {\n  // Create index<->object table.\n  Context ctx(config);\n\n  // Clear config object\n  config->clear();\n\n  return this->decodeElements(ctx, err);\n}\n\nbool\nTyTCodeplug::encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err)\n{\n  // Set timestamp\n  if (! this->encodeTimestamp()) {\n    errMsg(err) << \"Cannot encode time-stamp.\";\n    return false;\n  }\n  // General config\n  if (! this->encodeGeneralSettings(flags, ctx)) {\n    errMsg(err) << \"Cannot encode general settings.\";\n    return false;\n  }\n\n  // Define Contacts\n  if (! this->encodeContacts(flags, ctx)) {\n    errMsg(err) << \"Cannot encode contacts.\";\n    return false;\n  }\n\n  // Define RX GroupLists\n  if (! this->encodeGroupLists(flags, ctx)) {\n    errMsg(err) << \"Cannot encode group lists.\";\n    return false;\n  }\n\n  // Define encryption keys\n  if (! this->encodePrivacyKeys(flags, ctx)) {\n    errMsg(err) << \"Cannot encode encryption keys.\";\n    return false;\n  }\n\n  // Define Channels\n  if (! this->encodeChannels(flags, ctx)) {\n    errMsg(err) << \"Cannot encode channels.\";\n    return false;\n  }\n\n  // Define Zones\n  if (! this->encodeZones(flags, ctx)) {\n    errMsg(err) << \"Cannot encode zones.\";\n    return false;\n  }\n\n  // Define Scanlists\n  if (! this->encodeScanLists(flags, ctx)) {\n    errMsg(err) << \"Cannot encode scan lists.\";\n    return false;\n  }\n\n  // Define GPS systems\n  if (! this->encodePositioningSystems(flags, ctx)) {\n    errMsg(err) << \"Cannot encode positioning systems.\";\n    return false;\n  }\n\n  // Encode button settings\n  if (! this->encodeButtonSettings(flags, ctx)) {\n    errMsg(err) << \"Cannot encode button settings.\";\n    return false;\n  }\n\n  // Encode text messages\n  if (! this->encodeTextMessages(ctx, flags, err)) {\n    errMsg(err) << \"Cannot encode text messages.\";\n    return false;\n  }\n\n  return true;\n}\n\nbool\nTyTCodeplug::decodeElements(Context &ctx, const ErrorStack &err) {\n  // General config\n  if (! this->decodeGeneralSettings(ctx, err)) {\n    errMsg(err) << \"Cannot decode general settings.\";\n    return false;\n  }\n\n  // Define Contacts\n  if (! this->createContacts(ctx, err)) {\n    errMsg(err) << \"Cannot create contacts.\";\n    return false;\n  }\n\n  // Define RX GroupLists\n  if (! this->createGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot create group lists.\";\n    return false;\n  }\n\n  // Define Channels\n  if (! this->createChannels(ctx, err)) {\n    errMsg(err) << \"Cannot create channels.\";\n    return false;\n  }\n\n  // Define Zones\n  if (! this->createZones(ctx, err)) {\n    errMsg(err) << \"Cannot create zones.\";\n    return false;\n  }\n\n  // Define Scanlists\n  if (! this->createScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot create scan lists.\";\n    return false;\n  }\n\n  // Define GPS systems\n  if (! this->createPositioningSystems(ctx, err)) {\n    errMsg(err) << \"Cannot create positioning systems.\";\n    return false;\n  }\n\n  // Decode button settings\n  if (! this->decodeButtonSetttings(ctx, err)) {\n    errMsg(err) << \"Cannot decode button settings.\";\n    return false;\n  }\n\n  // Decode text messages\n  if (! this->decodeTextMessages(ctx, err)) {\n    errMsg(err) << \"Cannot decode text messages.\";\n    return false;\n  }\n\n  // Decode encryption settings\n  if (! this->decodePrivacyKeys(ctx, err)) {\n    errMsg(err) << \"Cannot decode encryption settings.\";\n    return false;\n  }\n\n  // Link RX GroupLists\n  if (! this->linkGroupLists(ctx, err)) {\n    errMsg(err) << \"Cannot link group lists.\";\n    return false;\n  }\n\n  // Link Channels\n  if (! this->linkChannels(ctx, err)) {\n    errMsg(err) << \"Cannot link channels.\";\n    return false;\n  }\n\n  // Link Zones\n  if (! this->linkZones(ctx, err)) {\n    errMsg(err) << \"Cannot link zones.\";\n    return false;\n  }\n\n  // Link Scanlists\n  if (! this->linkScanLists(ctx, err)) {\n    errMsg(err) << \"Cannot link scan lists.\";\n    return false;\n  }\n\n  // Link GPS systems\n  if (! this->linkPositioningSystems(ctx, err)) {\n    errMsg(err) << \"Cannot link positioning systems.\";\n    return false;\n  }\n\n  return true;\n}\n\n"
  },
  {
    "path": "lib/tyt_codeplug.hh",
    "content": "#ifndef TYT_CODEPLUG_HH\n#define TYT_CODEPLUG_HH\n\n#include <QDateTime>\n\n#include \"codeplug.hh\"\n#include \"signaling.hh\"\n#include \"channel.hh\"\n#include \"contact.hh\"\n#include \"bootsettings.hh\"\n#include \"tyt_extensions.hh\"\n\nclass DMRContact;\nclass Zone;\nclass RXGroupList;\nclass ScanList;\nclass DMRAPRSSystem;\nclass SMSExtension;\nclass SMSTemplate;\n\n/** Base class of all TyT codeplugs. This class implements the majority of all codeplug elements\n * present in all TyT codeplugs. This eases the support of several TyT radios, as only the\n * differences in the codeplug to this base class must be implemented.\n *\n * @ingroup tyt */\nclass TyTCodeplug : public Codeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Represents a single channel (analog or digital) within the TyT codeplug.\n   *\n   * Memory layout of encoded channel:\n   * @verbinclude tyt_channel.txt */\n  class ChannelElement: public Codeplug::Element\n  {\n  public:\n    /** Possible modes for the channel, i.e. analog and digital. */\n    enum Mode {\n      MODE_ANALOG  = 1,             ///< Analog channel.\n      MODE_DIGITAL = 2              ///< Digital channel.\n    };\n\n    /** Bandwidth of the channel. */\n    enum Bandwidth {\n      BW_12_5_KHZ = 0,              ///< 12.5 kHz narrow, (default for binary channels).\n      BW_20_KHZ   = 1,              ///< 20 kHz (really?)\n      BW_25_KHZ   = 2               ///< 25kHz wide.\n    };\n\n    /** Possible privacy types. */\n    enum PrivacyType {\n      PRIV_NONE = 0,                ///< No privacy.\n      PRIV_BASIC = 1,               ///< Basic privacy.\n      PRIV_ENHANCED = 2             ///< Enhanced privacy.\n    };\n\n    /** TX Admit criterion. */\n    enum Admit {\n      ADMIT_ALWAYS = 0,             ///< Always allow TX.\n      ADMIT_CH_FREE = 1,            ///< Allow TX if channel is free.\n      ADMIT_TONE = 2,               ///< Allow TX if CTCSS tone matches.\n      ADMIT_COLOR = 3,              ///< Allow TX if color-code matches.\n    };\n\n  protected:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ChannelElement();\n\n    /** Returns @c true if channel is valid/enabled. */\n    bool isValid() const;\n    /** Clears/resets the channel and therefore disables it. */\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x0040; }\n\n    /** Returns the mode of the channel. */\n    virtual Mode mode() const;\n    /** Sets the mode of the channel. */\n    virtual void setMode(Mode setMode);\n\n    /** Returns the bandwidth of the (analog) channel. */\n    virtual FMChannel::Bandwidth bandwidth() const;\n    /** Sets the bandwidth of the (analog) channel. */\n    virtual void setBandwidth(FMChannel::Bandwidth bw);\n\n    /** Returns @c true if the channel has auto scan enabled. */\n    virtual bool autoScan() const;\n    /** Enables/disables auto scan for this channel. */\n    virtual void enableAutoScan(bool enable);\n\n    /** Returns @c true if the channel has lone worker enabled. */\n    virtual bool loneWorker() const;\n    /** Enables/disables lone worker for this channel. */\n    virtual void enableLoneWorker(bool enable);\n\n    /** Returns @c true if the channel has talkaround enabled. */\n    virtual bool talkaround() const;\n    /** Enables/disables talkaround for this channel. */\n    virtual void enableTalkaround(bool enable);\n\n    /** Returns @c true if the channel has rx only enabled. */\n    virtual bool rxOnly() const;\n    /** Enables/disables rx only for this channel. */\n    virtual void enableRXOnly(bool enable);\n\n    /** Returns the time slot of this channel. */\n    virtual DMRChannel::TimeSlot timeSlot() const;\n    /** Sets the time slot of this channel. */\n    virtual void setTimeSlot(DMRChannel::TimeSlot ts);\n\n    /** Returns the color code of this channel. */\n    virtual uint8_t colorCode() const;\n    /** Sets the color code of this channel. */\n    virtual void setColorCode(uint8_t ts);\n\n    /** Returns the index of the privacy system (key). */\n    virtual uint8_t privacyIndex() const;\n    /** Sets the index of the privacy system (key). */\n    virtual void setPrivacyIndex(uint8_t ts);\n    /** Returns the type of the privacy system. */\n    virtual PrivacyType privacyType() const;\n    /** Sets the type of the privacy system. */\n    virtual void setPrivacyType(PrivacyType type);\n\n    /** Returns @c true if the channel has private call confirmation enabled. */\n    virtual bool privateCallConfirm() const;\n    /** Enables/disables private call confirmation for this channel. */\n    virtual void enablePrivateCallConfirm(bool enable);\n\n    /** Returns @c true if the channel has data call confirmation enabled. */\n    virtual bool dataCallConfirm() const;\n    /** Enables/disables data call confirmation for this channel. */\n    virtual void enableDataCallConfirm(bool enable);\n\n    /** Returns some weird reference frequency setting for reception. */\n    virtual TyTChannelExtension::RefFrequency rxRefFrequency() const;\n    /** Sets some weird reference frequency setting for reception. */\n    virtual void setRXRefFrequency(TyTChannelExtension::RefFrequency ref);\n    /** Returns some weird reference frequency setting for transmission. */\n    virtual TyTChannelExtension::RefFrequency txRefFrequency() const;\n    /** Sets some weird reference frequency setting for transmission. */\n    virtual void setTXRefFrequency(TyTChannelExtension::RefFrequency ref);\n\n    /** Returns @c true if the channel has alarm confirmation enabled. */\n    virtual bool emergencyAlarmACK() const;\n    /** Enables/disables alarm confirmation for this channel. */\n    virtual void enableEmergencyAlarmACK(bool enable);\n\n    /** Returns @c true if the channel has display PTT ID enabled. */\n    virtual bool displayPTTId() const;\n    /** Enables/disables PTT ID display for this channel. */\n    virtual void enableDisplayPTTId(bool enable);\n\n    /** Returns @c true if the channel has VOX enabled. */\n    virtual bool vox() const;\n    /** Enables/disables VOX for this channel. */\n    virtual void enableVOX(bool enable);\n\n    /** Returns the admit criterion for this channel. */\n    virtual Admit admitCriterion() const;\n    /** Sets the admit criterion for this channel. */\n    virtual void setAdmitCriterion(Admit admit);\n\n    /** Returns the transmit contact index (+1) for this channel. */\n    virtual uint16_t contactIndex() const;\n    /** Sets the transmit contact index (+1) for this channel. */\n    virtual void setContactIndex(uint16_t idx);\n\n    /** Returns @c true, if the transmit time out is disabled. */\n    virtual bool txTimeOutDisabled() const;\n    /** Returns the transmit time-out in seconds. */\n    virtual Interval txTimeOut() const;\n    /** Sets the transmit time-out in seconds. */\n    virtual void setTXTimeOut(const Interval &tot);\n    /** Disables the transmit timeout. */\n    virtual void resetTXTimeOut();\n    /** Returns the transmit time-out re-key delay in seconds. */\n    virtual uint8_t txTimeOutRekeyDelay() const;\n    /** Sets the transmit time-out re-key delay in seconds. */\n    virtual void setTXTimeOutRekeyDelay(uint8_t delay);\n\n    /** Returns the emergency system index (+1) for this channel. */\n    virtual uint8_t emergencySystemIndex() const;\n    /** Sets the emergency system index (+1) for this channel. */\n    virtual void setEmergencySystemIndex(uint8_t idx);\n\n    /** Returns the scan-list index (+1) for this channel. */\n    virtual uint8_t scanListIndex() const;\n    /** Sets the scan-list index (+1) for this channel. */\n    virtual void setScanListIndex(uint8_t idx);\n\n    /** Returns the RX group list index (+1) for this channel. */\n    virtual uint8_t groupListIndex() const;\n    /** Sets the RX group list index (+1) for this channel. */\n    virtual void setGroupListIndex(uint8_t idx);\n\n    /** Returns the positioning system index (+1) for this channel. */\n    virtual uint8_t positioningSystemIndex() const;\n    /** Sets the positioning system index (+1) for this channel. */\n    virtual void setPositioningSystemIndex(uint8_t idx);\n\n    /** Returns @c true if the channel has DTMF decoding enabled. */\n    virtual bool dtmfDecode(uint8_t idx) const;\n    /** Enables/disables DTMF decoding this channel. */\n    virtual void setDTMFDecode(uint8_t idx, bool enable);\n\n    /** Returns the RX frequency in Hz. */\n    virtual Frequency rxFrequency() const;\n    /** Sets the RX frequency in Hz. */\n    virtual void setRXFrequency(const Frequency &Hz);\n    /** Returns the TX frequency in Hz. */\n    virtual Frequency txFrequency() const;\n    /** Sets the TX frequency in Hz. */\n    virtual void setTXFrequency(const Frequency &Hz);\n\n    /** Returns the CTCSS/DSC signaling for RX. */\n    virtual SelectiveCall rxSignaling() const;\n    /** Sets the CTCSS/DSC signaling for RX. */\n    virtual void setRXSignaling(const SelectiveCall &code);\n    /** Returns the CTCSS/DSC signaling for TX. */\n    virtual SelectiveCall txSignaling() const;\n    /** Sets the CTCSS/DSC signaling for TX. */\n    virtual void setTXSignaling(const SelectiveCall &code);\n    /** Returns the signaling system index (+1) for RX. */\n    virtual uint8_t rxSignalingSystemIndex() const;\n    /** Sets the signaling system index (+1) for RX. */\n    virtual void setRXSignalingSystemIndex(uint8_t idx);\n    /** Returns the signaling system index (+1) for TX. */\n    virtual uint8_t txSignalingSystemIndex() const;\n    /** Sets the signaling system index (+1) for TX. */\n    virtual void setTXSignalingSystemIndex(uint8_t idx);\n\n    /** Returns @c true if the channel transmits GPS information enabled. */\n    virtual bool txGPSInfo() const;\n    /** Enables/disables transmission of GPS information for this channel. */\n    virtual void enableTXGPSInfo(bool enable);\n    /** Returns @c true if the channel receives GPS information enabled. */\n    virtual bool rxGPSInfo() const;\n    /** Enables/disables reception of GPS information for this channel. */\n    virtual void enableRXGPSInfo(bool enable);\n\n    /** Returns the name of this channel. */\n    virtual QString name() const;\n    /** Sets the name of this channel. */\n    virtual void setName(const QString &setName);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const;\n    /** Links a previously constructed channel to the rest of the configuration. */\n    virtual bool linkChannelObj(Channel *c, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual void fromChannelObj(const Channel *c, Context &ctx);\n\n  public:\n    /** Some limits of the element. */\n    struct Limit: public Element::Limit {\n      /** Maximum length of the name. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some internal offsets within the element. */\n    struct Offset: public Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Offset::Bit mode() { return {0x0000, 0}; }\n      static constexpr Offset::Bit bandwidth() { return {0x0000, 2}; }\n      static constexpr Offset::Bit autoscan() { return {0x0000, 4}; }\n      static constexpr Offset::Bit loneworker() { return {0x0000, 8}; }\n      static constexpr Offset::Bit talkaround() { return {0x0001, 0}; }\n      static constexpr Offset::Bit rxonly() { return {0x0001, 1}; }\n      static constexpr Offset::Bit timeslot() { return {0x0001, 2}; }\n      static constexpr Offset::Bit colorcode() { return {0x0001, 4}; }\n      static constexpr Offset::Bit privacyIndex() { return {0x0002, 0}; }\n      static constexpr Offset::Bit privacyType() { return {0x0002, 4}; }\n      static constexpr Offset::Bit privateCallConfirm() { return {0x0002, 6}; }\n      static constexpr Offset::Bit dataCallConfirm() { return {0x0002, 7}; }\n      static constexpr Offset::Bit rxRefFrequency() { return {0x0003, 0}; }\n      static constexpr Offset::Bit emergencyAlarmACK() { return {0x0003, 3}; }\n      static constexpr Offset::Bit displayPTTId() { return {0x0003, 7}; }\n      static constexpr Offset::Bit txRefFrequency() { return {0x0004, 0}; }\n      static constexpr Offset::Bit vox() { return {0x0004, 4}; }\n      static constexpr Offset::Bit admitCriterion() { return {0x0004, 6}; }\n      static constexpr unsigned int contactIndex() { return 0x0006; }\n      static constexpr Offset::Bit txTimeOut() { return {0x0008, 0}; }\n      static constexpr unsigned int txTimeOutRekeyDelay() { return 0x0009; }\n      static constexpr unsigned int emergencySystemIndex() { return 0x000a; }\n      static constexpr unsigned int scanListIndex() { return 0x000b; }\n      static constexpr unsigned int groupListIndex() { return 0x000c; }\n      static constexpr unsigned int positioningSystemIndex() { return 0x000d; }\n      static constexpr unsigned int dtmfDecode() { return 0x000e; }\n      static constexpr unsigned int rxFrequency() { return 0x0010; }\n      static constexpr unsigned int txFrequency() { return 0x0014; }\n      static constexpr unsigned int rxSignaling() { return 0x018; }\n      static constexpr unsigned int txSignaling() { return 0x01a; }\n      static constexpr unsigned int rxSignalingSystemIndex() { return 0x01c; }\n      static constexpr unsigned int txSignalingSystemIndex() { return 0x01d; }\n      static constexpr Offset::Bit txGPSInfo() { return {0x001f, 0}; }\n      static constexpr Offset::Bit rxGPSInfo() { return {0x001f, 1}; }\n      static constexpr unsigned int name() { return 0x0020; }\n      /// @endcond\n    };\n  };\n\n  /** Represents a digital (DMR) contact within the codeplug.\n   *\n   * Memory layout of encoded contact:\n   * @verbinclude tyt_contact.txt */\n  class ContactElement: public Codeplug::Element\n  {\n  protected:\n    /** Encoded call types. */\n    enum class CallType {\n      GroupCall=1, PrivateCall=2, AllCall=3\n    };\n\n  protected:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ContactElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ContactElement();\n\n    /** Size of the element. */\n    static constexpr unsigned int size() { return 0x000024; }\n    void clear();\n    bool isValid() const;\n\n    /** Returns the DMR ID of the contact. */\n    virtual uint32_t dmrId() const;\n    /** Sets the DMR ID of the contact. */\n    virtual void setDMRId(uint32_t id);\n\n    /** Returns the call-type of the contact. */\n    virtual DMRContact::Type callType() const;\n    /** Sets the call-type of the contact. */\n    virtual void setCallType(DMRContact::Type type);\n\n    /** Returns @c true if the ring-tone is enabled for this contact. */\n    virtual bool ringTone() const;\n    /** Enables/disables the ring-tone for this contact. */\n    virtual void enableRingTone(bool enable);\n\n    /** Returns the name of the contact. */\n    virtual QString name() const;\n    /** Sets the name of the contact. */\n    virtual void setName(const QString &nm);\n\n    /** Encodes the give contact. */\n    virtual bool fromContactObj(const DMRContact *contact);\n    /** Creates a contact. */\n    virtual DMRContact *toContactObj() const;\n\n  public:\n    /** Some limits. */\n    struct Limit: Element::Limit {\n      /** Maximum name length. */\n      static constexpr unsigned int nameLength() { return 16; }\n    };\n\n  protected:\n    /** Some offsets within the element. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int dmrId() { return 0x0000; }\n      static constexpr Bit callType()       { return {0x0003, 0}; }\n      static constexpr Bit ringTone()       { return {0x0003, 5}; }\n      static constexpr unsigned int name()  { return 0x0004; }\n      /// @endcond DO_NOT_DOCUMENT\n    };\n  };\n\n  /** Represents a zone within the codeplug.\n   * Please note that a zone consists of two elements the @c ZoneElement and the @c ZoneExtElement.\n   * The latter adds additional channels for VFO A and the channels for VFO B.\n   *\n   * Memory layout of encoded zone:\n   * @verbinclude tyt_zone.txt */\n  class ZoneElement: public Codeplug::Element\n  {\n  protected:\n    /** Constructor. */\n    ZoneElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ZoneElement(uint8_t *ptr);\n    /** Desturctor. */\n    virtual ~ZoneElement();\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the name of the zone. */\n    virtual QString name() const;\n    /** Sets the name of the zone. */\n    virtual void setName(const QString &setName);\n\n    /** Returns @c true if the n-th member index is set. */\n    virtual bool hasMemberIndex(unsigned n) const;\n    /** Returns the index (+1) of the @c n-th member. */\n    virtual uint16_t memberIndex(unsigned n) const;\n    /** Sets the index (+1) of the @c n-th member. */\n    virtual void setMemberIndex(unsigned n, uint16_t idx);\n\n    /** Encodes a given zone object. */\n    virtual bool fromZoneObj(const Zone *zone, Context &ctx);\n    /** Creates a zone. */\n    virtual Zone *toZoneObj() const;\n    /** Links the created zone to channels. */\n    virtual bool linkZone(Zone *zone, Context &ctx) const;\n  };\n\n  /** Representation of an RX group list within the codeplug.\n   *\n   * Memory layout of encoded RX group list:\n   * @verbinclude tyt_grouplist.txt */\n  class GroupListElement: public Codeplug::Element\n  {\n  protected:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GroupListElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GroupListElement();\n\n    void clear();\n    bool isValid() const;\n\n    /** Returns the name of the group list. */\n    virtual QString name() const;\n    /** Sets the name of the group list. */\n    virtual void setName(const QString &nm);\n\n    /** Returns the n-th member index. */\n    virtual uint16_t memberIndex(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMemberIndex(unsigned n, uint16_t idx);\n\n    /** Encodes the given group list. */\n    virtual bool fromGroupListObj(const RXGroupList *lst, Context &ctx);\n    /** Creates a group list object. */\n    virtual RXGroupList *toGroupListObj(Context &ctx);\n    /** Links the given group list. */\n    virtual bool linkGroupListObj(RXGroupList *lst, Context &ctx);\n  };\n\n  /** Represents a scan list within the codeplug.\n   *\n   * Memory layout of encoded scan list (0x0068 bytes):\n   * @verbinclude tyt_scanlist.txt */\n  class ScanListElement: public Codeplug::Element\n  {\n  protected:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    ScanListElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ScanListElement();\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the name of the scan list. */\n    virtual QString name() const;\n    /** Sets the name of the scan list. */\n    virtual void setName(const QString &nm);\n\n    /** Returns the index (+1) of the first priority channel. */\n    virtual uint16_t priorityChannel1Index() const;\n    /** Set the index (+1) of the first priority channel. */\n    virtual void setPriorityChannel1Index(uint16_t idx);\n\n    /** Returns the index (+1) of the second priority channel. */\n    virtual uint16_t priorityChannel2Index() const;\n    /** Set the index (+1) of the second priority channel. */\n    virtual void setPriorityChannel2Index(uint16_t idx);\n\n    /** Returns the index (+1) of the TX channel. 0=current, 0xffff=none. */\n    virtual uint16_t txChannelIndex() const;\n    /** Sets the index (+1) of the TX channel. 0=current, 0xffff=none. */\n    virtual void setTXChannelIndex(uint16_t idx);\n\n    /** Returns the hold time in ms. */\n    virtual unsigned holdTime() const;\n    /** Sets the hold time in ms. */\n    virtual void setHoldTime(unsigned time);\n\n    /** Returns the priority sample time in ms. */\n    virtual unsigned prioritySampleTime() const;\n    /** Sets the priority sample time in ms. */\n    virtual void setPrioritySampleTime(unsigned time);\n\n    /** Returns the n-th member index. */\n    virtual uint16_t memberIndex(unsigned n) const;\n    /** Sets the n-th member index. */\n    virtual void setMemberIndex(unsigned n, uint16_t idx);\n\n    /** Encodes the given scan list. */\n    virtual bool fromScanListObj(const ScanList *lst, Context &ctx);\n    /** Creates a scan list. */\n    virtual ScanList *toScanListObj(Context &ctx);\n    /** Links the scan list object. */\n    virtual bool linkScanListObj(ScanList *lst, Context &ctx, const ErrorStack &err=ErrorStack());\n  };\n\n  /** Codeplug representation of the general settings.\n   *\n   * Memory layout of encoded settings:\n   * @verbinclude tyt_settings.txt */\n  class GeneralSettingsElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit GeneralSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GeneralSettingsElement();\n\n    void clear();\n\n    /** Returns the first intro line. */\n    virtual QString introLine1() const;\n    /** Sets the first intro line. */\n    virtual void setIntroLine1(const QString line);\n    /** Returns the second intro line. */\n    virtual QString introLine2() const;\n    /** Sets the second intro line. */\n    virtual void setIntroLine2(const QString line);\n\n    /** Returns the monitor type. */\n    virtual TyTSettingsExtension::MonitorType monitorType() const;\n    /** Sets the monitor type. */\n    virtual void setMonitorType(TyTSettingsExtension::MonitorType type);\n\n    /** Returns @c true if all LEDs are disabled. */\n    virtual bool allLEDsDisabled() const;\n    /** Enables/disables all LEDs. */\n    virtual void disableAllLEDs(bool disable);\n\n    /** Returns @c true, if save preamble is enabled. */\n    virtual bool savePreamble() const;\n    /** Enables/disables save preamble. */\n    virtual void setSavePreamble(bool enable);\n    /** Returns @c true, if save RX mode is enabled. */\n    virtual bool saveModeRX() const;\n    /** Enables/disables save mode RX. */\n    virtual void setSaveModeRX(bool enable);\n    /** Returns @c true, if all tones are disabled. */\n    virtual bool allTonesDisabled() const;\n    /** Enables/disables all tones. */\n    virtual void disableAllTones(bool disable);\n    /** Returns @c true, if the channel free indication tone is enabled. */\n    virtual bool chFreeIndicationTone() const;\n    /** Enables/disables the channel free indication tone. */\n    virtual void setChFreeIndicationTone(bool enable);\n    /** Returns @c true, if password and lock is enabled. */\n    virtual bool passwdAndLock() const;\n    /** Enables/disables password and lock. */\n    virtual void enablePasswdAndLock(bool enable);\n    /** Returns @c true, if the talk permit tone is enabled for DMR channels. */\n    virtual bool talkPermitToneDigital() const;\n    /** Enables/disables talk permit tone for DMR channels. */\n    virtual void enableTalkPermitToneDigital(bool enable);\n    /** Returns @c true, if the talk permit tone is enabled for analog channels. */\n    virtual bool talkPermitToneAnalog() const;\n    /** Enables/disables talk permit tone for analog channels. */\n    virtual void enableTalkPermitToneAnalog(bool enable);\n\n    /** Returns boot display mode. */\n    virtual BootSettings::BootDisplay bootDisplay() const;\n    /** Sets boot display mode. */\n    virtual void setBootDisplay(BootSettings::BootDisplay mode);\n\n    /** Returns the default DMR ID of the radio. */\n    virtual uint32_t dmrId() const;\n    /** Sets the default DMR ID of the radio. */\n    virtual void setDMRId(uint32_t id);\n\n    /** Returns the TX preamble duration. */\n    virtual Interval txPreambleDuration () const;\n    /** Sets the TX preamble duration. */\n    virtual void setTXPreambleDuration(const Interval &ms);\n    /** Returns the group call hang time. */\n    virtual Interval groupCallHangTime() const;\n    /** Sets the group call hang time. */\n    virtual void setGroupCallHangTime(const Interval &ms);\n    /** Returns the private call hang time. */\n    virtual Interval privateCallHangTime() const;\n    /** Sets the private call hang time. */\n    virtual void setPrivateCallHangTime(const Interval &ms);\n    /** Returns the VOX sensitivity. */\n    virtual Level voxSesitivity() const;\n    /** Sets the group call hang time. */\n    virtual void setVOXSesitivity(Level level);\n    /** Returns the low-battery warning interval. */\n    virtual unsigned lowBatteryInterval() const;\n    /** Sets the low-battery warning interval. */\n    virtual void setLowBatteryInterval(unsigned sec);\n\n    /** Returns @c true if the call-alert is continuous. */\n    virtual bool callAlertToneIsContinuous() const;\n    /** Returns the call-alert tone duration. */\n    virtual unsigned callAlertToneDuration() const;\n    /** Sets the call-alert tone duration. */\n    virtual void setCallAlertToneDuration(unsigned sec);\n    /** Sets the call-alert tone continuous. */\n    virtual void setCallAlertToneContinuous();\n\n    /** Returns the lone-worker response time. */\n    virtual unsigned loneWorkerResponseTime() const;\n    /** Sets the lone-worker response time. */\n    virtual void setLoneWorkerResponseTime(unsigned min);\n    /** Returns the lone-worker reminder time. */\n    virtual unsigned loneWorkerReminderTime() const;\n    /** Sets the lone-worker reminder time. */\n    virtual void setLoneWorkerReminderTime(unsigned min);\n    /** Returns the scan digital hang time. */\n    virtual unsigned scanDigitalHangTime() const;\n    /** Sets the scan digital hang time. */\n    virtual void setScanDigitalHangTime(unsigned ms);\n    /** Returns the scan analog hang time. */\n    virtual unsigned scanAnalogHangTime() const;\n    /** Sets the scan analog hang time. */\n    virtual void setScanAnalogHangTime(unsigned ms);\n\n    /** Returns @c true if the backlight is always on. */\n    virtual bool backlightIsAlways() const;\n    /** Returns the backlight time. */\n    virtual unsigned backlightTime() const;\n    /** Sets the backlight time. */\n    virtual void setBacklightTime(unsigned sec);\n    /** Turns the backlight always on. */\n    virtual void backlightTimeSetAlways();\n\n    /** Returns @c true if the keypad lock is manual. */\n    virtual bool keypadLockIsManual() const;\n    /** Returns the keypad lock time. */\n    virtual unsigned keypadLockTime() const;\n    /** Sets the keypad lock time. */\n    virtual void setKeypadLockTime(unsigned sec);\n    /** Set keypad lock to manual. */\n    virtual void keypadLockTimeSetManual();\n\n    /** Returns the 8-digit power-on password. */\n    virtual uint32_t powerOnPassword() const;\n    /** Sets the 8-digit power-on password. */\n    virtual void setPowerOnPassword(uint32_t passwd);\n\n    /** Returns @c true, if the radio programming password is enabled. */\n    virtual bool radioProgPasswordEnabled() const;\n    /** Returns the 8-digit radio programming password. */\n    virtual uint32_t radioProgPassword() const;\n    /** Sets the 8-digit radio programming password. */\n    virtual void setRadioProgPassword(uint32_t passwd);\n    /** Disables the radio programming password. */\n    virtual void radioProgPasswordDisable();\n\n    /** Returns @c true, if the PC programming password is enabled. */\n    virtual bool pcProgPasswordEnabled() const;\n    /** Returns the PC programming password. */\n    virtual QString pcProgPassword() const;\n    /** Sets the PC programming password. */\n    virtual void setPCProgPassword(const QString &pass);\n    /** Disables the PC programming password. */\n    virtual void pcProgPasswordDisable();\n\n    /** Returns the radio name. */\n    virtual QString radioName() const;\n    /** Sets the radio name. */\n    virtual void setRadioName(const QString &name);\n\n    /** Encodes the general settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from general settings. */\n    virtual bool updateConfig(Config *config);\n\n  public:\n    /** Some limits for the settings. */\n    struct Limit: Element::Limit {\n      // Valid VOX sensitivity range.\n      static constexpr Range<unsigned int> vox() { return {1,10}; }\n      static constexpr unsigned bootPasswordLength() { return 8; }\n    };\n\n  protected:\n    /** Internal offsets. */\n    struct Offset: Element::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit bootImageEnabled() { return {0x0042,4}; }\n      /// @endcond\n    };\n  };\n\n  /** Codeplug representation of programming time-stamp and CPS version.\n   *\n   * Memory layout of encoded timestamp:\n   * @verbinclude tyt_timestamp.txt */\n  class TimestampElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    TimestampElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit TimestampElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~TimestampElement();\n\n    void clear();\n\n    /** Returns the time stamp. */\n    virtual QDateTime timestamp() const;\n    /** Sets the time stamp. */\n    virtual void setTimestamp(const QDateTime &ts);\n\n    /** Returns the CPS version. */\n    virtual QString cpsVersion() const;\n  };\n\n  /** Represents a single GPS system within the codeplug.\n   *\n   * Memory layout of encoded GPS system (size 0x0010 bytes):\n   * @verbinclude tyt_gpssystem.txt */\n  class GPSSystemElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    GPSSystemElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit GPSSystemElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~GPSSystemElement();\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns @c true if the revert channel is the current one. */\n    virtual bool revertChannelIsSelected() const;\n    /** Returns the revert channel index (+1). */\n    virtual uint16_t revertChannelIndex() const;\n    /** Sets the revert channel index (+1). */\n    virtual void setRevertChannelIndex(uint16_t idx);\n    /** Sets the revert channel to the current one. */\n    virtual void setRevertChannelSelected();\n\n    /** Returns @c true if the repeat interval is disabled. */\n    virtual bool repeatIntervalDisabled() const;\n    /** Returns the repeat interval. */\n    virtual Interval repeatInterval() const;\n    /** Sets the repeat interval in seconds. */\n    virtual void setRepeatInterval(const Interval &sec);\n    /** Disables the GPS repeat interval. */\n    virtual void disableRepeatInterval();\n\n    /** Returns @c true if the destination contact is disabled. */\n    virtual bool destinationContactDisabled() const;\n    /** Returns the destination contact index (+1). */\n    virtual uint16_t destinationContactIndex() const;\n    /** Sets the destination contact index (+1). */\n    virtual void setDestinationContactIndex(uint16_t idx);\n    /** Disables the destination contact. */\n    virtual void disableDestinationContact();\n\n    /** Encodes the given GPS system. */\n    virtual bool fromGPSSystemObj(DMRAPRSSystem *sys, Context &ctx);\n    /** Constructs a GPS system. */\n    virtual DMRAPRSSystem *toGPSSystemObj();\n    /** Links the given GPS system. */\n    virtual bool linkGPSSystemObj(DMRAPRSSystem *sys, Context &ctx);\n  };\n\n  /** Represents all menu settings within the codeplug on the radio.\n   *\n   * Memory representation of the menu settings:\n   * @verbinclude tyt_menusettings.txt */\n  class MenuSettingsElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MenuSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit MenuSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~MenuSettingsElement();\n\n    void clear();\n\n    /** Returns @c true if the menu hang time is infinite. */\n    virtual bool menuHangtimeIsInfinite() const;\n    /** Returns the menu hang time in seconds. */\n    virtual unsigned menuHangtime() const;\n    /** Sets the menu hang time in seconds. */\n    virtual void setMenuHangtime(unsigned sec);\n    /** Sets the menu hang time to be infinite. */\n    virtual void infiniteMenuHangtime();\n\n    /** Returns @c true if text message menu is enabled. */\n    virtual bool textMessage() const;\n    /** Enables/disables text message menu. */\n    virtual void enableTextMessage(bool enable);\n    /** Returns @c true if call alert menu is enabled. */\n    virtual bool callAlert() const;\n    /** Enables/disables call alert menu. */\n    virtual void enableCallAlert(bool enable);\n    /** Returns @c true if contact editing is enabled. */\n    virtual bool contactEditing() const;\n    /** Enables/disables contact editing. */\n    virtual void enableContactEditing(bool enable);\n    /** Returns @c true if manual dial is enabled. */\n    virtual bool manualDial() const;\n    /** Enables/disables manual dial. */\n    virtual void enableManualDial(bool enable);\n    /** Returns @c true if contact radio-check menu is enabled. */\n    virtual bool remoteRadioCheck() const;\n    /** Enables/disables contact radio-check menu. */\n    virtual void enableRemoteRadioCheck(bool enable);\n    /** Returns @c true if remote monitor menu is enabled. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables remote monitor menu. */\n    virtual void enableRemoteMonitor(bool enable);\n    /** Returns @c true if radio enable menu is enabled. */\n    virtual bool remoteRadioEnable() const;\n    /** Enables/disables radio enable menu. */\n    virtual void enableRemoteRadioEnable(bool enable);\n    /** Returns @c true if radio disable menu is enabled. */\n    virtual bool remoteRadioDisable() const;\n    /** Enables/disables radio disable menu. */\n    virtual void enableRemoteRadioDisable(bool enable);\n\n    /** Returns @c true if scan menu is enabled. */\n    virtual bool scan() const;\n    /** Enables/disables scan menu. */\n    virtual void enableScan(bool enable);\n    /** Returns @c true if edit scan-list menu is enabled. */\n    virtual bool scanListEditing() const;\n    /** Enables/disables edit scan-list menu. */\n    virtual void enableScanListEditing(bool enable);\n    /** Returns @c true if call-log missed menu is enabled. */\n    virtual bool callLogMissed() const;\n    /** Enables/disables call-log missed menu. */\n    virtual void enableCallLogMissed(bool enable);\n    /** Returns @c true if call-log answered menu is enabled. */\n    virtual bool callLogAnswered() const;\n    /** Enables/disables call-log answered menu. */\n    virtual void enableCallLogAnswered(bool enable);\n    /** Returns @c true if call-log outgoing menu is enabled. */\n    virtual bool callLogOutgoing() const;\n    /** Enables/disables call-log outgoing menu. */\n    virtual void enableCallLogOutgoing(bool enable);\n    /** Returns @c true if talkaround menu is enabled. */\n    virtual bool talkaround() const;\n    /** Enables/disables talkaround menu. */\n    virtual void enableTalkaround(bool enable);\n    /** Returns @c true if tone/alert menu is enabled. */\n    virtual bool alertTone() const;\n    /** Enables/disables tone/alert  menu. */\n    virtual void enableAlertTone(bool enable);\n\n    /** Returns @c true if power menu is enabled. */\n    virtual bool power() const;\n    /** Enables/disables power menu. */\n    virtual void enablePower(bool enable);\n    /** Returns @c true if backlight menu is enabled. */\n    virtual bool backlight() const;\n    /** Enables/disables backlight menu. */\n    virtual void enableBacklight(bool enable);\n    /** Returns @c true if intro screen menu is enabled. */\n    virtual bool bootScreen() const;\n    /** Enables/disables intro screen menu. */\n    virtual void enableBootScreen(bool enable);\n    /** Returns @c true if keypad lock menu is enabled. */\n    virtual bool keypadLock() const;\n    /** Enables/disables keypad lock menu. */\n    virtual void enableKeypadLock(bool enable);\n    /** Returns @c true if LED indicator menu is enabled. */\n    virtual bool ledIndicator() const;\n    /** Enables/disables LED indicator menu. */\n    virtual void enableLEDIndicator(bool enable);\n    /** Returns @c true if squelch menu is enabled. */\n    virtual bool squelch() const;\n    /** Enables/disables squelch menu. */\n    virtual void enableSquelch(bool enable);\n    /** Returns @c true if VOX menu is enabled. */\n    virtual bool vox() const;\n    /** Enables/disables VOX menu. */\n    virtual void enableVOX(bool enable);\n\n    /** Returns @c true if password menu is enabled. */\n    virtual bool password() const;\n    /** Enables/disables password menu. */\n    virtual void enablePassword(bool enable);\n    /** Returns @c true if display mode menu is enabled. */\n    virtual bool displayMode() const;\n    /** Enables/disables display mode menu. */\n    virtual void enableDisplayMode(bool enable);\n    /** Returns @c true if program radio menu is enabled. */\n    virtual bool radioProgramming() const;\n    /** Enables/disables program radio menu. */\n    virtual void enableRadioProgramming(bool enable);\n\n    /** Encodes the menu settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from menu settings. */\n    virtual bool updateConfig(Config *config);\n  };\n\n  /** Represents all button settings within the codeplug on the radio.\n   *\n   * Memory representation of the button settings:\n   * @verbinclude tyt_buttonsettings.txt */\n  class ButtonSettingsElement: public Codeplug::Element\n  {\n  public:\n    /** The possible button actions. */\n    typedef enum TyTButtonSettings::ButtonAction ButtonAction;\n\n  protected:\n    /** Hidden constructor. */\n    ButtonSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit ButtonSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~ButtonSettingsElement();\n\n    void clear();\n\n    /** Returns the action for a short press on side button 1. */\n    virtual ButtonAction sideButton1Short() const;\n    /** Sets the action for a short press on side button 1. */\n    virtual void setSideButton1Short(ButtonAction action);\n    /** Returns the action for a long press on side button 1. */\n    virtual ButtonAction sideButton1Long() const;\n    /** Sets the action for a short press on side button 1. */\n    virtual void setSideButton1Long(ButtonAction action);\n\n    /** Returns the action for a short press on side button 2. */\n    virtual ButtonAction sideButton2Short() const;\n    /** Sets the action for a short press on side button 2. */\n    virtual void setSideButton2Short(ButtonAction action);\n    /** Returns the action for a long press on side button 2. */\n    virtual ButtonAction sideButton2Long() const;\n    /** Sets the action for a short press on side button 2. */\n    virtual void setSideButton2Long(ButtonAction action);\n\n    /** Returns the long-press duration in ms. */\n    virtual unsigned longPressDuration() const;\n    /** Sets the long-press duration in ms. */\n    virtual void setLongPressDuration(unsigned ms);\n\n    /** Encodes the button settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from button settings. */\n    virtual bool updateConfig(Config *config);\n  };\n\n  /** Represents a single one-touch setting within the codeplug on the radio.\n   *\n   * Memory representation of a one-touch setting:\n   * @verbinclude tyt_onetouchsettings.txt */\n  class OneTouchSettingElement: public Codeplug::Element\n  {\n  public:\n    /** Possible one-touch actions. */\n    enum Action {\n      CALL    = 0b0000,                 ///< Call someone, see @c contact.\n      MESSAGE = 0b0001,                 ///< Send a message, see @c message.\n      DTMF1   = 0b1000,                 ///< Analog call DTMF system 1.\n      DTMF2   = 0b1001,                 ///< Analog call DTMF system 2.\n      DTMF3   = 0b1010,                 ///< Analog call DTMF system 3.\n      DTMF4   = 0b1011                  ///< Analog call DTMF system 4.\n    };\n\n    /** Possible one-touch action types. */\n    enum Type {\n      Disabled = 0b00,                  ///< Disabled one-touch.\n      Digital  = 0b01,                  ///< Digital call/message.\n      Analog   = 0b10                   ///< Analog call.\n    };\n\n  protected:\n    /** Hidden constructor. */\n    OneTouchSettingElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit OneTouchSettingElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~OneTouchSettingElement();\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the action to perform. */\n    virtual Action action() const;\n    /** Sets the action to perform. */\n    virtual void setAction(Action action);\n\n    /** Returns the type of the action. */\n    virtual Type actionType() const;\n    /** Sets the type of the action. */\n    virtual void setActionType(Type action);\n\n    /** Returns the message index +1. */\n    virtual uint8_t messageIndex() const;\n    /** Sets the message index +1. */\n    virtual void setMessageIndex(uint8_t idx);\n\n    /** Returns the contact index +1. */\n    virtual uint16_t contactIndex() const;\n    /** Sets the contact index +1. */\n    virtual void setContactIndex(uint16_t idx);\n  };\n\n  /** Represents the emergency settings within the codeplug on the radio.\n   *\n   * Memory representation of the emergency settings (size 0x0010 bytes):\n   * @verbinclude tyt_emergencysettings.txt */\n  class EmergencySettingsElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EmergencySettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit EmergencySettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~EmergencySettingsElement();\n\n    virtual void clear();\n\n    /** Returns @c true if emergency remote monitor is enabled. */\n    virtual bool emergencyRemoteMonitor() const;\n    /** Enables/disables emergency remote monitor. */\n    virtual void enableEmergencyRemoteMonitor(bool enable);\n    /** Returns @c true if remote monitor is enabled. */\n    virtual bool remoteMonitor() const;\n    /** Enables/disables remote monitor. */\n    virtual void enableRemoteMonitor(bool enable);\n    /** Returns @c true if radio disable is enabled. */\n    virtual bool radioDisable() const;\n    /** Enables/disables radio disable. */\n    virtual void enableRadioDisable(bool enable);\n\n    /** Returns the remote monitor duration in seconds. */\n    virtual unsigned remoteMonitorDuration() const;\n    /** Sets the remote monitor duration in seconds. */\n    virtual void setRemoteMonitorDuration(unsigned sec);\n\n    /** Returns the TX time-out in ms. */\n    virtual unsigned txTimeOut() const;\n    /** Sets the TX time-out in ms. */\n    virtual void setTXTimeOut(unsigned ms);\n\n    /** Returns the message limit. */\n    virtual unsigned messageLimit() const;\n    /** Sets the message limit. */\n    virtual void setMessageLimit(unsigned limit);\n  };\n\n  /** Represents a single emergency system within the radio.\n   *\n   * Memory representation of emergency system (size 0x0028 bytes):\n   * @verbinclude tyt_emergencysystem.txt */\n  class EmergencySystemElement: public Codeplug::Element\n  {\n  public:\n    /** Possible alarm type for the system. */\n    enum AlarmType {\n      DISABLED = 0,                     ///< No alarm at all\n      REGULAR = 1,                      ///< Regular alarm sound.\n      SILENT = 2,                       ///< Silent alarm.\n      SILENT_W_VOICE = 3                ///< silent alarm with voice.\n    };\n    /** Possible alarm modes for the system. */\n    enum AlarmMode {\n      ALARM = 0,                        ///< Just alarm.\n      ALARM_W_CALL = 1,                 ///< Alarm + call.\n      ALARM_W_VOICE = 2                 ///< Alarm + call + voice?\n    };\n\n  protected:\n    /** Hidden constructor. */\n    EmergencySystemElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit EmergencySystemElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~EmergencySystemElement();\n\n    bool isValid() const;\n    void clear();\n\n    /** Returns the name of the system. */\n    virtual QString name() const;\n    /** Sets the name of the system. */\n    virtual void setName(const QString &name);\n\n    /** Returns the alarm type of the system. */\n    virtual AlarmType alarmType() const;\n    /** Sets the alarm type of the system. */\n    virtual void setAlarmType(AlarmType type);\n    /** Returns the alarm mode of the system. */\n    virtual AlarmMode alarmMode() const;\n    /** Sets the alarm mode of the system. */\n    virtual void setAlarmMode(AlarmMode mode);\n\n    /** Returns the number of impolite retries. */\n    virtual unsigned impoliteRetries() const;\n    /** Sets the number of impolite retries. */\n    virtual void setImpoliteRetries(unsigned num);\n\n    /** Returns the number of polite retries. */\n    virtual unsigned politeRetries() const;\n    /** Sets the number of polite retries. */\n    virtual void setPoliteRetries(unsigned num);\n\n    /** Returns the hot MIC duration in seconds. */\n    virtual unsigned hotMICDuration() const;\n    /** Sets the hot MIC duration in seconds. */\n    virtual void setHotMICDuration(unsigned sec);\n\n    /** Returns @c true if the revert channel is the selected one. */\n    virtual bool revertChannelIsSelected() const;\n    /** Returns the index of the revert channel. */\n    virtual uint16_t revertChannelIndex() const;\n    /** Sets the revert channel index. */\n    virtual void setRevertChannelIndex(uint16_t idx);\n    /** Sets revert channel to selected channel. */\n    virtual void revertChannelSelected();\n  };\n\n  /** Represents all encryption keys and settings within the codeplug on the device.\n   *\n   * Memory representation of encryption settings:\n   * @verbinclude tyt_privacy.txt */\n  class EncryptionElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    EncryptionElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit EncryptionElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~EncryptionElement();\n\n    void clear();\n\n    /** Returns the size of the element. */\n    static constexpr unsigned int size() { return 0x00b0; }\n\n    /** Returns @c true if the n-th \"enhanced\" key (128bit) is set.\n     * That is, if it is not filled with 0xff. */\n    virtual bool isEnhancedKeySet(unsigned n) const;\n    /** Returns the n-th \"enhanced\" key (128bit). */\n    virtual QByteArray enhancedKey(unsigned n) const;\n    /** Sets the n-th \"enhanced\" key (128bit). */\n    virtual void setEnhancedKey(unsigned n, const QByteArray &key);\n\n    /** Returns @c true if the n-th \"basic\" key (16bit) is set.\n     * That is, if it is not filled with 0xff. */\n    virtual bool isBasicKeySet(unsigned n) const;\n    /** Returns the n-th \"basic\" key (16bit). */\n    virtual QByteArray basicKey(unsigned n) const;\n    /** Sets the n-th \"basic\" key (16bit). */\n    virtual void setBasicKey(unsigned n, const QByteArray &key);\n\n    /** Encodes given commercial extension. */\n    virtual bool fromCommercialExt(CommercialExtension *encr, Context &ctx);\n    /** Updates the commercial extension. */\n    virtual bool updateCommercialExt(Context &ctx);\n    /** Links the given encryption extension. */\n    virtual bool linkCommercialExt(CommercialExtension *ext, Context &ctx);\n\n  public:\n    /** Some limits for the element. */\n    struct Limit {\n      /** Specifies the maximum number of basic (DMR) encryption keys (16bit). */\n      static constexpr unsigned int basicKeys() { return 16; }\n      /** Specifies the maximum number of advanced (AES) encryption keys (128bit). */\n      static constexpr unsigned int advancedKeys() { return 8; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr unsigned int advancedKeys() { return 0x0000; }\n      static constexpr unsigned int betweenAdvancedKeys() { return 0x0010; }\n      static constexpr unsigned int basicKeys() { return 0x0090; }\n      static constexpr unsigned int betweenBasicKeys() { return 0x0002; }\n      /// @endcond\n    };\n  };\n\n\n  /** Basic pre-defined SMS text message. */\n  class MessageElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return 0x00120; }\n\n    void clear();\n\n    bool isValid() const;\n\n    /** Returns the text of the message. */\n    virtual QString text() const;\n    /** Sets the text of the message. */\n    virtual void setText(const QString &text);\n\n    /** Encodes the given SMS template. */\n    virtual bool encode(SMSTemplate *sms, const ErrorStack &err=ErrorStack());\n    /** Decodes the given SMS template. */\n    virtual SMSTemplate *decode(const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some limits. */\n    struct Limit {\n      static constexpr unsigned int length() { return 144; }    ///< Maximum message length.\n    };\n  };\n\n  /** Bank of pre-defined SMS text messages. */\n  class MessageBankElement: public Element\n  {\n  protected:\n    /** Hidden constructor. */\n    MessageBankElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    MessageBankElement(uint8_t *ptr);\n\n    /** The size of the element. */\n    static constexpr unsigned int size() { return MessageElement::size()*Limit::messages(); }\n\n    void clear();\n\n    /** Returns the i-th message. */\n    virtual MessageElement message(unsigned int i) const;\n\n    /** Encodes all messages. */\n    virtual bool encode(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack());\n    /** Decodes all messages. */\n    virtual bool decode(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  public:\n    /** Some Limits. */\n    struct Limit {\n      /** The maximum number of messages in a bank. */\n      static constexpr unsigned int messages() { return 50; }\n    };\n  };\n\n\nprotected:\n  /** Empty constructor. */\n  explicit TyTCodeplug(QObject *parent = nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~TyTCodeplug();\n\n  /** Clears and resets the complete codeplug to some default values. */\n  virtual void clear();\n\n  bool index(Config *config, Context &ctx, const ErrorStack &err=ErrorStack()) const;\n\n  Config *preprocess(Config *config, const ErrorStack &err=ErrorStack()) const;\n\n  /** Decodes the binary codeplug and stores its content in the given generic configuration. */\n  bool decode(Config *config, const ErrorStack &err=ErrorStack());\n  /** Encodes the given generic configuration as a binary codeplug. */\n  bool encode(Config *config, const Flags &flags = Flags(), const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Decodes the binary codeplug and stores its content in the given generic configuration using\n   * the given context. */\n  virtual bool decodeElements(Context &ctx, const ErrorStack &err=ErrorStack());\n  /** Encodes the given generic configuration as a binary codeplug using the given context. */\n  virtual bool encodeElements(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n\n  /** Clears the time-stamp in the codeplug. */\n  virtual void clearTimestamp() = 0;\n  /** Sets the time-stamp. */\n  virtual bool encodeTimestamp() = 0;\n\n  /** Clears the general settings in the codeplug. */\n  virtual void clearGeneralSettings() = 0;\n  /** Updates the general settings from the given configuration. */\n  virtual bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Updates the given configuration from the general settings. */\n  virtual bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all contacts in the codeplug. */\n  virtual void clearContacts() = 0;\n  /** Encodes all digital contacts in the configuration into the codeplug. */\n  virtual bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a digital contact to the configuration for each one in the codeplug. */\n  virtual bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all RX group lists in the codeplug. */\n  virtual void clearGroupLists() = 0;\n  /** Encodes all group lists in the configuration into the codeplug. */\n  virtual bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a RX group list to the configuration for each one in the codeplug. */\n  virtual bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all added RX group lists within the configuration. */\n  virtual bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all channels in the codeplug. */\n  virtual void clearChannels() = 0;\n  /** Encodes all channels in the configuration into the codeplug. */\n  virtual bool encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a channel to the configuration for each one in the codeplug. */\n  virtual bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all added channels within the configuration. */\n  virtual bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all zones in the codeplug. */\n  virtual void clearZones() = 0;\n  /** Encodes all zones in the configuration into the codeplug. */\n  virtual bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a zone to the configuration for each one in the codeplug. */\n  virtual bool createZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all added zones within the configuration. */\n  virtual bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all scan lists in the codeplug. */\n  virtual void clearScanLists() = 0;\n  /** Encodes all scan lists in the configuration into the codeplug. */\n  virtual bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a scan list to the configuration for each one in the codeplug. */\n  virtual bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all added scan lists within the configuration. */\n  virtual bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all positioning systems in the codeplug. */\n  virtual void clearPositioningSystems() = 0;\n  /** Encodes all DMR positioning systems in the configuration into the codeplug. */\n  virtual bool encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Adds a GPS positioning system to the configuration for each one in the codeplug. */\n  virtual bool createPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Links all added positioning systems within the configuration. */\n  virtual bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the button settings in the codeplug. */\n  virtual void clearButtonSettings() = 0;\n  /** Encodes the button settings. */\n  virtual bool encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the button settings. */\n  virtual bool decodeButtonSetttings(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all encryption keys in the codeplug. */\n  virtual void clearPrivacyKeys() = 0;\n  /** Encodes the encryption keys. */\n  virtual bool encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes the encryption keys. */\n  virtual bool decodePrivacyKeys(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears the menu settings in the codeplug. */\n  virtual void clearMenuSettings() = 0;\n\n  /** Clears all text messages in the codeplug. */\n  virtual void clearTextMessages() = 0;\n  /** Encodes text messages. */\n  virtual bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err=ErrorStack()) = 0;\n  /** Decodes text messages. */\n  virtual bool decodeTextMessages(Context &ctx, const ErrorStack &err=ErrorStack()) = 0;\n\n  /** Clears all emergency systems in the codeplug. */\n  virtual void clearEmergencySystems() = 0;\n};\n\n#endif // TYT_CODEPLUG_HH\n"
  },
  {
    "path": "lib/tyt_extensions.cc",
    "content": "#include \"tyt_extensions.hh\"\n#include \"level.hh\"\n\n/* ******************************************************************************************** *\n * Implementation of TyTChannelExtension\n * ******************************************************************************************** */\nTyTChannelExtension::TyTChannelExtension(QObject *parent)\n  : ConfigExtension(parent), _autoScan(false), _emergencyAlarmConfirmed(false),\n    _displayPTTId(true), _rxRefFrequency(RefFrequency::Low), _txRefFrequency(RefFrequency::Low),\n    _tightSquelch(false), _compressedUDPHeader(false),\n    _killTone(KillTone::Off), _inCallCriterion(InCallCriterion::Always), _allowInterrupt(false),\n    _dcdmLeader(false), _dmrSquelch(Level::fromValue(1))\n{\n  // pass...\n}\n\nConfigItem *\nTyTChannelExtension::clone() const {\n  TyTChannelExtension *ex = new TyTChannelExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\n\nbool\nTyTChannelExtension::autoScan() const {\n  return _autoScan;\n}\n\nvoid\nTyTChannelExtension::enableAutoScan(bool enable) {\n  if (_autoScan == enable)\n    return;\n  _autoScan = enable;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::emergencyAlarmConfirmed() const {\n  return _emergencyAlarmConfirmed;\n}\n\nvoid\nTyTChannelExtension::enableEmergencyAlarmConfirmed(bool enable) {\n  if (_emergencyAlarmConfirmed == enable)\n    return;\n  _emergencyAlarmConfirmed = enable;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::displayPTTId() const {\n  return _displayPTTId;\n}\n\nvoid\nTyTChannelExtension::enableDisplayPTTId(bool enable) {\n  if (_displayPTTId == enable)\n    return;\n  _displayPTTId = enable;\n  emit modified(this);\n}\n\n\nTyTChannelExtension::RefFrequency\nTyTChannelExtension::rxRefFrequency() const {\n  return _rxRefFrequency;\n}\n\nvoid\nTyTChannelExtension::setRXRefFrequency(RefFrequency ref) {\n  if (_rxRefFrequency == ref)\n    return;\n  _rxRefFrequency = ref;\n  emit modified(this);\n}\n\n\nTyTChannelExtension::RefFrequency\nTyTChannelExtension::txRefFrequency() const {\n  return _txRefFrequency;\n}\n\nvoid\nTyTChannelExtension::setTXRefFrequency(RefFrequency ref) {\n  if (_txRefFrequency == ref)\n    return;\n  _txRefFrequency = ref;\n  emit modified(this);\n}\n\n\nLevel\nTyTChannelExtension::dmrSquelch() const {\n  return _dmrSquelch;\n}\n\nvoid\nTyTChannelExtension::setDMRSquelch(Level sq) {\n  if (_dmrSquelch == sq)\n    return;\n  _dmrSquelch = sq;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::tightSquelch() const {\n  return _tightSquelch;\n}\n\nvoid\nTyTChannelExtension::enableTightSquelch(bool enable) {\n  if (_tightSquelch == enable)\n    return;\n  _tightSquelch = enable;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::compressedUDPHeader() const {\n  return _compressedUDPHeader;\n}\n\nvoid\nTyTChannelExtension::enableCompressedUDPHeader(bool enable) {\n  if (_compressedUDPHeader == enable)\n    return;\n  _compressedUDPHeader = enable;\n  emit modified(this);\n}\n\n\nTyTChannelExtension::KillTone\nTyTChannelExtension::killTone() const {\n  return _killTone;\n}\n\nvoid\nTyTChannelExtension::setKillTone(KillTone tone) {\n  if (_killTone == tone)\n    return;\n  _killTone = tone;\n  emit modified(this);\n}\n\n\nTyTChannelExtension::InCallCriterion\nTyTChannelExtension::inCallCriterion() const {\n  return _inCallCriterion;\n}\n\nvoid\nTyTChannelExtension::setInCallCriterion(InCallCriterion crit) {\n  if (_inCallCriterion == crit)\n    return;\n  _inCallCriterion = crit;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::allowInterrupt() const {\n  return _allowInterrupt;\n}\n\nvoid\nTyTChannelExtension::enableAllowInterrupt(bool enable) {\n  if (_allowInterrupt == enable)\n    return;\n  _allowInterrupt = enable;\n  emit modified(this);\n}\n\n\nbool\nTyTChannelExtension::dcdmLeader() const {\n  return _dcdmLeader;\n}\n\nvoid\nTyTChannelExtension::enableDCDMLeader(bool enable) {\n  if (_dcdmLeader == enable)\n    return;\n  _dcdmLeader = enable;\n  emit modified(this);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTScanListExtension\n * ******************************************************************************************** */\nTyTScanListExtension::TyTScanListExtension(QObject *parent)\n  : ConfigExtension(parent), _holdTime(500), _prioritySampleTime(2000)\n{\n  // pass...\n}\n\nConfigItem *\nTyTScanListExtension::clone() const {\n  TyTScanListExtension *ex = new TyTScanListExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\nunsigned\nTyTScanListExtension::holdTime() const {\n  return _holdTime;\n}\nvoid\nTyTScanListExtension::setHoldTime(unsigned ms) {\n  if (_holdTime == ms)\n    return;\n  _holdTime = ms;\n  emit modified(this);\n}\n\nunsigned\nTyTScanListExtension::prioritySampleTime() const {\n  return _prioritySampleTime;\n}\nvoid\nTyTScanListExtension::setPrioritySampleTime(unsigned ms) {\n  if (_prioritySampleTime == ms)\n    return;\n  _prioritySampleTime = ms;\n  emit modified(this);\n}\n\n/*ConfigItem *\nTyTScanListExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                                    const Context &ctx, const ErrorStack &err)\n{\n  Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err)\n  // There are no further extension/children to TyTScanListExtension.\n  return nullptr;\n}*/\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTButtonSettings\n * ******************************************************************************************** */\nTyTButtonSettings::TyTButtonSettings(QObject *parent)\n  : ConfigExtension(parent)\n{\n  _sideButton1Short = Disabled;\n  _sideButton1Long  = Tone1750Hz;\n  _sideButton2Short = MonitorToggle;\n  _sideButton2Long  = Disabled;\n  _sideButton3Short = Disabled;\n  _sideButton3Long = Disabled;\n  _progButton1Short = Disabled;\n  _progButton1Long = Disabled;\n  _progButton2Short = Disabled;\n  _progButton2Long = Disabled;\n  _longPressDuration = 1000;\n}\n\nConfigItem *\nTyTButtonSettings::clone() const {\n  TyTButtonSettings *ex = new TyTButtonSettings();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton1Short() const {\n  return _sideButton1Short;\n}\nvoid\nTyTButtonSettings::setSideButton1Short(ButtonAction action) {\n  if (_sideButton1Short == action)\n    return;\n  _sideButton1Short = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton1Long() const {\n  return _sideButton1Long;\n}\nvoid\nTyTButtonSettings::setSideButton1Long(ButtonAction action) {\n  if (_sideButton1Long == action)\n    return;\n  _sideButton1Long = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton2Short() const {\n  return _sideButton2Short;\n}\nvoid\nTyTButtonSettings::setSideButton2Short(ButtonAction action) {\n  if (_sideButton2Short == action)\n    return;\n  _sideButton2Short = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton2Long() const {\n  return _sideButton2Long;\n}\nvoid\nTyTButtonSettings::setSideButton2Long(ButtonAction action) {\n  if (_sideButton2Long == action)\n    return;\n  _sideButton2Long = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton3Short() const {\n  return _sideButton3Short;\n}\nvoid\nTyTButtonSettings::setSideButton3Short(ButtonAction action) {\n  if (_sideButton3Short == action)\n    return;\n  _sideButton3Short = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::sideButton3Long() const {\n  return _sideButton3Long;\n}\nvoid\nTyTButtonSettings::setSideButton3Long(ButtonAction action) {\n  if (_sideButton3Long == action)\n    return;\n  _sideButton3Long = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::progButton1Short() const {\n  return _progButton1Short;\n}\nvoid\nTyTButtonSettings::setProgButton1Short(ButtonAction action) {\n  if (_progButton1Short == action)\n    return;\n  _progButton1Short = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::progButton1Long() const {\n  return _progButton1Long;\n}\nvoid\nTyTButtonSettings::setProgButton1Long(ButtonAction action) {\n  if (_progButton1Long == action)\n    return;\n  _progButton1Long = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::progButton2Short() const {\n  return _progButton2Short;\n}\nvoid\nTyTButtonSettings::setProgButton2Short(ButtonAction action) {\n  if (_progButton2Short == action)\n    return;\n  _progButton2Short = action;\n  emit modified(this);\n}\n\nTyTButtonSettings::ButtonAction\nTyTButtonSettings::progButton2Long() const {\n  return _progButton2Long;\n}\nvoid\nTyTButtonSettings::setProgButton2Long(ButtonAction action) {\n  if (_progButton2Long == action)\n    return;\n  _progButton2Long = action;\n  emit modified(this);\n}\n\nunsigned\nTyTButtonSettings::longPressDuration() const {\n  return _longPressDuration;\n}\nvoid\nTyTButtonSettings::setLongPressDuration(unsigned dur) {\n  _longPressDuration = dur;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTMenuSettings\n * ******************************************************************************************** */\nTyTMenuSettings::TyTMenuSettings(QObject *parent)\n  : ConfigExtension(parent), _inifiniteHangTime(false), _hangTime(10), _textMessage(true),\n    _callAlert(true), _contactEditing(true), _manualDial(true), _remoteRadioCheck(true),\n    _remoteMonitor(true), _remoteRadioEnable(true), _remoteRadioDisable(true), _scan(true),\n    _scanListEditing(true), _callLogMissed(true), _callLogAnswered(true), _callLogOutgoing(true),\n    _talkaround(true), _alertTone(true), _power(true), _backlight(true), _bootScreen(true),\n    _keypadLock(true), _ledIndicator(true), _squelch(true), _vox(true), _password(true),\n    _displayMode(true), _radioProgramming(true), _gpsInformation(true)\n{\n  // pass...\n}\n\nConfigItem *\nTyTMenuSettings::clone() const {\n  TyTMenuSettings *ex = new TyTMenuSettings();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\n/*ConfigItem *\nTyTMenuSettings::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err)\n  // There are no further extension/children to TyTButtonSettings.\n  return nullptr;\n}*/\n\nbool\nTyTMenuSettings::hangtimeIsInfinite() const {\n  return _inifiniteHangTime;\n}\nvoid\nTyTMenuSettings::setHangtimeInfinite(bool infinite) {\n  if (_inifiniteHangTime == infinite)\n    return;\n  _inifiniteHangTime = infinite;\n  if (_inifiniteHangTime)\n    _hangTime = 0;\n  emit modified(this);\n}\n\nunsigned\nTyTMenuSettings::hangTime() const {\n  return _hangTime;\n}\n\nvoid\nTyTMenuSettings::setHangTime(unsigned sec) {\n  if (_hangTime == sec)\n    return;\n  _hangTime = sec;\n  _inifiniteHangTime = (0 == _hangTime);\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::textMessage() const {\n  return _textMessage;\n}\nvoid\nTyTMenuSettings::enableTextMessage(bool enable) {\n  if (_textMessage == enable)\n    return;\n  _textMessage = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::callAlert() const {\n  return _callAlert;\n}\nvoid\nTyTMenuSettings::enableCallAlert(bool enable) {\n  if (_callAlert == enable)\n    return;\n  _callAlert = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::contactEditing() const {\n  return _contactEditing;\n}\nvoid\nTyTMenuSettings::enableContactEditing(bool enable) {\n  if (_contactEditing == enable)\n    return;\n  _contactEditing = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::manualDial() const {\n  return _manualDial;\n}\nvoid\nTyTMenuSettings::enableManualDial(bool enable) {\n  if (_manualDial == enable)\n    return;\n  _manualDial = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::remoteRadioCheck() const {\n  return _remoteRadioCheck;\n}\nvoid\nTyTMenuSettings::enableRemoteRadioCheck(bool enable) {\n  if (_remoteRadioCheck == enable)\n    return;\n  _remoteRadioCheck = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::remoteMonitor() const {\n  return _remoteMonitor;\n}\nvoid\nTyTMenuSettings::enableRemoteMonitor(bool enable) {\n  if (_remoteMonitor == enable)\n    return;\n  _remoteMonitor = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::remoteRadioEnable() const {\n  return _remoteRadioEnable;\n}\nvoid\nTyTMenuSettings::enableRemoteRadioEnable(bool enable) {\n  if (_remoteRadioEnable == enable)\n    return;\n  _remoteRadioEnable = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::remoteRadioDisable() const {\n  return _remoteRadioDisable;\n}\nvoid\nTyTMenuSettings::enableRemoteRadioDisable(bool enable) {\n  if (_remoteRadioDisable == enable)\n    return;\n  _remoteRadioDisable = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::scan() const {\n  return _scan;\n}\nvoid\nTyTMenuSettings::enableScan(bool enable) {\n  if (_scan == enable)\n    return;\n  _scan = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::scanListEditing() const {\n  return _scanListEditing;\n}\nvoid\nTyTMenuSettings::enableScanListEditing(bool enable) {\n  if (_scanListEditing == enable)\n    return;\n  _scanListEditing = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::callLogMissed() const {\n  return _callLogMissed;\n}\nvoid\nTyTMenuSettings::enableCallLogMissed(bool enable) {\n  if (_callLogMissed == enable)\n    return;\n  _callLogMissed = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::callLogAnswered() const {\n  return _callLogAnswered;\n}\nvoid\nTyTMenuSettings::enableCallLogAnswered(bool enable) {\n  if (_callLogAnswered == enable)\n    return;\n  _callLogAnswered = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::callLogOutgoing() const {\n  return _callLogOutgoing;\n}\nvoid\nTyTMenuSettings::enableCallLogOutgoing(bool enable) {\n  if (_callLogOutgoing == enable)\n    return;\n  _callLogOutgoing = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::talkaround() const {\n  return _talkaround;\n}\nvoid\nTyTMenuSettings::enableTalkaround(bool enable) {\n  if (_talkaround == enable)\n    return;\n  _talkaround = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::alertTone() const {\n  return _alertTone;\n}\nvoid\nTyTMenuSettings::enableAlertTone(bool enable) {\n  if (_alertTone == enable)\n    return;\n  _alertTone = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::power() const {\n  return _power;\n}\nvoid\nTyTMenuSettings::enablePower(bool enable) {\n  if (_power == enable)\n    return;\n  _power = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::backlight() const {\n  return _backlight;\n}\nvoid\nTyTMenuSettings::enableBacklight(bool enable) {\n  if (_backlight == enable)\n    return;\n  _backlight = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::bootScreen() const {\n  return _bootScreen;\n}\nvoid\nTyTMenuSettings::enableBootScreen(bool enable) {\n  if (_bootScreen == enable)\n    return;\n  _bootScreen = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::keypadLock() const {\n  return _keypadLock;\n}\nvoid\nTyTMenuSettings::enableKeypadLock(bool enable) {\n  if (_keypadLock == enable)\n    return;\n  _keypadLock = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::ledIndicator() const {\n  return _ledIndicator;\n}\nvoid\nTyTMenuSettings::enableLEDIndicator(bool enable) {\n  if (_ledIndicator == enable)\n    return;\n  _ledIndicator = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::squelch() const {\n  return _squelch;\n}\nvoid\nTyTMenuSettings::enableSquelch(bool enable) {\n  if (_squelch == enable)\n    return;\n  _squelch = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::vox() const {\n  return _vox;\n}\nvoid\nTyTMenuSettings::enableVOX(bool enable) {\n  if (_vox == enable)\n    return;\n  _vox = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::password() const {\n  return _password;\n}\nvoid\nTyTMenuSettings::enablePassword(bool enable) {\n  if (_password == enable)\n    return;\n  _password = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::displayMode() const {\n  return _displayMode;\n}\nvoid\nTyTMenuSettings::enableDisplayMode(bool enable) {\n  if (_displayMode == enable)\n    return;\n  _displayMode = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::radioProgramming() const {\n  return _radioProgramming;\n}\nvoid\nTyTMenuSettings::enableRadioProgramming(bool enable) {\n  if (_radioProgramming == enable)\n    return;\n  _radioProgramming = enable;\n  emit modified(this);\n}\n\nbool\nTyTMenuSettings::gpsInformation() const {\n  return _gpsInformation;\n}\nvoid\nTyTMenuSettings::enableGPSInformation(bool enable) {\n  if (_gpsInformation == enable)\n    return;\n  _gpsInformation = enable;\n  emit modified(this);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTSettingsExtension\n * ******************************************************************************************** */\nTyTSettingsExtension::TyTSettingsExtension(QObject *parent)\n  : ConfigExtension(parent), _monitorType(MonitorType::Open), _allLEDsDisabled(false),\n    _passwdAndLock(false),\n    _powerSaveMode(true),\n    _wakeupPreamble(true), _channelModeA(true), _channelModeB(true),\n    _channelMode(true), _txPreambleDuration(600), _groupCallHangTime(3000),\n    _privateCallHangTime(3000), _lowBatteryWarnInterval(120), _callAlertToneContinuous(false),\n    _callAlertToneDuration(0), _loneWorkerResponseTime(1), _loneWorkerReminderTime(10),\n    _digitalScanHangTime(1000), _analogScanHangTime(1000), _backlightAlwaysOn(false),\n    _backlightDuration(10), _keypadLockManual(true), _keypadLockTime(5*0xff),\n    _pcProgPasswordEnabled(false), _pcProgPassword(),\n    _radioProgPasswordEnabled(false), _radioProgPassword(0),\n    _privateCallMatch(true), _groupCallMatch(true),\n    _channelHangTime(3000)\n{\n  // pass...\n}\n\nConfigItem *\nTyTSettingsExtension::clone() const {\n  TyTSettingsExtension *ex = new TyTSettingsExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n\n  return ex;\n}\n\nTyTSettingsExtension::MonitorType\nTyTSettingsExtension::monitorType() const {\n  return _monitorType;\n}\nvoid\nTyTSettingsExtension::setMonitorType(MonitorType type) {\n  if (_monitorType == type)\n    return;\n  _monitorType = type;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::allLEDsDisabled() const {\n  return _allLEDsDisabled;\n}\nvoid\nTyTSettingsExtension::disableAllLEDs(bool disable) {\n  if (_allLEDsDisabled == disable)\n    return;\n  _allLEDsDisabled = disable;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::passwordAndLock() const {\n  return _passwdAndLock;\n}\nvoid\nTyTSettingsExtension::enablePasswordAndLock(bool enable) {\n  if (_passwdAndLock == enable)\n    return;\n  _passwdAndLock = enable;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::powerSaveMode() const {\n  return _powerSaveMode;\n}\nvoid\nTyTSettingsExtension::enablePowerSaveMode(bool enable) {\n  if (_powerSaveMode == enable)\n    return;\n  _powerSaveMode = enable;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::wakeupPreamble() const {\n  return _wakeupPreamble;\n}\nvoid\nTyTSettingsExtension::enableWakeupPreamble(bool enable) {\n  if (_wakeupPreamble == enable)\n    return;\n  _wakeupPreamble = enable;\n  emit modified(this);\n}\n\n\nbool\nTyTSettingsExtension::channelMode() const {\n  return _channelMode;\n}\nvoid\nTyTSettingsExtension::enableChannelMode(bool enable) {\n  if (_channelMode == enable)\n    return;\n  _channelMode = enable;\n  emit modified(this);\n}\nbool\nTyTSettingsExtension::channelModeA() const {\n  return _channelModeA;\n}\nvoid\nTyTSettingsExtension::enableChannelModeA(bool enable) {\n  if (_channelModeA == enable)\n    return;\n  _channelModeA = enable;\n  emit modified(this);\n}\nbool\nTyTSettingsExtension::channelModeB() const {\n  return _channelModeB;\n}\nvoid\nTyTSettingsExtension::enableChannelModeB(bool enable) {\n  if (_channelModeB == enable)\n    return;\n  _channelModeB = enable;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::lowBatteryWarnInterval() const {\n  return _lowBatteryWarnInterval;\n}\nvoid\nTyTSettingsExtension::setLowBatteryWarnInterval(unsigned sec) {\n  if (_lowBatteryWarnInterval == sec)\n    return;\n  _lowBatteryWarnInterval = sec;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::callAlertToneContinuous() const {\n  return _callAlertToneContinuous;\n}\nvoid\nTyTSettingsExtension::enableCallAlertToneContinuous(bool enable) {\n  if (_callAlertToneContinuous == enable)\n    return;\n  _callAlertToneContinuous = enable;\n  emit modified(this);\n}\nunsigned\nTyTSettingsExtension::callAlertToneDuration() const {\n  return _callAlertToneDuration;\n}\nvoid\nTyTSettingsExtension::setCallAlertToneDuration(unsigned sec) {\n  if (_callAlertToneDuration == sec)\n    return;\n  _callAlertToneDuration = sec;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::loneWorkerResponseTime() const {\n  return _loneWorkerResponseTime;\n}\nvoid\nTyTSettingsExtension::setLoneWorkerResponseTime(unsigned min) {\n  if (_loneWorkerResponseTime == min)\n    return;\n  _loneWorkerResponseTime = min;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::loneWorkerReminderTime() const {\n  return _loneWorkerReminderTime;\n}\nvoid\nTyTSettingsExtension::setLoneWorkerReminderTime(unsigned sec) {\n  if (_loneWorkerReminderTime == sec)\n    return;\n  _loneWorkerReminderTime = sec;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::digitalScanHangTime() const {\n  return _digitalScanHangTime;\n}\nvoid\nTyTSettingsExtension::setDigitalScanHangTime(unsigned ms) {\n  if (_digitalScanHangTime == ms)\n    return;\n  _digitalScanHangTime = ms;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::analogScanHangTime() const {\n  return _analogScanHangTime;\n}\nvoid\nTyTSettingsExtension::setAnalogScanHangTime(unsigned ms) {\n  if (_analogScanHangTime == ms)\n    return;\n  _analogScanHangTime = ms;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::backlightAlwaysOn() const {\n  return _backlightAlwaysOn;\n}\nvoid\nTyTSettingsExtension::enableBacklightAlwaysOn(bool enable) {\n  if (_backlightAlwaysOn == enable)\n    return;\n  _backlightAlwaysOn = enable;\n  emit modified(this);\n}\nunsigned\nTyTSettingsExtension::backlightDuration() const {\n  return _backlightDuration;\n}\nvoid\nTyTSettingsExtension::setBacklightDuration(unsigned sec) {\n  if (_backlightDuration == sec)\n    return;\n  _backlightDuration = sec;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::keypadLockManual() const {\n  return _keypadLockManual;\n}\nvoid\nTyTSettingsExtension::enableKeypadLockManual(bool enable) {\n  if (_keypadLockManual == enable)\n    return;\n  _keypadLockManual = enable;\n  emit modified(this);\n}\nunsigned\nTyTSettingsExtension::keypadLockTime() const {\n  return _keypadLockTime;\n}\nvoid\nTyTSettingsExtension::setKeypadLockTime(unsigned sec) {\n  if (_keypadLockTime == sec)\n    return;\n  _keypadLockTime = sec;\n  emit modified(this);\n}\n\nbool\nTyTSettingsExtension::radioProgPasswordEnabled() const {\n  return _radioProgPassword;\n}\nvoid\nTyTSettingsExtension::enableRadioProgPassword(bool enable) {\n  if (_radioProgPasswordEnabled == enable)\n    return;\n  _radioProgPasswordEnabled = enable;\n  emit modified(this);\n}\nunsigned\nTyTSettingsExtension::radioProgPassword() const {\n  return _radioProgPassword;\n}\nvoid\nTyTSettingsExtension::setRadioProgPassword(unsigned passwd) {\n  if (_radioProgPassword == passwd)\n    return;\n  _radioProgPassword = passwd;\n  emit modified(this);\n}\n\nconst QString &\nTyTSettingsExtension::pcProgPassword() const {\n  return _pcProgPassword;\n}\nvoid\nTyTSettingsExtension::setPCProgPassword(const QString &passwd) {\n  if (_pcProgPassword == passwd)\n    return;\n  _pcProgPassword = passwd;\n  emit modified(this);\n}\n\nunsigned\nTyTSettingsExtension::channelHangTime() const {\n  return _channelHangTime;\n}\nvoid\nTyTSettingsExtension::setChannelHangTime(unsigned ms) {\n  if (_channelHangTime == ms)\n    return;\n  _channelHangTime = ms;\n  emit modified(this);\n}\n\n/*ConfigItem *\nTyTSettingsExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node, const Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err)\n  // No extensions to this extension.\n  return nullptr;\n}*/\n\n\n/* ******************************************************************************************** *\n * Implementation of TyTButtonSettings\n * ******************************************************************************************** */\nTyTConfigExtension::TyTConfigExtension(QObject *parent)\n  : ConfigExtension(parent), _buttonSettings(new TyTButtonSettings(this)),\n    _menuSettings(new TyTMenuSettings(this))\n{\n  // Pass...\n}\n\nConfigItem *\nTyTConfigExtension::clone() const {\n  TyTConfigExtension *ex = new TyTConfigExtension();\n  if (! ex->copy(*this)) {\n    ex->deleteLater();\n    return nullptr;\n  }\n  return ex;\n}\n\nTyTButtonSettings *\nTyTConfigExtension::buttonSettings() const {\n  return _buttonSettings;\n}\n\nTyTMenuSettings *\nTyTConfigExtension::menuSettings() const {\n  return _menuSettings;\n}\n\n/*ConfigItem *\nTyTConfigExtension::allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                                  const Context &ctx, const ErrorStack &err)\n{\n  Q_UNUSED(prop); Q_UNUSED(node); Q_UNUSED(ctx); Q_UNUSED(err)\n  // All extensions are pre-allocated. So nothing to do here.\n  return nullptr;\n}*/\n"
  },
  {
    "path": "lib/tyt_extensions.hh",
    "content": "#ifndef TYTEXTENSION_HH\n#define TYTEXTENSION_HH\n\n#include \"configobject.hh\"\n#include \"level.hh\"\n\n\n/** Represents the TyT channel extension.\n *\n * That is, all device specific settings for TyT devices, that are not represented though the common\n * codeplug.\n *\n * @ingroup tyt */\nclass TyTChannelExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  Q_CLASSINFO(\"description\", \"Settings for MD-390, RT8, MD-UV390, RT3S, MD-2017, RT82, DM-1701, RT84.\")\n  Q_CLASSINFO(\"longDescription\", \"Device specific channel settings for TyT and Retevis devices.\"\n                                 \"Including TyT MD-390, MD-UV390, MD-2017, Retevis RT8, RT3S and RT82\"\n                                 \" as well as Baofeng DM-1701.\")\n\n  /** The auto scan feature. */\n  Q_PROPERTY(bool autoScan READ autoScan WRITE enableAutoScan)\n  /** If @c true, emergency calls are confirmed. */\n  Q_PROPERTY(bool emergencyAlarmConfirmed READ emergencyAlarmConfirmed WRITE enableEmergencyAlarmConfirmed)\n  /** If @c true, displays analog PTT IDs. */\n  Q_PROPERTY(bool displayPTTId READ displayPTTId WRITE enableDisplayPTTId)\n  /** Holds the reference frequency setting for RX. */\n  Q_PROPERTY(RefFrequency rxRefFrequency READ rxRefFrequency WRITE setRXRefFrequency)\n  /** Holds the reference frequency setting for TX. */\n  Q_PROPERTY(RefFrequency txRefFrequency READ txRefFrequency WRITE setTXRefFrequency)\n\n  /** The tight-squelch feature. */\n  Q_PROPERTY(bool tightSquelch READ tightSquelch WRITE enableTightSquelch)\n  /** The compressed UDP header feature. */\n  Q_PROPERTY(bool compressedUDPHeader READ compressedUDPHeader WRITE enableCompressedUDPHeader)\n\n  /** Holds the kill tone frequency. */\n  Q_PROPERTY(KillTone killTone READ killTone WRITE setKillTone)\n  /** Holds the in-call criterion. */\n  Q_PROPERTY(InCallCriterion inCallCriterion READ inCallCriterion WRITE setInCallCriterion)\n  /** Holds the allow-interrupt flag. */\n  Q_PROPERTY(bool allowInterrupt READ allowInterrupt WRITE enableAllowInterrupt)\n  /** If @c true, and dcdm is enabled, this radio is the leader, specifying the clock. */\n  Q_PROPERTY(bool dcdmLeader READ dcdmLeader WRITE enableDCDMLeader)\n  /** The squelch level for DMR channels. */\n  Q_PROPERTY(Level dmrSquelch READ dmrSquelch WRITE setDMRSquelch)\n  Q_CLASSINFO(\"dmrSquelchDescription\", \"Sets the squelch level for DMR channels. \"\n              \"Only applicable for MD-UV390 and MD-2017\")\n\npublic:\n  /** Possible reference frequency settings for RX & TX. */\n  enum class RefFrequency {\n    Low=0, Medium=1, High=2\n  };\n  Q_ENUM(RefFrequency)\n\n  /** Possible kill-tone settings. */\n  enum class KillTone {\n    Tone259_2Hz=0, Tone55_2Hz=1, Off=3\n  };\n  Q_ENUM(KillTone)\n\n  /** Possible in-call criterions. */\n  enum class InCallCriterion {\n    Always = 0, AdmitCriterion=1, TXInterrupt=2\n  };\n  Q_ENUM(InCallCriterion)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit TyTChannelExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the auto scan feature is enabled. */\n  bool autoScan() const;\n  /** Enables/disables the auto-scan feature. */\n  void enableAutoScan(bool enable);\n  /** Returns @c true if emergency calls are confirmed. */\n  bool emergencyAlarmConfirmed() const;\n  /** Enables/disables emergency-call confirmation. */\n  void enableEmergencyAlarmConfirmed(bool enable);\n  /** Returns @c true if analog PTT IDs are shown. */\n  bool displayPTTId() const;\n  /** Enables/disables analog PTT ID display. */\n  void enableDisplayPTTId(bool enable);\n  /** Returns the reference frequency setting for RX. */\n  RefFrequency rxRefFrequency() const;\n  /** Sets the reference frequency setting for RX. */\n  void setRXRefFrequency(RefFrequency ref);\n  /** Returns the reference frequency setting for TX. */\n  RefFrequency txRefFrequency() const;\n  /** Sets the reference frequency setting for TX. */\n  void setTXRefFrequency(RefFrequency ref);\n\n  /** Returns @c true if the tight squelch is enabled. */\n  bool tightSquelch() const;\n  /** Enables/disables the tight squelch. */\n  void enableTightSquelch(bool enable);\n  /** Returns @c true if the compressed UDP header is enabled. */\n  bool compressedUDPHeader() const;\n  /** Enables/disables the compressed UDP header. */\n  void enableCompressedUDPHeader(bool enable);\n\n  /** Returns the kill tone frequency. */\n  KillTone killTone() const;\n  /** Sets the kill-tone frequency. */\n  void setKillTone(KillTone tone);\n  /** Returns the in-call criterion. */\n  InCallCriterion inCallCriterion() const;\n  /** Sets the in-call criterion. */\n  void setInCallCriterion(InCallCriterion crit);\n  /** Returns @c true if interrupt is allowed. */\n  bool allowInterrupt() const;\n  /** Enables/disables interrupt. */\n  void enableAllowInterrupt(bool enable);\n  /** Returns @c true if this radio is the leader for a DCDM simplex channel. */\n  bool dcdmLeader() const;\n  /** Enables/disables this radio to be the leader on a DCDM simplex channel. */\n  void enableDCDMLeader(bool enable);\n  /** Squelch level for DMR channels. */\n  Level dmrSquelch() const;\n  /** Sets the squelch-level for DMR channels. */\n  void setDMRSquelch(Level sq);\n\npublic:\n  /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                            const Context &ctx, const ErrorStack &err=ErrorStack());*/\n\nprotected:\n  // Common properties\n  /** Holds the auto-scan flag. */\n  bool _autoScan;\n  /** Holds the emergency-call confirmation flag. */\n  bool _emergencyAlarmConfirmed;\n  /** Holds the display PTT ID flag. */\n  bool _displayPTTId;\n  /** Holds the reference frequency setting for RX. */\n  RefFrequency _rxRefFrequency;\n  /** Holds the reference frequency setting for TX. */\n  RefFrequency _txRefFrequency;\n\n  // MD-390 properties\n  /** Holds the tightSquelch flag. */\n  bool _tightSquelch;\n  /** Holds the compressed UDP header flag. */\n  bool _compressedUDPHeader;\n\n  // MD-UV390, MD-2017 properties\n  /** Holds the kill tone setting. */\n  KillTone _killTone;\n  /** Holds the in-call criterion. */\n  InCallCriterion _inCallCriterion;\n  /** Holds the interrupt flag. */\n  bool _allowInterrupt;\n  /** Holds the DCDM-leader flag. */\n  bool _dcdmLeader;\n  /** The squelch level [0-10] for DMR channels. */\n  Level _dmrSquelch;\n};\n\n\n/** Represents device specific scan-list settings for TyT devices.\n * @ingroup tyt */\nclass TyTScanListExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** Holds the hold time in ms. */\n  Q_PROPERTY(unsigned holdTime READ holdTime WRITE setHoldTime)\n  /** Holds the sample time in ms for priority channels. */\n  Q_PROPERTY(unsigned prioritySampleTime READ prioritySampleTime WRITE setPrioritySampleTime)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit TyTScanListExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the hold time in ms. */\n  unsigned holdTime() const;\n  /** Sets the hold time im ms. */\n  void setHoldTime(unsigned ms);\n  /** Returns the sample time for priority channels in ms. */\n  unsigned prioritySampleTime() const;\n  /** Sets the sample time for priority channels in ms. */\n  void setPrioritySampleTime(unsigned ms);\n\npublic:\n  /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                            const Context &ctx, const ErrorStack &err=ErrorStack());*/\n\nprotected:\n  /** The hold time in ms. */\n  unsigned _holdTime;\n  /** The sample time for priority channels in ms. */\n  unsigned _prioritySampleTime;\n};\n\n\n/** Represents the TyT button settings extension.\n * @ingroup tyt */\nclass TyTButtonSettings : public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The action to perform on a short press on side button 1. */\n  Q_PROPERTY(ButtonAction sideButton1Short READ sideButton1Short WRITE setSideButton1Short)\n  /** The action to perform on a long press on side button 1. */\n  Q_PROPERTY(ButtonAction sideButton1Long READ sideButton1Long WRITE setSideButton1Long)\n  /** The action to perform on a short press on side button 2. */\n  Q_PROPERTY(ButtonAction sideButton2Short READ sideButton2Short WRITE setSideButton2Short)\n  /** The action to perform on a long press on side button 2. */\n  Q_PROPERTY(ButtonAction sideButton2Long READ sideButton2Long WRITE setSideButton2Long)\n  /** The action to perform on a short press on side button 3. */\n  Q_PROPERTY(ButtonAction sideButton3Short READ sideButton3Short WRITE setSideButton3Short)\n  /** The action to perform on a long press on side button 3. */\n  Q_PROPERTY(ButtonAction sideButton3Long READ sideButton3Long WRITE setSideButton3Long)\n  /** The action to perform on a short press on programmable button 1. */\n  Q_PROPERTY(ButtonAction progButton1Short READ progButton1Short WRITE setProgButton1Short)\n  /** The action to perform on a long press on programmable button 1. */\n  Q_PROPERTY(ButtonAction progButton1Long READ progButton1Long WRITE setProgButton1Long)\n  /** The action to perform on a short press on programmable button 2. */\n  Q_PROPERTY(ButtonAction progButton2Short READ progButton2Short WRITE setProgButton2Short)\n  /** The action to perform on a long press on programmable button 2. */\n  Q_PROPERTY(ButtonAction progButton2Long READ progButton2Long WRITE setProgButton2Long)\n\n  /** The duration of a long press in msec. */\n  Q_PROPERTY(unsigned longPressDuration READ longPressDuration WRITE setLongPressDuration)\n\npublic:\n  /** Possible actions for the side-buttons. */\n  enum ButtonAction {\n    Disabled = 0,                       ///< Disabled side-button action.\n    ToggleAllAlertTones = 1,            ///< Toggle all alert tones.\n    EmergencyOn = 2,                    ///< Enable emergency.\n    EmergencyOff = 3,                   ///< Disable emergency.\n    PowerSelect = 4,                    ///< Select TX power.\n    MonitorToggle = 5,                  ///< Toggle monitor (promiscuous mode on digital channel, open squelch on analog channel).\n    NuisanceDelete = 6,                 ///< Nuisance delete.\n    OneTouch1 = 7,                      ///< Perform one-touch action 1.\n    OneTouch2 = 8,                      ///< Perform one-touch action 2.\n    OneTouch3 = 9,                      ///< Perform one-touch action 3.\n    OneTouch4 = 10,                     ///< Perform one-touch action 4.\n    OneTouch5 = 11,                     ///< Perform one-touch action 5.\n    OneTouch6 = 12,                     ///< Perform one-touch action 6.\n    RepeaterTalkaroundToggle = 13,      ///< Toggle repater mode / talkaround.\n    ScanToggle = 14,                    ///< Start/stop scan.\n    SquelchToggle = 21,                 ///< Enable/disable squelch.\n    PrivacyToggle = 22,                 ///< Enable/disable privacy system.\n    VoxToggle = 23,                     ///< Enable/disable VOX.\n    ZoneIncrement = 24,                 ///< Switch to next zone.\n    BatteryIndicator = 26,              ///< Show battery charge.\n    ManualDialForPrivate = 30,          ///< Manual dial for private.\n    LoneWorkerToggle = 31,              ///< Toggle lone-worker.\n    RecordToggle = 34,                  ///< Enable/disable recording (dep. on firmware).\n    RecordPlayback = 35,                ///< Start/stop playback.\n    RecordDeleteAll = 36,               ///< Delete all recordings.\n    Tone1750Hz = 38,                    ///< Send 1750Hz tone.\n    SwitchUpDown = 47,                  ///< Switch Channel A/B.\n    RightKey = 48,                      ///< Who knows?\n    LeftKey = 49,                       ///< Who knows?\n    ZoneDecrement = 55,                 ///< Switch to previous zone.\n    SetTalkgroup = 81,                  ///< md380tools: set temp. TG\n    PromiscuousToggle = 86              ///< md380tools: enable/disable promiscuous mode\n  };\n  Q_ENUM(ButtonAction)\n\npublic:\n  /** Constructor. */\n  Q_INVOKABLE explicit TyTButtonSettings(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the action for the side button 1 short-press. */\n  ButtonAction sideButton1Short() const;\n  /** Sets the action for the side button 1 short-press. */\n  void setSideButton1Short(ButtonAction action);\n  /** Returns the action for the side button 1 long-press. */\n  ButtonAction sideButton1Long() const;\n  /** Sets the action for the side button 1 long-press. */\n  void setSideButton1Long(ButtonAction action);\n\n  /** Returns the action for the side button 2 short-press. */\n  ButtonAction sideButton2Short() const;\n  /** Sets the action for the side button 2 short-press. */\n  void setSideButton2Short(ButtonAction action);\n  /** Returns the action for the side button 2 long-press. */\n  ButtonAction sideButton2Long() const;\n  /** Sets the action for the side button 2 long-press. */\n  void setSideButton2Long(ButtonAction action);\n\n  /** Returns the action for the side button 3 short-press (Baofeng DM-1701). */\n  ButtonAction sideButton3Short() const;\n  /** Sets the action for the side button 3 short-press (Baofeng DM-1701). */\n  void setSideButton3Short(ButtonAction action);\n  /** Returns the action for the side button 3 long-press (Baofeng DM-1701). */\n  ButtonAction sideButton3Long() const;\n  /** Sets the action for the side button 3 long-press (Baofeng DM-1701). */\n  void setSideButton3Long(ButtonAction action);\n\n  /** Returns the action for the programmable button 1 short-press (Baofeng DM-1701). */\n  ButtonAction progButton1Short() const;\n  /** Sets the action for the programmable button 1 short-press (Baofeng DM-1701). */\n  void setProgButton1Short(ButtonAction action);\n  /** Returns the action for the programmable button 1 long-press (Baofeng DM-1701). */\n  ButtonAction progButton1Long() const;\n  /** Sets the action for the programmable button 1 long-press (Baofeng DM-1701). */\n  void setProgButton1Long(ButtonAction action);\n\n  /** Returns the action for the programmable button 2 short-press (Baofeng DM-1701). */\n  ButtonAction progButton2Short() const;\n  /** Sets the action for the programmable button 2 short-press (Baofeng DM-1701). */\n  void setProgButton2Short(ButtonAction action);\n  /** Returns the action for the programmable button 2 long-press (Baofeng DM-1701). */\n  ButtonAction progButton2Long() const;\n  /** Sets the action for the programmable button 2 long-press (Baofeng DM-1701). */\n  void setProgButton2Long(ButtonAction action);\n\n  /** Returns the long-press duration in msec. */\n  unsigned longPressDuration() const;\n  /** Sets the long-press duration in msec. */\n  void setLongPressDuration(unsigned dur);\n\nprotected:\n  /** Holds the side button 1 short-press action. */\n  ButtonAction _sideButton1Short;\n  /** Holds the side button 1 long-press action. */\n  ButtonAction _sideButton1Long;\n  /** Holds the side button 2 short-press action. */\n  ButtonAction _sideButton2Short;\n  /** Holds the side button 2 long-press action. */\n  ButtonAction _sideButton2Long;\n  /** Holds the side button 3 short-press action. */\n  ButtonAction _sideButton3Short;\n  /** Holds the side button 3 long-press action. */\n  ButtonAction _sideButton3Long;\n  /** Holds the prog button 1 short-press action. */\n  ButtonAction _progButton1Short;\n  /** Holds the prog button 1 long-press action. */\n  ButtonAction _progButton1Long;\n  /** Holds the prog button 2 short-press action. */\n  ButtonAction _progButton2Short;\n  /** Holds the prog button 2 long-press action. */\n  ButtonAction _progButton2Long;\n\n  /** Holds the long-press duration in ms. */\n  unsigned _longPressDuration;\n};\n\n\n/** Represents the TyT menu settings extension.\n * @ingroup tyt */\nclass TyTMenuSettings : public ConfigExtension\n{\n  Q_OBJECT\n\n  /** If @c true, the menu hang time is infinite. */\n  Q_PROPERTY(bool hangtimeIsInfinite READ hangtimeIsInfinite WRITE setHangtimeInfinite)\n  /** The menu hang time in seconds. */\n  Q_PROPERTY(unsigned hangTime READ hangTime WRITE setHangTime)\n  /** If @c true, the text message menu is shown. */\n  Q_PROPERTY(bool textMessage READ textMessage WRITE enableTextMessage)\n  /** If @c true, the call-alert menu item is shown. */\n  Q_PROPERTY(bool callAlert READ callAlert WRITE enableCallAlert)\n  /** If @c true, the contact editing menu is shown. */\n  Q_PROPERTY(bool contactEditing READ contactEditing WRITE enableContactEditing)\n  /** If @c true, the manual dial menu item is shown. */\n  Q_PROPERTY(bool manualDial READ manualDial WRITE enableManualDial)\n  /** If @c true, the remote radio check menu item is shown. */\n  Q_PROPERTY(bool remoteRadioCheck READ remoteRadioCheck WRITE enableRemoteRadioCheck)\n  /** If @c true, the remote monitor menu item is shown. */\n  Q_PROPERTY(bool remoteMonitor READ remoteMonitor WRITE enableRemoteMonitor)\n  /** If @c true, the remote radio enable menu item is shown. */\n  Q_PROPERTY(bool remoteRadioEnable READ remoteRadioEnable WRITE enableRemoteRadioEnable)\n  /** If @c true, the remote radio disable menu item is shown. */\n  Q_PROPERTY(bool remoteRadioDisable READ remoteRadioDisable WRITE enableRemoteRadioDisable)\n  /** If @c true, the scan menu item is shown. */\n  Q_PROPERTY(bool scan READ scan WRITE enableScan)\n  /** If @c true, the scan list editing is enabled. */\n  Q_PROPERTY(bool scanListEditing READ scanListEditing WRITE enableScanListEditing)\n  /** If @c true, the list of missed calls is shown. */\n  Q_PROPERTY(bool callLogMissed READ callLogMissed WRITE enableCallLogMissed)\n  /** If @c true, the list of answered calls is shown. */\n  Q_PROPERTY(bool callLogAnswered READ callLogAnswered WRITE enableCallLogAnswered)\n  /** If @c true, the list of outgoing calls is shown. */\n  Q_PROPERTY(bool callLogOutgoing READ callLogOutgoing WRITE enableCallLogOutgoing)\n  /** If @c true, the talkaround menu item is shown. */\n  Q_PROPERTY(bool talkaround READ talkaround WRITE enableTalkaround)\n  /** If @c true, the alert-tone menu item is shown. */\n  Q_PROPERTY(bool alertTone READ alertTone WRITE enableAlertTone)\n  /** If @c true, the power settings menu item is shown. */\n  Q_PROPERTY(bool power READ power WRITE enablePower)\n  /** If @c true, the backlight menu item is shown. */\n  Q_PROPERTY(bool backlight READ backlight WRITE enableBacklight)\n  /** If @c true, the boot-screen settings menu item is shown. */\n  Q_PROPERTY(bool bootScreen READ bootScreen WRITE enableBootScreen)\n  /** If @c true, the keypad-lock settings menu item is shown. */\n  Q_PROPERTY(bool keypadLock READ keypadLock WRITE enableKeypadLock)\n  /** If @c true, the LED indicator settings menu item is shown. */\n  Q_PROPERTY(bool ledIndicator READ ledIndicator WRITE enableLEDIndicator)\n  /** If @c true, the squelch settings menu item is shown. */\n  Q_PROPERTY(bool squelch READ squelch WRITE enableSquelch)\n  /** If @c true, the VOX settings menu item is shown. */\n  Q_PROPERTY(bool vox READ vox WRITE enableVOX)\n  /** If @c true, the password menu item is shown. */\n  Q_PROPERTY(bool password READ password WRITE enablePassword)\n  /** If @c true, the display-mode settings menu item is shown. */\n  Q_PROPERTY(bool displayMode READ displayMode WRITE enableDisplayMode)\n  /** If @c true, radio programming on the radio is enabled. */\n  Q_PROPERTY(bool radioProgramming READ radioProgramming WRITE enableRadioProgramming)\n  /** If @c true, the positioning settings menu item is shown. */\n  Q_PROPERTY(bool gpsInformation READ gpsInformation WRITE enableGPSInformation)\n\npublic:\n  /** Constructor. */\n  Q_INVOKABLE explicit TyTMenuSettings(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns @c true if the hang time is infinite. */\n  bool hangtimeIsInfinite() const;\n  /** Enables/disables infinite hang time. */\n  void setHangtimeInfinite(bool infinite);\n\n  /** Returns the menu hang time in seconds. */\n  unsigned hangTime() const;\n  /** Sets the menu hang time in seconds. */\n  void setHangTime(unsigned sec);\n\n  /** Returns @c true if the text message menu item is enabled. */\n  bool textMessage() const;\n  /** Enables/disables the text message menu item. */\n  void enableTextMessage(bool enable);\n\n  /** Returns @c true if the call alert menu item is enabled. */\n  bool callAlert() const;\n  /** Enables/disables the call alert menu item. */\n  void enableCallAlert(bool enable);\n\n  /** Returns @c true if contact editing is enabled. */\n  bool contactEditing() const;\n  /** Enables/disables contact editing. */\n  void enableContactEditing(bool enable);\n\n  /** Returns @c true if the manual dial menu item is enabled. */\n  bool manualDial() const;\n  /** Enables/disables the manual dial menu item. */\n  void enableManualDial(bool enable);\n\n  /** Returns @c true if the remote radio check menu item is enabled. */\n  bool remoteRadioCheck() const;\n  /** Enables/disables the remote radio check menu item. */\n  void enableRemoteRadioCheck(bool enable);\n\n  /** Returns @c true if the remote monitor menu item is enabled. */\n  bool remoteMonitor() const;\n  /** Enables/disables the remote monitor menu item. */\n  void enableRemoteMonitor(bool enable);\n\n  /** Returns @c true if the remote radio enable menu item is enabled. */\n  bool remoteRadioEnable() const;\n  /** Enables/disables the remote radio enable menu item. */\n  void enableRemoteRadioEnable(bool enable);\n\n  /** Returns @c true if the remote radio disable menu item is enabled. */\n  bool remoteRadioDisable() const;\n  /** Enables/disables the remote radio disable menu item. */\n  void enableRemoteRadioDisable(bool enable);\n\n  /** Returns @c true if the scan menu item is enabled. */\n  bool scan() const;\n  /** Enables/disables the scan menu item. */\n  void enableScan(bool enable);\n\n  /** Returns @c true if the scan list editing menu item is enabled. */\n  bool scanListEditing() const;\n  /** Enables/disables the scan list editing menu item. */\n  void enableScanListEditing(bool enable);\n\n  /** Returns @c true if the list of missed calls menu item is enabled. */\n  bool callLogMissed() const;\n  /** Enables/disables the list of missed calls menu item. */\n  void enableCallLogMissed(bool enable);\n\n  /** Returns @c true if the list of answered calls menu item is enabled. */\n  bool callLogAnswered() const;\n  /** Enables/disables the list of answered calls menu item. */\n  void enableCallLogAnswered(bool enable);\n\n  /** Returns @c true if the list of outgoing calls menu item is enabled. */\n  bool callLogOutgoing() const;\n  /** Enables/disables the list of outgoing calls menu item. */\n  void enableCallLogOutgoing(bool enable);\n\n  /** Returns @c true if the talkaround menu item is enabled. */\n  bool talkaround() const;\n  /** Enables/disables the talkaround menu item. */\n  void enableTalkaround(bool enable);\n\n  /** Returns @c true if the alert tone menu item is enabled. */\n  bool alertTone() const;\n  /** Enables/disables the alert tone menu item. */\n  void enableAlertTone(bool enable);\n\n  /** Returns @c true if the power menu item is enabled. */\n  bool power() const;\n  /** Enables/disables the power menu item. */\n  void enablePower(bool enable);\n\n  /** Returns @c true if the backlight menu item is enabled. */\n  bool backlight() const;\n  /** Enables/disables the backlight menu item. */\n  void enableBacklight(bool enable);\n\n  /** Returns @c true if the boot screen menu item is enabled. */\n  bool bootScreen() const;\n  /** Enables/disables the boot screen menu item. */\n  void enableBootScreen(bool enable);\n\n  /** Returns @c true if the keypad lock menu item is enabled. */\n  bool keypadLock() const;\n  /** Enables/disables the keypad lock menu item. */\n  void enableKeypadLock(bool enable);\n\n  /** Returns @c true if the LED indicator menu item is enabled. */\n  bool ledIndicator() const;\n  /** Enables/disables the LED indicator menu item. */\n  void enableLEDIndicator(bool enable);\n\n  /** Returns @c true if the squelch menu item is enabled. */\n  bool squelch() const;\n  /** Enables/disables the squelch menu item. */\n  void enableSquelch(bool enable);\n\n  /** Returns @c true if the VOX menu item is enabled. */\n  bool vox() const;\n  /** Enables/disables the VOX menu item. */\n  void enableVOX(bool enable);\n\n  /** Returns @c true if the password menu item is enabled. */\n  bool password() const;\n  /** Enables/disables the password menu item. */\n  void enablePassword(bool enable);\n\n  /** Returns @c true if the display mode menu item is enabled. */\n  bool displayMode() const;\n  /** Enables/disables the display mode menu item. */\n  void enableDisplayMode(bool enable);\n\n  /** Returns @c true if the radio programming menu item is enabled. */\n  bool radioProgramming() const;\n  /** Enables/disables the radio programming menu item. */\n  void enableRadioProgramming(bool enable);\n\n  /** Returns @c true if the GPS information menu item is enabled. */\n  bool gpsInformation() const;\n  /** Enables/disables the GPS information menu item. */\n  void enableGPSInformation(bool enable);\n\npublic:\n  /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                            const Context &ctx, const ErrorStack &err=ErrorStack());*/\n\nprotected:\n  /** If @c true, the menu hang time is infinite. */\n  bool _inifiniteHangTime;\n  /** The menu hang time in seconds. */\n  unsigned _hangTime;\n  /** If @c true, the text message menu is shown. */\n  bool _textMessage;\n  /** If @c true, the call-alert menu item is shown. */\n  bool _callAlert;\n  /** If @c true, the contact editing menu is shown. */\n  bool _contactEditing;\n  /** If @c true, the manual dial menu item is shown. */\n  bool _manualDial;\n  /** If @c true, the remote radio check menu item is shown. */\n  bool _remoteRadioCheck;\n  /** If @c true, the remote monitor menu item is shown. */\n  bool _remoteMonitor;\n  /** If @c true, the remote radio enable menu item is shown. */\n  bool _remoteRadioEnable;\n  /** If @c true, the remote radio disable menu item is shown. */\n  bool _remoteRadioDisable;\n  /** If @c true, the scan menu item is shown. */\n  bool _scan;\n  /** If @c true, the scan list editing is enabled. */\n  bool _scanListEditing;\n  /** If @c true, the list of missed calls is shown. */\n  bool _callLogMissed;\n  /** If @c true, the list of answered calls is shown. */\n  bool _callLogAnswered;\n  /** If @c true, the list of outgoing calls is shown. */\n  bool _callLogOutgoing;\n  /** If @c true, the talkaround menu item is shown. */\n  bool _talkaround;\n  /** If @c true, the alert-tone menu item is shown. */\n  bool _alertTone;\n  /** If @c true, the power settings menu item is shown. */\n  bool _power;\n  /** If @c true, the backlight menu item is shown. */\n  bool _backlight;\n  /** If @c true, the boot-screen settings menu item is shown. */\n  bool _bootScreen;\n  /** If @c true, the keypad-lock settings menu item is shown. */\n  bool _keypadLock;\n  /** If @c true, the LED indicator settings menu item is shown. */\n  bool _ledIndicator;\n  /** If @c true, the squelch settings menu item is shown. */\n  bool _squelch;\n  /** If @c true, the VOX settings menu item is shown. */\n  bool _vox;\n  /** If @c true, the password menu item is shown. */\n  bool _password;\n  /** If @c true, the display-mode settings menu item is shown. */\n  bool _displayMode;\n  /** If @c true, radio programming on the radio is enabled. */\n  bool _radioProgramming;\n  /** If @c true, the positioning settings menu item is shown. */\n  bool _gpsInformation;\n};\n\n\n/** Represents the TyT general settings extension.\n * @ingroup tyt */\nclass TyTSettingsExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The monitor type setting. */\n  Q_PROPERTY(MonitorType monitorType READ monitorType WRITE setMonitorType)\n  /** If @c true, all LEDs are disabled. */\n  Q_PROPERTY(bool allLEDsDisabled READ allLEDsDisabled WRITE disableAllLEDs)\n  /** If @c true, the password and lock is enabled. */\n  Q_PROPERTY(bool passwordAndLock READ passwordAndLock WRITE enablePasswordAndLock)\n  /** If @c true, the power save mode is enabled. */\n  Q_PROPERTY(bool powerSaveMode READ powerSaveMode WRITE enablePowerSaveMode)\n  Q_CLASSINFO(\"powerSaveModeDescription\", \"Puts the radio into sleep-mode when idle.\")\n  Q_CLASSINFO(\"powerSaveModeLongDescription\",\n              \"When enabled, the radio enters a sleep mode when idle. That is, when on receive and \"\n              \"there is no activity on the current channel. However, the radio may need some time \"\n              \"to wake up from this mode. Hence, the 'wakeupPreamble' need to be enabled by all \"\n              \"radios in the network to provide this wake-up delay.\")\n  /** If @c true, a wakeup preamble is sent. */\n  Q_PROPERTY(bool wakeupPreamble READ wakeupPreamble WRITE enableWakeupPreamble)\n  Q_CLASSINFO(\"wakeupPreambleDescription\", \"If enabled, the radio will transmit a short wake-up \"\n              \"preamble before each call.\")\n  /** If @c true, the radio is in channel mode. */\n  Q_PROPERTY(bool channelMode READ channelMode WRITE enableChannelMode)\n  /** If @c true or channelMode is true, the VFO A is in channel mode. */\n  Q_PROPERTY(bool channelModeA READ channelModeA WRITE enableChannelModeA)\n  /** If @c true or channelMode is true, the VFO B is in channel mode. */\n  Q_PROPERTY(bool channelModeB READ channelModeB WRITE enableChannelModeB)\n  /** The low battery warn interval in seconds. */\n  Q_PROPERTY(unsigned lowBatteryWarnInterval READ lowBatteryWarnInterval WRITE setLowBatteryWarnInterval)\n  /** If @c true, the call alert-tone is continuous. */\n  Q_PROPERTY(bool callAlertToneContinuous READ callAlertToneContinuous WRITE enableCallAlertToneContinuous)\n  /** The call alert duration in seconds. */\n  Q_PROPERTY(unsigned callAlertToneDuration READ callAlertToneDuration WRITE setCallAlertToneDuration)\n  /** The lone-worker response time in minutes. */\n  Q_PROPERTY(unsigned loneWorkerResponseTime READ loneWorkerResponseTime WRITE setLoneWorkerResponseTime)\n  /** The lone-worker reminder time in seconds. */\n  Q_PROPERTY(unsigned loneWorkerReminderTime READ loneWorkerReminderTime WRITE setLoneWorkerReminderTime)\n  /** The digital channel scan hang time in ms. */\n  Q_PROPERTY(unsigned digitalScanHangTime READ digitalScanHangTime WRITE setDigitalScanHangTime)\n  /** The analog channel scan hang time in ms. */\n  Q_PROPERTY(unsigned analogScanHangTime READ analogScanHangTime WRITE setAnalogScanHangTime)\n  /** If @c true, the backlight is always on. */\n  Q_PROPERTY(bool backlightAlwaysOn READ backlightAlwaysOn WRITE enableBacklightAlwaysOn)\n  /** If @c backlightAlwaysOn is @c false, specifies the backlight duration in seconds. */\n  Q_PROPERTY(unsigned backlightDuration READ backlightDuration WRITE setBacklightDuration)\n  /** If @c true, the keypad is locked manually. */\n  Q_PROPERTY(bool keypadLockManual READ keypadLockManual WRITE enableKeypadLockManual)\n  /** If @c keypadLockManual is @c false, specifies the keypad lock time. */\n  Q_PROPERTY(unsigned keypadLockTime READ keypadLockTime WRITE setKeypadLockTime)\n  /** If @c true the radio programming password is enabled. */\n  Q_PROPERTY(bool radioProgPasswordEnabled READ radioProgPasswordEnabled WRITE enableRadioProgPassword)\n  /** If @c radioProgPasswordEnabled is @c true, specifies the radio programming password. */\n  Q_PROPERTY(unsigned radioProgPassword READ radioProgPassword WRITE setRadioProgPassword)\n  /** Specifies the PC programming password. */\n  Q_PROPERTY(QString pcProgPassword READ pcProgPassword WRITE setPCProgPassword)\n  /** Holds the channel hang time in ms. */\n  Q_PROPERTY(unsigned channelHangTime READ channelHangTime WRITE setChannelHangTime)\n\n  Q_CLASSINFO(\"description\", \"Settings for MD-390, RT8, MD-UV390, RT3S, MD-2017, RT82.\")\n  Q_CLASSINFO(\"longDescription\", \"Device specific radio settings for TyT and Retevis devices.\"\n                                 \"Including TyT MD-390, MD-UV390, MD-2017 as well as Retevis RT8, \"\n                                 \"RT3S and RT82.\")\n\npublic:\n  /** Possible monitor types. */\n  enum class MonitorType {\n    Silent=0, Open=1\n  };\n  Q_ENUM(MonitorType)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit TyTSettingsExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the monitor type. */\n  MonitorType monitorType() const;\n  /** Sets the monitor type. */\n  void setMonitorType(MonitorType type);\n\n  /** Returns @c true if all LEDs are disabled. */\n  bool allLEDsDisabled() const;\n  /** Disables all LEDs. */\n  void disableAllLEDs(bool disable);\n\n  /** Returns @c true if the password and lock is enabled. */\n  bool passwordAndLock() const;\n  /** Enables the password and lock. */\n  void enablePasswordAndLock(bool enable);\n\n  /** Returns @c true if the power save mode is enabled. */\n  bool powerSaveMode() const;\n  /** Enables the power save mode. */\n  void enablePowerSaveMode(bool enable);\n\n  /** Returns @c true if the wake-up preamble is sent. */\n  bool wakeupPreamble() const;\n  /** Enables transmission of wakeup preamble. */\n  void enableWakeupPreamble(bool enable);\n\n  /** Returns @c true if the radio is in channel mode. Overrides @c channelModeA and @c channelModeB. */\n  bool channelMode() const;\n  /** Enables/disables channel mode for the radio. */\n  void enableChannelMode(bool enable);\n  /** Returns @c true if VFO A is in channel mode. Overridden by @c channelMode. */\n  bool channelModeA() const;\n  /** Enables/disables channel mode for the VFO A. */\n  void enableChannelModeA(bool enable);\n  /** Returns @c true if VFO B is in channel mode. Overridden by @c channelMode. */\n  bool channelModeB() const;\n  /** Enables/disables channel mode for the VFO B. */\n  void enableChannelModeB(bool enable);\n\n  /** Returns the low-battery warn interval in seconds. */\n  unsigned lowBatteryWarnInterval() const;\n  /** Sets the low-battery warn interval in seconds. */\n  void setLowBatteryWarnInterval(unsigned sec);\n\n  /** Returns @c true if the call alert-tone is continuous. */\n  bool callAlertToneContinuous() const;\n  /** Sets the call alert-tone continuous. */\n  void enableCallAlertToneContinuous(bool enable);\n  /** Returns the call alert-tone duration in seconds. */\n  unsigned callAlertToneDuration() const;\n  /** Sets the call alert-tone duration in seconds. */\n  void setCallAlertToneDuration(unsigned sec);\n\n  /** Returns the lone worker response time in minutes. */\n  unsigned loneWorkerResponseTime() const;\n  /** Sets the lone-worker response time in minutes. */\n  void setLoneWorkerResponseTime(unsigned min);\n\n  /** Returns the lone-worker reminder time in seconds. */\n  unsigned loneWorkerReminderTime() const;\n  /** Sets the lone-worker reminder timer in seconds. */\n  void setLoneWorkerReminderTime(unsigned sec);\n\n  /** Returns the hang time scanning for digital channels. */\n  unsigned digitalScanHangTime() const;\n  /** Sets the scan hang-time for digital channels. */\n  void setDigitalScanHangTime(unsigned ms);\n\n  /** Returns the hang time scanning for analog channels. */\n  unsigned analogScanHangTime() const;\n  /** Sets the scan hang-time for analog channels. */\n  void setAnalogScanHangTime(unsigned ms);\n\n  /** Returns @c true if the backlight is always on. */\n  bool backlightAlwaysOn() const;\n  /** Enables the backlight continuously. */\n  void enableBacklightAlwaysOn(bool enable);\n  /** Returns the backlight duration in seconds. */\n  unsigned backlightDuration() const;\n  /** Sets the backlight duration in seconds. */\n  void setBacklightDuration(unsigned sec);\n\n  /** Returns @c true if the keypad lock is manual. */\n  bool keypadLockManual() const;\n  /** Sets the keypad lock to manual. */\n  void enableKeypadLockManual(bool enable);\n  /** Returns the keypad lock time in seconds. */\n  unsigned keypadLockTime() const;\n  /** Sets the keypad lock time in seconds. */\n  void setKeypadLockTime(unsigned sec);\n\n  /** Returns @c true if radio programming password is enabled. */\n  bool radioProgPasswordEnabled() const;\n  /** Enables the radio programming password. */\n  void enableRadioProgPassword(bool enable);\n  /** Returns the radio programming password. */\n  unsigned radioProgPassword() const;\n  /** Sets the radio programming password. */\n  void setRadioProgPassword(unsigned passwd);\n\n  /** Returns the PC programming password. */\n  const QString &pcProgPassword() const;\n  /** Sets PC programming password. */\n  void setPCProgPassword(const QString &passwd);\n\n  /** Returns the channel hang time in ms. */\n  unsigned channelHangTime() const;\n  /** Sets the channel hang time in ms. */\n  void setChannelHangTime(unsigned ms);\n\npublic:\n  /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                            const Context &ctx, const ErrorStack &err=ErrorStack());*/\nprotected:\n  /** Holds the monitor type. */\n  MonitorType _monitorType;\n  /** If @c true all LEDs are disabled. */\n  bool _allLEDsDisabled;\n  /** If @c true the password and lock is enabled. */\n  bool _passwdAndLock;\n  /** If @c true, the power save mode is enabled. */\n  bool _powerSaveMode;\n  /** If @c true, the wake-up preamble is sent. */\n  bool _wakeupPreamble;\n  /** If @c true or channelMode is true, the VFO A is in channel (memory) mode. */\n  bool _channelModeA;\n  /** If @c true or channelMode is true, the VFO B is in channel (memory) mode. */\n  bool _channelModeB;\n  /** If @c true, the radio is in channel (memory) mode. Overrides channelModeA and channelModeB. */\n  bool _channelMode;\n  /** Holds the TX preamble duration. */\n  unsigned _txPreambleDuration;\n  /** Holds the group-call hang time. */\n  unsigned _groupCallHangTime;\n  /** Holds the private-call hang time. */\n  unsigned _privateCallHangTime;\n  /** Holds the low-battery warn interval. */\n  unsigned _lowBatteryWarnInterval;\n  /** If @c true, the call alert-tone is continuous. */\n  bool _callAlertToneContinuous;\n  /** Holds the call alert-tone duration. */\n  unsigned _callAlertToneDuration;\n  /** Holds the lone-worker response time. */\n  unsigned _loneWorkerResponseTime;\n  /** Holds the lone-worker reminder time. */\n  unsigned _loneWorkerReminderTime;\n  /** Holds the scan hang-time for digital channels. */\n  unsigned _digitalScanHangTime;\n  /** Holds the scan hang-time for analog channels. */\n  unsigned _analogScanHangTime;\n  /** If @c true, the backlight is always on. */\n  bool _backlightAlwaysOn;\n  /** Holds the backlight duration. */\n  unsigned _backlightDuration;\n  /** If @c true, the keypad lock is manual. */\n  bool _keypadLockManual;\n  /** Holds the keypad lock time. */\n  unsigned _keypadLockTime;\n  /** If @c true, the programming password is enabled. */\n  bool _pcProgPasswordEnabled;\n  /** Holds the programming password. */\n  QString _pcProgPassword;\n  /** If @c true, the radio programming password is enabled. */\n  bool _radioProgPasswordEnabled;\n  /** Holds the radio programming password. */\n  unsigned _radioProgPassword;\n  /** If @c true, the private call IDs must match. */\n  bool _privateCallMatch;\n  /** If @c true, the group call IDs must match. */\n  bool _groupCallMatch;\n  /** Holds the channel hang time in ms. */\n  unsigned _channelHangTime;\n};\n\n\n/** Groups several extension for TyT devices.\n * @ingroup tyt */\nclass TyTConfigExtension: public ConfigExtension\n{\n  Q_OBJECT\n\n  /** The button settings for TyT devices. */\n  Q_PROPERTY(TyTButtonSettings* buttonSettings READ buttonSettings)\n  /** The menu settings for TyT devices. */\n  Q_PROPERTY(TyTMenuSettings* menuSettings READ menuSettings)\n\npublic:\n  /** Constructor. Also allocates all associates extensions. */\n  Q_INVOKABLE explicit TyTConfigExtension(QObject *parent=nullptr);\n\n  ConfigItem *clone() const;\n\n  /** Returns the button settings extension for TyT devices. */\n  TyTButtonSettings *buttonSettings() const;\n\n  /** Returns the menu settings extension for TyT devices. */\n  TyTMenuSettings *menuSettings() const;\n\npublic:\n  /*ConfigItem *allocateChild(QMetaProperty &prop, const YAML::Node &node,\n                            const Context &ctx, const ErrorStack &err=ErrorStack());*/\n\nprotected:\n  /** Owns the button settings extension. */\n  TyTButtonSettings *_buttonSettings;\n  /** Owns the menu settings extension. */\n  TyTMenuSettings *_menuSettings;\n};\n\n#endif // TYTBUTTONSETTINGSEXTENSION_HH\n"
  },
  {
    "path": "lib/tyt_interface.cc",
    "content": "#include \"tyt_interface.hh\"\n#include \"logger.hh\"\n#include <unistd.h>\n#include \"utils.hh\"\n#include \"errorstack.hh\"\n\n#define USB_VID 0x0483\n#define USB_PID 0xdf11\n\n\nTyTInterface::TyTInterface(const USBDeviceDescriptor &descr, const ErrorStack &err, QObject *parent)\n  : DFUSEDevice(descr, err, 16, parent), RadioInterface()\n{\n  if (! DFUDevice::isOpen()) {\n    errMsg(err) << \"Cannot open TyTInterface.\";\n    return;\n  }\n\n  // Enter Programming Mode.\n  if (wait_idle()) {\n    errMsg(err) << \"Device not ready. Close device.\";\n    close(); return;\n  }\n\n  if (md380_command(0x91, 0x01, err)) {\n    errMsg(err) << \"Cannot enter programming mode. Close device.\";\n    reboot(err);\n    close(); return;\n  }\n\n  // Get device identifier in a static buffer.\n  const char *idstr = identify(err);\n  if (idstr && (0==strcmp(\"DR780\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::MD380);\n  } else if (idstr && (0==strcmp(\"MD390\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::MD390);\n  } else if (idstr && (0==strcmp(\"MD-UV380\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::UV380);\n  } else if (idstr && (0==strcmp(\"MD-UV390\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::UV390);\n  } else if (idstr && (0==strcmp(\"2017\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::MD2017);\n  } else if (idstr && (0==strcmp(\"DM-1701\", idstr))) {\n    _ident = RadioInfo::byID(RadioInfo::DM1701);\n  } else if (idstr) {\n    errMsg(err) << \"Unknown TyT device '\" << idstr << \"'.\";\n    close(); return;\n  }\n\n  // Zero address.\n  if(set_address(0x00000000, err)) {\n    errMsg(err) << \"Cannot set device address to 0x00000000.\";\n    close(); return;\n  }\n\n  logDebug() << \"Found device \" << _ident.manufacturer() << \" \"<< _ident.name()\n             << \" at \" << descr.description() << \".\";\n}\n\nTyTInterface::~TyTInterface() {\n  if (isOpen())\n    close();\n}\n\nUSBDeviceInfo\nTyTInterface::interfaceInfo() {\n  return USBDeviceInfo(USBDeviceInfo::Class::DFU, USB_VID, USB_PID);\n}\n\nQList<USBDeviceDescriptor>\nTyTInterface::detect(bool saveOnly) {\n  Q_UNUSED(saveOnly);\n  return DFUDevice::detect(USB_VID, USB_PID);\n}\n\nvoid\nTyTInterface::close() {\n  if (isOpen()) {\n    _ident = RadioInfo();\n  }\n  DFUSEDevice::close();\n}\n\nbool\nTyTInterface::isOpen() const {\n  return DFUSEDevice::isOpen() && _ident.isValid();\n}\n\nRadioInfo\nTyTInterface::identifier(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return _ident;\n}\n\nint\nTyTInterface::md380_command(uint8_t a, uint8_t b, const ErrorStack &err)\n{\n  unsigned char cmd[2] = { a, b };\n\n  if (int error = download(0, cmd, 2, err))\n    return error;\n\n  usleep(100000);\n  return wait_idle();\n}\n\n\nint\nTyTInterface::set_address(uint32_t address, const ErrorStack &err)\n{\n  unsigned char cmd[5] =\n  { 0x21,\n    (uint8_t)address,\n    (uint8_t)(address >> 8),\n    (uint8_t)(address >> 16),\n    (uint8_t)(address >> 24), };\n\n  if (int error = download(0, cmd, 5, err))\n    return error;\n\n  return wait_idle();\n}\n\n\nint\nTyTInterface::erase_block(uint32_t address, const ErrorStack &err)\n{\n  unsigned char cmd[5] =\n  { 0x41,\n    (uint8_t)address,\n    (uint8_t)(address >> 8),\n    (uint8_t)(address >> 16),\n    (uint8_t)(address >> 24), };\n\n  if (int error = download(0, cmd, 5, err))\n    return error;\n\n  wait_idle();\n\n  return 0;\n}\n\nconst char *\nTyTInterface::identify(const ErrorStack &err)\n{\n  static uint8_t data[64];\n\n  md380_command(0xa2, 0x01, err);\n\n  if (upload(0, data, 64, err))\n    return nullptr;\n\n  return (const char*) data;\n}\n\n\nbool\nTyTInterface::erase(unsigned start, unsigned size, void(*progress)(unsigned, void *), void *ctx, const ErrorStack &err) {\n  int error;\n  // Enter Programming Mode.\n  if ((error = get_status(err)))\n    return false;\n  if ((error = wait_idle()))\n    return false;\n  if ((error = md380_command(0x91, 0x01, err)))\n    return false;\n  usleep(100000);\n\n  unsigned end = start+size;\n  start = align_addr(start, 0x10000);\n  end = align_size(end, 0x10000);\n  size = end-start;\n\n  for (unsigned i=0; i<size; i+=0x10000) {\n    erase_block(start+i, err);\n    if (progress)\n      progress((i*100)/size, ctx);\n  }\n\n  // Zero address.\n  return (0 == set_address(0x00000000, err));\n}\n\nbool\nTyTInterface::read_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank);\n  Q_UNUSED(addr);\n  Q_UNUSED(err);\n  // pass...\n  return true;\n}\n\nbool\nTyTInterface::read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  if (nullptr == data) {\n    errMsg(err) << \"Cannot write data into nullptr!\";\n    return false;\n  }\n\n  uint32_t block = addr/1024;\n  return 0 == upload(block+2, data, nbytes, err);\n}\n\nbool\nTyTInterface::read_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return true;\n}\n\n\nbool\nTyTInterface::write_start(uint32_t bank, uint32_t addr, const ErrorStack &err) {\n  Q_UNUSED(bank); Q_UNUSED(addr); Q_UNUSED(err)\n  return true;\n}\n\nbool\nTyTInterface::write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err) {\n  Q_UNUSED(bank);\n\n  if (nullptr == data) {\n    errMsg(err) << \"Cannot read data from nullptr!\";\n    return false;\n  }\n\n  uint32_t block = addr/1024;\n  if (download(block+2, data, nbytes, err))\n    return false;\n\n  return 0 == wait_idle();\n}\n\nbool\nTyTInterface::write_finish(const ErrorStack &err) {\n  Q_UNUSED(err);\n  return true;\n}\n\n\nbool\nTyTInterface::reboot(const ErrorStack &err) {\n  if (! _ctx)\n    return false;\n\n  if (wait_idle())\n    return false;\n\n  unsigned char cmd[2] = { 0x91, 0x05 };\n  return download(0, cmd, 2, err);\n}\n\n"
  },
  {
    "path": "lib/tyt_interface.hh",
    "content": "#ifndef TYTINTERFACE_HH\n#define TYTINTERFACE_HH\n\n#include \"dfu_libusb.hh\"\n#include \"radiointerface.hh\"\n\n/** Interface to TyT (and Retevis) devices.\n *\n * This class extends the @c DFUDevice and implements the @c RadioInterface to identify the radio,\n * reading/writing and erasing codeplugs or the callsign DB.\n *\n * @section tytif TyT protocol\n * The communication with the device is kind of weird. It all happens through memory reads and\n * writes. This is not unusual as such, but they implemented a weird memory mapping. Everything\n * written to block 0 (i.e., memory address 0x00000000) is a command to the radio. Responses to\n * these commands are read back from the same address/block.\n *\n * The funny thing is, however, that there is a command that sets the memory read and write address,\n * although the DFU interface actually provides means to specify that address. Moreover, commands\n * and data are sent and written to the same DFU interface number. Someone did not understand the\n * DFU interface.\n *\n * @subsection Commands\n * The following commands has been seen.\n *\n * @subsubsection tytifepm Enter program mode\n * The request written is\n * @code\n * +------+------+\n * | 0x91 | 0x01 |\n * +------+------+\n * @endcode\n * There is no explicit response read back.\n *\n * @subsubsection tytiferb Reboot\n * The request written is\n * @code\n * +------+------+\n * | 0x91 | 0x05 |\n * +------+------+\n * @endcode\n * There is no explicit response read back.\n *\n * @subsubsection tyifid Identify/Get Info\n * The request written is\n * @code\n * +------+------+\n * | 0xa2 | What |\n * +------+------+\n * @endcode\n * There is always some response read back. The size varies with whatever is read back.\n * <table>\n *  <tr><th>WhatCode</th> <th>Resp. Len.</th> <th>Description></th></tr>\n *  <tr><td>0x01</td>     <td>32</td>         <td>The radio identifier as a string + some unknown\n *          information.</td></tr>\n *  <tr><td>0x02</td>     <td>4</td>          <td>Unknown</td></tr>\n *  <tr><td>0x03</td>     <td>24</td>         <td>Unknown</td></tr>\n *  <tr><td>0x04</td>     <td>8</td>          <td>Unknown</td></tr>\n *  <tr><td>0x07</td>     <td>16</td>         <td>Unknown</td></tr>\n * </table>\n *\n * @ingroup tyt */\nclass TyTInterface : public DFUSEDevice, public RadioInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. Opens an interface to the specified interface. */\n  TyTInterface(const USBDeviceDescriptor &descr,\n               const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n  /** Destructor. */\n  ~TyTInterface();\n\n  bool isOpen() const;\n  RadioInfo identifier(const ErrorStack &err=ErrorStack());\n  void close();\n\n  bool read_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool read(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool read_finish(const ErrorStack &err=ErrorStack());\n  bool write_start(uint32_t bank, uint32_t addr, const ErrorStack &err=ErrorStack());\n  bool write(uint32_t bank, uint32_t addr, uint8_t *data, int nbytes, const ErrorStack &err=ErrorStack());\n  bool write_finish(const ErrorStack &err=ErrorStack());\n  bool reboot(const ErrorStack &err=ErrorStack());\n\n  /** Erases a memory section at @c start of size @c size. */\n  bool erase(unsigned start, unsigned size, void (*progress)(unsigned, void *)=nullptr, void *ctx=nullptr, const ErrorStack &err=ErrorStack());\n\npublic:\n  /** Returns some information about the interface. */\n  static USBDeviceInfo interfaceInfo();\n  /** Tries to find all interfaces connected TyT radios. */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Internal used function to send a control command to the device. */\n  int md380_command(uint8_t a, uint8_t b, const ErrorStack &err=ErrorStack());\n  /** Internal used function to set the current I/O address. */\n  int set_address(uint32_t address, const ErrorStack &err=ErrorStack());\n  /** Internal used function to erase a specific block. */\n  int erase_block(uint32_t address, const ErrorStack &err=ErrorStack());\n  /** Internal used function to read the device identifier. */\n  const char *identify(const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Read identifier. */\n  RadioInfo _ident;\n};\n\n#endif // TYTINTERFACE_HH\n"
  },
  {
    "path": "lib/tyt_radio.cc",
    "content": "#include \"tyt_radio.hh\"\n#include \"config.hh\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n\n#define BSIZE 1024\n\n\nTyTRadio::TyTRadio(TyTInterface *device, QObject *parent)\n  : Radio(parent), _dev(device), _codeplugFlags(), _config(nullptr)\n{\n  // pass...\n}\n\nTyTRadio::~TyTRadio() {\n  if (_dev && _dev->isOpen()) {\n    logDebug() << \"Reboot TyT device.\";\n    _dev->reboot();\n    logDebug() << \"Close connection to TyT device.\";\n    _dev->close();\n  }\n  if (_dev) {\n    _dev->deleteLater();\n    _dev = nullptr;\n  }\n  logDebug() << \"Destructed TyT radio.\";\n}\n\nbool\nTyTRadio::startDownload(const TransferFlags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  _task = StatusDownload;\n  _errorStack = err;\n\n  if (flags.blocking()) {\n    run();\n    return (StatusIdle == _task);\n  }\n\n  start();\n  return true;\n}\n\nbool\nTyTRadio::startUpload(Config *config, const Codeplug::Flags &flags, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  if (_config)\n    delete _config;\n\n  if (! (_config = config))\n    return false;\n  _config->setParent(this);\n\n  _task = StatusUpload;\n  _errorStack = err;\n  _codeplugFlags = flags;\n\n  if (flags.blocking()) {\n    this->run();\n    return (StatusIdle == _task);\n  }\n  this->start();\n  return true;\n}\n\nbool\nTyTRadio::startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection, const ErrorStack &err) {\n  if (StatusIdle != _task)\n    return false;\n\n  logDebug() << \"Encode call-sign DB.\";\n  if (nullptr == callsignDB()) {\n    errMsg(err) << \"Cannot upload callsign DB. DB not created.\";\n    return false;\n  }\n  callsignDB()->encode(db, selection);\n\n  _task = StatusUploadCallsigns;\n  _errorStack = err;\n\n  if (selection.blocking()) {\n    this->run();\n    return (StatusIdle == _task);\n  }\n\n  this->start();\n  return true;\n}\n\n\nbool\nTyTRadio::startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err) {\n  Q_UNUSED(db); Q_UNUSED(flags);\n  errMsg(err) << \"Satellite config upload is not implemented yet.\";\n  return false;\n}\n\n\nvoid\nTyTRadio::run() {\n  if (StatusDownload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit downloadError(this);\n      return;\n    }\n\n    if (! download()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit downloadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit downloadFinished(this, &codeplug());\n    _config = nullptr;\n  } else if (StatusUpload == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if (! upload()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _dev->reboot();\n    _dev->close();\n    _task = StatusIdle;\n    emit uploadComplete(this);\n  } else if (StatusUploadCallsigns == _task) {\n    if ((nullptr==_dev) || (! _dev->isOpen())) {\n      emit uploadError(this);\n      return;\n    }\n\n    if(! uploadCallsigns()) {\n      _dev->reboot();\n      _dev->close();\n      _task = StatusError;\n      emit uploadError(this);\n      return;\n    }\n\n    _task = StatusIdle;\n    _dev->reboot();\n    _dev->close();\n    emit uploadComplete(this);\n  }\n}\n\nbool\nTyTRadio::download() {\n  emit downloadStarted();\n  logDebug() << \"Download of \" << codeplug().image(0).numElements() << \" elements.\";\n\n  // Check every segment in the codeplug\n  size_t totb = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    if (! codeplug().image(0).element(n).isAligned(BSIZE)) {\n      errMsg(_errorStack)\n          << \"Cannot download codeplug: Codeplug element \" << n\n          << \" (addr=\" << codeplug().image(0).element(n).address()\n          << \", size=\" << codeplug().image(0).element(n).data().size()\n          << \") is not aligned with blocksize \" << BSIZE;\n      return false;\n    }\n    totb += codeplug().image(0).element(n).data().size()/BSIZE;\n  }\n\n  // Then download codeplug\n  size_t bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    unsigned addr = codeplug().image(0).element(n).address();\n    unsigned size = codeplug().image(0).element(n).data().size();\n    unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n    for (unsigned b=0; b<nb; b++, bcount++) {\n      if (! _dev->read(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot download codeplug.\";\n        return false;\n      }\n      emit downloadProgress(float(bcount*100)/totb);\n    }\n  }\n\n  return true;\n}\n\nbool\nTyTRadio::upload() {\n  emit uploadStarted();\n\n  // Check every segment in the codeplug\n  if (! codeplug().isAligned(BSIZE)) {\n    errMsg(_errorStack)\n        << \"Cannot upload codeplug: Codeplug is not aligned with blocksize \" << BSIZE << \".\";\n    return false;\n  }\n\n  size_t totb = codeplug().memSize();\n\n  size_t bcount = 0;\n  // If codeplug gets updated, download codeplug from device first:\n  if (_codeplugFlags.updateCodeplug()) {\n    for (int n=0; n<codeplug().image(0).numElements(); n++) {\n      unsigned addr = codeplug().image(0).element(n).address();\n      unsigned size = codeplug().image(0).element(n).data().size();\n      unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n      for (unsigned b=0; b<nb; b++, bcount+=BSIZE) {\n        if (! _dev->read(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) {\n          errMsg(_errorStack) << \"Cannot upload codeplug.\";\n          return false;\n        }\n        emit uploadProgress(float(bcount*50)/totb);\n      }\n    }\n  }\n\n  // Encode config into codeplug\n  logDebug() << \"Encode codeplug.\";\n  codeplug().encode(_config, _codeplugFlags);\n\n  // then erase memory\n  for (int i=0; i<codeplug().image(0).numElements(); i++)\n    _dev->erase(codeplug().image(0).element(i).address(), codeplug().image(0).element(i).memSize(),\n                nullptr, nullptr, _errorStack);\n\n  logDebug() << \"Upload \" << codeplug().image(0).numElements() << \" elements.\";\n  // then, upload modified codeplug\n  bcount = 0;\n  for (int n=0; n<codeplug().image(0).numElements(); n++) {\n    unsigned addr = codeplug().image(0).element(n).address();\n    unsigned size = codeplug().image(0).element(n).memSize();\n    unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n    for (size_t b=0; b<nb; b++,bcount+=BSIZE) {\n      if (! _dev->write(0, (b0+b)*BSIZE, codeplug().data((b0+b)*BSIZE), BSIZE, _errorStack)) {\n        errMsg(_errorStack) << \"Cannot upload codeplug.\";\n        return false;\n      }\n      emit uploadProgress(50+float(bcount*50)/totb);\n    }\n  }\n\n  return true;\n}\n\nbool\nTyTRadio::uploadCallsigns() {\n  emit uploadStarted();\n\n  logDebug() << \"Check alignment.\";\n  // Check alignment in the codeplug\n  if (! callsignDB()->isAligned(BSIZE)) {\n    errMsg(_errorStack) << \"Cannot upload callsign db: Callsign DB is not aligned with blocksize \"\n                        << BSIZE << \".\";\n    return false;\n  }\n\n  // then erase memory\n  logDebug() << \"Erase memory section for call-sign DB.\";\n  _dev->erase(callsignDB()->image(0).element(0).address(),\n              callsignDB()->image(0).element(0).memSize(),\n              [](unsigned percent, void *ctx) { emit ((TyTRadio *)ctx)->uploadProgress(percent/2); },\n              this, _errorStack);\n\n  logDebug() << \"Upload \" << callsignDB()->image(0).numElements() << \" elements.\";\n  // Total amount of data to transfer\n  size_t totb = callsignDB()->memSize();\n  // Upload callsign DB\n  unsigned addr = callsignDB()->image(0).element(0).address();\n  unsigned size = callsignDB()->image(0).element(0).memSize();\n  unsigned b0 = addr/BSIZE, nb = size/BSIZE;\n  for (size_t b=0, bcount=0; b<nb; b++,bcount+=BSIZE) {\n    if (! _dev->write(0, (b0+b)*BSIZE, callsignDB()->data((b0+b)*BSIZE), BSIZE, _errorStack)) {\n      errMsg(_errorStack) << \"Cannot upload codeplug.\";\n      return false;\n    }\n    emit uploadProgress(50+float(bcount*50)/totb);\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/tyt_radio.hh",
    "content": "/** @defgroup tyt TYT/Retevis Radios\n * Abstract classes for TYT and Retevis radios.\n *\n * @ingroup dsc */\n#ifndef TYT_RADIO_HH\n#define TYT_RADIO_HH\n\n#include \"radio.hh\"\n#include \"tyt_interface.hh\"\n\n/** Implements an USB interface to TYT & Retevis radios.\n *\n * @ingroup tyt */\nclass TyTRadio: public Radio\n{\n\tQ_OBJECT\n\npublic:\n  /** Do not construct this class directly, rather use @c Radio::detect. */\n  explicit TyTRadio(TyTInterface *device=nullptr, QObject *parent=nullptr);\n\n  virtual ~TyTRadio();\n\npublic slots:\n  /** Starts the download of the codeplug and derives the generic configuration from it. */\n  bool startDownload(const TransferFlags &flags, const ErrorStack &err=ErrorStack());\n  /** Derives the device-specific codeplug from the generic configuration and uploads that\n   * codeplug to the radio. */\n  bool startUpload(Config *config, const Codeplug::Flags &flags = Codeplug::Flags(),\n                   const ErrorStack &err=ErrorStack());\n  /** Encodes the given user-database and uploads it to the device. */\n  bool startUploadCallsignDB(UserDatabase *db, const CallsignDB::Flags &selection=CallsignDB::Flags(),\n                             const ErrorStack &err=ErrorStack());\n\n  bool startUploadSatelliteConfig(SatelliteDatabase *db, const TransferFlags &flags, const ErrorStack &err);\n\nprotected:\n  /** Thread main routine, performs all blocking IO operations for codeplug up- and download. */\n\tvoid run();\n\nprivate:\n  virtual bool download();\n  virtual bool upload();\n  virtual bool uploadCallsigns();\n\nprotected:\n  /** The interface to the radio. */\n  TyTInterface *_dev;\n  /** Holds the flags to control assembly and upload of code-plugs. */\n  Codeplug::Flags _codeplugFlags;\n  /** The generic configuration. */\n\tConfig *_config;\n  /** A weak reference to the user-database. */\n  UserDatabase *_userDB;\n};\n\n#endif // UV390_HH\n"
  },
  {
    "path": "lib/usbdevice.cc",
    "content": "#include \"usbdevice.hh\"\n#include <QTextStream>\n#include <QSerialPortInfo>\n#include <libusb.h>\n#include \"logger.hh\"\n#include \"radioinfo.hh\"\n\n#include \"anytone_interface.hh\"\n#include \"radioddity_interface.hh\"\n#include \"opengd77_interface.hh\"\n#include \"tyt_interface.hh\"\n#include \"dr1801uv_interface.hh\"\n#include \"c7000device.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of USBDeviceHandle\n * ********************************************************************************************* */\nUSBDeviceHandle::USBDeviceHandle()\n  : bus(0xff), device(0xff)\n{\n  // pass...\n}\n\nUSBDeviceHandle::USBDeviceHandle(uint8_t busno, uint8_t deviceno, uint32_t locid)\n  : bus(busno), device(deviceno), locationId(locid)\n{\n  // pass...\n}\n\nbool\nUSBDeviceHandle::operator==(const USBDeviceHandle &other) {\n  return (bus == other.bus) && (device == other.device);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of USBDeviceInfo\n * ********************************************************************************************* */\nUSBDeviceInfo::USBDeviceInfo()\n  : _class(Class::None), _vid(0), _pid(0)\n{\n  // pass...\n}\n\nUSBDeviceInfo::USBDeviceInfo(Class cls, uint16_t vid, uint16_t pid, bool save)\n  : _class(cls), _vid(vid), _pid(pid), _save(save)\n{\n  // pass...\n}\n\nUSBDeviceInfo::USBDeviceInfo(const USBDeviceInfo &other)\n  : _class(other._class), _vid(other._vid), _pid(other._pid), _save(other._save)\n{\n  // pass...\n}\n\nUSBDeviceInfo &\nUSBDeviceInfo::operator =(const USBDeviceInfo &other) {\n  _class = other._class;\n  _vid = other._vid;\n  _pid = other._pid;\n  _save = other._save;\n  return *this;\n}\n\nbool\nUSBDeviceInfo::operator ==(const USBDeviceInfo &other) const {\n  // Class must match, VID/PID only need to match, if both are != 0.\n  return (other._class == _class) &&\n      ((! other.hasVendorID()) || (! hasVendorID()) || (other._vid == _vid))\n      && ((! other.hasProductID()) || (! hasProductID()) || (other._pid == _pid));\n}\nbool\nUSBDeviceInfo::operator !=(const USBDeviceInfo &other) const {\n  return !(*this == other);\n}\n\nUSBDeviceInfo::~USBDeviceInfo() {\n  // pass...\n}\n\nbool\nUSBDeviceInfo::isValid() const {\n  return Class::None != _class;\n}\n\nUSBDeviceInfo::Class\nUSBDeviceInfo::interfaceClass() const {\n  return _class;\n}\n\nbool\nUSBDeviceInfo::hasVendorID() const {\n  return 0 != vendorId();\n}\nuint16_t\nUSBDeviceInfo::vendorId() const {\n  return _vid;\n}\n\nbool\nUSBDeviceInfo::hasProductID() const {\n  return 0 != productId();\n}\nuint16_t\nUSBDeviceInfo::productId() const {\n  return _pid;\n}\n\nbool\nUSBDeviceInfo::isSave() const {\n  return _save;\n}\n\nQString\nUSBDeviceInfo::description() const {\n  QString res;\n  QTextStream stream(&res);\n  switch (_class) {\n  case Class::None:\n    stream << \"Invalid\";\n    break;\n  case Class::Serial:\n    stream << \"USB-serial interface \" << QString::number(_vid,16) << \":\" << QString::number(_pid,16);\n    break;\n  case Class::DFU:\n    stream << \"USB device in DFU mode \" << QString::number(_vid,16) << \":\" << QString::number(_pid,16);\n    break;\n  case Class::HID:\n    stream << \"HID \" << QString::number(_vid,16) << \":\" << QString::number(_pid,16);\n    break;\n  case Class::C7K:\n    stream << \"C7000 \" << QString::number(_vid,16) << \":\" << QString::number(_pid,16);\n    break;\n  }\n  return res;\n}\n\nQString\nUSBDeviceInfo::longDescription() const {\n  QStringList radios;\n  foreach (RadioInfo radio, RadioInfo::allRadios(*this, true)) {\n    radios.append(QString(\"%1 %2\").arg(radio.manufacturer(), radio.name()));\n  }\n  // This should not happen\n  if (radios.isEmpty())\n    return QString(\"Unknown interface.\");\n\n  return QString(\"Possibly interfacing %1\").arg(radios.join(\", \"));\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of USBDeviceDescriptor\n * ********************************************************************************************* */\nUSBDeviceDescriptor::USBDeviceDescriptor()\n  : USBDeviceInfo(), _device()\n{\n  // pass...\n}\n\nUSBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceInfo &info, const QString &device)\n  : USBDeviceInfo(info), _device(device)\n{\n  // pass...\n}\n\nUSBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceInfo &info, const USBDeviceHandle &device)\n  : USBDeviceInfo(info), _device(QVariant::fromValue<USBDeviceHandle>(device))\n{\n  // pass...\n}\n\nUSBDeviceDescriptor::USBDeviceDescriptor(const USBDeviceDescriptor &other)\n  : USBDeviceInfo(other), _device(other._device)\n{\n  // pass...\n}\n\nUSBDeviceDescriptor &\nUSBDeviceDescriptor::operator =(const USBDeviceDescriptor &other) {\n  USBDeviceInfo::operator =(other);\n  _device = other._device;\n  return *this;\n}\n\nbool\nUSBDeviceDescriptor::isValid() const {\n  if (! USBDeviceInfo::isValid())\n    return false;\n\n  // dispatch by device class\n  switch (_class) {\n  case Class::None:\n    return false;\n  case Class::Serial:\n    return validSerial();\n  case Class::DFU:\n  case Class::HID:\n  case Class::C7K:\n    return validRawUSB();\n  }\n  return false;\n}\n\nbool\nUSBDeviceDescriptor::validRawUSB() const {\n  int error, num;\n  libusb_context *ctx;\n  if (0 > (error = libusb_init(&ctx))) {\n    logError() << \"Libusb init failed (\" << error << \"): \"\n               << libusb_strerror((enum libusb_error) error) << \".\";\n    return false;\n  }\n\n  libusb_device **lst;\n  if (0 == (num = libusb_get_device_list(ctx, &lst))) {\n    logDebug() << \"No USB devices found at all.\";\n    // unref devices and free list\n    libusb_free_device_list(lst, 1);\n    libusb_exit(ctx);\n    return false;\n  }\n\n  USBDeviceHandle addr = _device.value<USBDeviceHandle>();\n  logDebug() << \"Search for a device matching VID:PID \"\n             << QString::number(_vid, 16) << \":\" << QString::number(_pid, 16)\n             << \" at bus \" << addr.bus << \", device \" << addr.device << \".\";\n  bool found = false;\n  for (int i=0; (i<num)&&(nullptr!=lst[i]); i++) {\n    libusb_device_descriptor descr;\n    libusb_get_device_descriptor(lst[i], &descr);\n    found = ( (_vid == descr.idVendor) && (_pid == descr.idProduct) &&\n              (libusb_get_bus_number(lst[i])==addr.bus) &&\n              (libusb_get_device_address(lst[i])==addr.device) );\n    if (found) {\n      logDebug() << \"Found device on bus=\" << libusb_get_bus_number(lst[i])\n                 << \", device=\" << libusb_get_device_address(lst[i])\n                 << \" with \" << QString::number(_vid, 16) << \":\" << QString::number(_pid, 16) << \".\";\n      break;\n    }\n  }\n\n  // Free list and context\n  libusb_free_device_list(lst, 1);\n  libusb_exit(ctx);\n\n  // done.\n  return found;\n}\n\nbool\nUSBDeviceDescriptor::validSerial() const {\n  QSerialPortInfo info(_device.toString());\n\n  logDebug() << \"Check if serial port \" << _device.toString() << \" still exisist and has VID:PID \"\n             << QString::number(_vid, 16) << \":\" << QString::number(_pid, 16) << \".\";\n\n  if (info.isNull()) {\n    logDebug() << \"Serial port \" << info.portName() << \"(\"\n               << _device.toString() << \") is not valid anymore.\";\n    return false;\n  }\n\n  if (info.hasProductIdentifier() && info.hasVendorIdentifier())\n    return ((_vid == info.vendorIdentifier()) && (_pid == info.productIdentifier()));\n  return true;\n}\n\nQString\nUSBDeviceDescriptor::description() const {\n  if (USBDeviceInfo::Class::Serial == _class) {\n    return QString(\"Serial interface '%1'\").arg(_device.toString());\n  } else if (USBDeviceInfo::Class::DFU == _class) {\n    USBDeviceHandle addr = _device.value<USBDeviceHandle>();\n    return QString(\"USB device in DFU mode: bus %1, device %2\").arg(addr.bus).arg(addr.device);\n  } else if (USBDeviceInfo::Class::HID == _class) {\n    USBDeviceHandle addr = _device.value<USBDeviceHandle>();\n    return QString(\"USB HID: bus %1, device %2\").arg(addr.bus).arg(addr.device);\n  } else if (USBDeviceInfo::Class::C7K == _class) {\n    USBDeviceHandle addr = _device.value<USBDeviceHandle>();\n    return QString(\"USB C7000 HT: bus %1, device %2\").arg(addr.bus).arg(addr.device);\n  }\n  return \"Invalid\";\n}\n\nconst QVariant &\nUSBDeviceDescriptor::device() const {\n  return _device;\n}\n\nQString\nUSBDeviceDescriptor::deviceHandle() const {\n  switch (_class) {\n  case Class::None:\n    break;\n  case Class::DFU:\n  case Class::HID:\n  case Class::C7K:\n    return QString(\"%1:%2\").arg(_device.value<USBDeviceHandle>().bus)\n        .arg(_device.value<USBDeviceHandle>().device);\n  case Class::Serial:\n    return _device.toString();\n  }\n\n  return \"[invalid]\";\n}\n\nQList<USBDeviceDescriptor>\nUSBDeviceDescriptor::detect(bool saveOnly) {\n  QList<USBDeviceDescriptor> res;\n  res.append(AnytoneGD32Interface::detect(saveOnly));\n  res.append(AnytoneSTM32Interface::detect(saveOnly));\n  res.append(OpenGD77Interface::detect(saveOnly));\n  res.append(RadioddityInterface::detect(saveOnly));\n  res.append(TyTInterface::detect(saveOnly));\n  res.append(DR1801UVInterface::detect(saveOnly));\n  res.append(C7000Device::detect(saveOnly));\n  return res;\n}\n\n"
  },
  {
    "path": "lib/usbdevice.hh",
    "content": "/** @defgroup detect Device detection and enumeration.\n * This module collects classes and functions to discover and select interfaces to connected radios.\n *\n * With an increasing number of supported devices, the issue arises, that auto detection of\n * radios may fail or may even harmful. Some manufacturers simply use generic USB-serial chips\n * within the cable to talk to them over USB. These chips may also be used in other devices that may\n * react harmful to qdmrs attempts to identify them. Moreover, the assumption of the first detected\n * device with a specific VID:PID combination may not be valid. To this end, some means of\n * discovering possible radios and selecting a specific one by the user are needed. Also some radios\n * do not identify themselves before any action is performed (i.e., reading, writing the codeplug\n * or callsign db). Hence, for some devices, the user must specify the type of the radio. The latter\n * concerns the Kydera CDR-300UV, Retevis RT73 and similar devices.\n *\n * This module specifies the classes and functions to discover possible radios and to address each\n * one uniquely. They allow for an implementation of a semi-automatic device detection. That is,\n * for the majority of radios, if a single matching VID:PID combination is found, it can be assumed\n * that this device is a radio and it can then be identified by sending commands to it. Some radios,\n * however, like the Kydera CDR-300UV, cannot be identified before the actual codeplug read or\n * write operation. They simply do not provide a command for identification.\n *\n * For a save semi-automatic detection the following steps are performed:\n * @dotfile autodetect.dot \"Semi-automatic radio detection\"\n *\n *   -# Search for all USB devices with known VID:PID combinations. This can be done using the\n *      @c USBDeviceDescriptor::detect method. It returns a list of matching device descriptors\n *      found. If only one device is found, one may continue with that one if it is safe to assume\n *      that the device detected is a DMR radio. The latter is not true for radios using generic\n *      USB CDC-ACM chips, as other serial devices may be connected. If several devices are found or\n *      it is not save to assume a DMR radio, the user must select a device.\n *   -# Once the USB device is selected, one needs to identify the connected radio. Unfortunately,\n *      not all radios can be identified easily by simply sending a command to it. To this end,\n *      one first needs to check if the selected device is identifiable. That is, if the protocol\n *      provides commands to identify the connected radio. The @c USBDeviceInfo provides this\n *      information. If a device is not identifiable, the user must specify the specific connected\n *      radio. This can be done by obtaining all known radios matching the selected USB device\n *      (VID:PID) by calling RadioInfo::allRadios, passing the @c USBDeviceDescriptor.\n *\n * @section detectExample A example for AnyTone devices\n * This example tries to detect an AnyTone device and reads the binary codeplug from it. Once the\n * codeplug is read, it is decoded into its generic device independent representation (@c Config).\n *\n * @code\n * #include \"libdmrconf/usbdevice.hh\"\n * #include \"libdmrconf/anytone_radio.hh\"\n *\n * int main(void)\n * {\n *   // First, search matching devices (only AnyTones)\n *   // to find all supported devices, call @c USBDescriptor::detect();\n *   QList<USBDeviceDescriptor> devices = AnytoneInterface::detect();\n *   if (1 != devices.count()) {\n *     // Either none or more than one device found...\n *     return -1;\n *   }\n *\n *   // A place to put error messages\n *   ErrorStack err;\n *   // Dedetect the specific radio and get radio descriptor.\n *   // To detect any radio based on the selected descriptor, call @c Radio::detect().\n *   Radio *radio = AnytoneRadio::detect(devices.first(), RadioInfo(), err);\n *   if (nullptr == radio) {\n *     // There went something wrong, check err.\n *     return -1;\n *   }\n *\n *   // Read codeplug from device blocking.\n *   if (! radio->startDownload(true, err)) {\n *     // Some read error, check err.\n *     delete radio;\n *     return -1;\n *   }\n *\n *   // Decode codeplug into generic representation\n *   Config genericCodeplug;\n *   if (! radio->codeplug().decode(&genericCodeplug, err)) {\n *     // Some decoding error, check err.\n *     delete radio;\n *     return -1;\n *   }\n *\n *   // Do whatever you like with the codeplug.\n *\n *   return 0;\n * }\n * @endcode\n *\n * @ingroup rif */\n\n#ifndef USBDEVICE_HH\n#define USBDEVICE_HH\n\n#include <inttypes.h>\n#include <QVariant>\n\n/** Combines the USB bus and device number, to address a USB device uniquely.\n *\n * @ingroup detect */\nstruct USBDeviceHandle {\n  uint8_t bus;         ///< Holds the bus number.\n  uint8_t device;      ///< Holds the device address.\n  uint32_t locationId; ///< On MacOS, holds the location ID.\n\n  /** Empty constructor. */\n  USBDeviceHandle();\n  /** Constructor from bus and device number. */\n  USBDeviceHandle(uint8_t busno, uint8_t deviceno, uint32_t locid=0);\n\n  /** Compares only wrt bus and device number. */\n  bool operator==(const USBDeviceHandle &other);\n};\nQ_DECLARE_METATYPE(USBDeviceHandle)\n\n/** Generic information about a possible radio interface.\n *\n * This class combines the USB vendor, product ID and some meta information about the interface.\n * In particular if it is safe to access the device without user ineraction and if the protocol\n * implements means for identifying the specific radio.\n *\n * @ingroup detect */\nclass USBDeviceInfo\n{\npublic:\n  /** Possible interface types. */\n  enum class Class {\n    None,       ///< Class for invalid interface info.\n    Serial,     ///< Serial port interface class.\n    DFU,        ///< DFU interface class.\n    HID,        ///< HID (human-interface device) interface class.\n    C7K         ///< Raw USB access to C7000 devices.\n  };\n\npublic:\n  /** Empty constructor. */\n  USBDeviceInfo();\n  /** Constructor from class, VID and PID. */\n  USBDeviceInfo(Class cls, uint16_t vid, uint16_t pid, bool save=true);\n  /** Destructor. */\n  virtual ~USBDeviceInfo();\n\n  /** Copy constructor. */\n  USBDeviceInfo(const USBDeviceInfo &other);\n  /** Assignment. */\n  USBDeviceInfo &operator =(const USBDeviceInfo &other);\n\n  /** Comparison. */\n  bool operator ==(const USBDeviceInfo &other) const;\n  /** Comparison. */\n  bool operator !=(const USBDeviceInfo &other) const;\n\n  /** Returns @c true if the interface info is valid. */\n  bool isValid() const;\n\n  /** Returns the interface class. */\n  Class interfaceClass() const;\n\n  /** Returns @c true, if a vendor ID is set. */\n  bool hasVendorID() const;\n  /** Returns the vendor ID or 0 if not set. */\n  uint16_t vendorId() const;\n  /** Returns @c true, if a product ID is set. */\n  bool hasProductID() const;\n  /** Returns the product ID or 0 if not set. */\n  uint16_t productId() const;\n\n  /** Returns a brief human readable description of the interface. */\n  QString description() const;\n\n  /** Returns a more extensive human readable description of the interface. */\n  QString longDescription() const;\n\n  /** Returns @c true if it is safe to send commands to this device without user approval.\n   * This is true for radios which use somewhat unique VID:PIDs. Radios with generic USB-serial\n   * chips are not save, as other devices may use the same chip and sending data to these devices\n   * may be harmful. */\n  bool isSave() const;\n\nprotected:\n  /** The class of the interface. */\n  Class _class;\n  /** The USB vid. */\n  uint16_t _vid;\n  /** The USB pid. */\n  uint16_t _pid;\n  /** If @c true, it is safe to send commands to the device without user approval. */\n  bool _save;\n};\n\n\n/** Base class for all radio interface descriptors representing a unique interface to a\n * connected radio.\n *\n * This class extends the @c USBDeviceInfo by some information to identify a USB uniquely. This is\n * either the bus and device number or the path to the serial port.\n *\n * @ingroup detect */\nclass USBDeviceDescriptor: public USBDeviceInfo\n{\nprotected:\n  /** Hidden constructor from info and path string. */\n  USBDeviceDescriptor(const USBDeviceInfo &info, const QString &device);\n  /** Hidden constructor from info and USB device address. */\n  USBDeviceDescriptor(const USBDeviceInfo &info, const USBDeviceHandle &device);\n\npublic:\n  /** Empty constructor. */\n  USBDeviceDescriptor();\n  /** Copy constructor. */\n  USBDeviceDescriptor(const USBDeviceDescriptor &other);\n\n  /** Assignment */\n  USBDeviceDescriptor &operator =(const USBDeviceDescriptor &other);\n\n  /** Returns @c true if the descriptor is still valid. That is, if the described device is still\n   * connected. */\n  bool isValid() const;\n\n  /** Returns a human readable description of the device. */\n  QString description() const;\n\n  /** Returns the device information identifying the interface uniquely. */\n  const QVariant &device() const;\n  /** Returns a unique string representation of the device information. */\n  QString deviceHandle() const;\n\npublic:\n  /** Searches for all connected radios (may contain false positives). */\n  static QList<USBDeviceDescriptor> detect(bool saveOnly=true);\n\nprotected:\n  /** Checks a serial port. */\n  bool validSerial() const;\n  /** Checks a raw USB device. */\n  bool validRawUSB() const;\n\nprotected:\n  /** Holds some information to identify the radio interface uniquely. */\n  QVariant _device;\n};\n\n\nnamespace std {\ntemplate <> struct hash<USBDeviceInfo>\n{\n  // seed is optional\n  inline size_t operator()(const USBDeviceInfo &key, size_t seed = 0) const {\n    return qHash((unsigned)key.interfaceClass(),\n                 qHash(key.vendorId(),\n                       qHash(key.productId(),\n                             qHash(key.isSave(), seed))));\n  }\n};\n}\n\n#endif // USBDEVICE_HH\n"
  },
  {
    "path": "lib/usbserial.cc",
    "content": "#include \"usbserial.hh\"\n#include \"logger.hh\"\n#include <QFileInfo>\n#include <QSerialPortInfo>\n#include <QThread>\n\n/* ******************************************************************************************** *\n * Implementation of USBSerial::Info\n * ******************************************************************************************** */\nUSBSerial::Descriptor::Descriptor(uint16_t vid, uint16_t pid, const QString &device, bool isSave)\n  : USBDeviceDescriptor(USBDeviceInfo(Class::Serial, vid, pid, isSave), device)\n{\n  // pass...\n}\n\n/* ******************************************************************************************** *\n * Implementation of USBSerial\n * ******************************************************************************************** */\nUSBSerial::USBSerial(const USBDeviceDescriptor &descriptor, BaudRate rate, const ErrorStack &err, QObject *parent)\n  : QSerialPort(parent), RadioInterface()\n{\n  if (USBDeviceInfo::Class::Serial != descriptor.interfaceClass()) {\n    errMsg(err) << \"Cannot open serial port for a non-serial descriptor: \"\n                << descriptor.description();\n  }\n\n  logDebug() << \"Try to open \" << descriptor.description() << \".\";\n  QSerialPortInfo port(descriptor.device().toString());\n  this->setPort(port);\n  if (! setParity(QSerialPort::NoParity)) {\n    logWarn() << \"Cannot set parity of the serial port to none.\";\n  }\n  if (! setStopBits(QSerialPort::OneStop)) {\n    logWarn() << \"Cannot set stop bit.\";\n  }\n  if (! setBaudRate(rate)) {\n    logWarn() << \"Cannot set speed to \" << rate << \" baud.\";\n  }\n  if (! setFlowControl(QSerialPort::HardwareControl)) {\n    logWarn() << \"Cannot enable hardware flow control.\";\n  }\n\n  if (! this->open(QIODevice::ReadWrite)) {\n#ifdef Q_OS_UNIX\n    QFileInfo portFileInfo(port.systemLocation());\n    if (portFileInfo.exists() &&\n        ((! portFileInfo.isReadable()) || (! portFileInfo.isWritable())))\n    {\n      QString owner = QString(portFileInfo.permission(QFileDevice::ReadOwner) ? \"r\" : \"-\")\n          + (portFileInfo.permission(QFile::WriteOwner) ? \"w\" : \"-\")\n          + (portFileInfo.permission(QFile::ExeOwner) ? \"x\" : \"-\");\n      QString group = QString(portFileInfo.permission(QFileDevice::ReadGroup) ? \"r\" : \"-\")\n          + (portFileInfo.permission(QFile::WriteGroup) ? \"w\" : \"-\")\n          + (portFileInfo.permission(QFile::ExeGroup) ? \"x\" : \"-\");\n      QString other = QString(portFileInfo.permission(QFileDevice::ReadOther) ? \"r\" : \"-\")\n          + (portFileInfo.permission(QFile::WriteOther) ? \"w\" : \"-\")\n          + (portFileInfo.permission(QFile::ExeOther) ? \"x\" : \"-\");\n      errMsg(err) << \"Insufficient rights to read or write '\" << port.systemLocation()\n                  << \"' (\" << port.description() << \"): \"\n                  << portFileInfo.owner() << \": \" << owner << \", \"\n                  << portFileInfo.group() << \": \" << group << \", other: \" << other << \".\";\n      if (portFileInfo.permission(QFile::ReadGroup | QFile::WriteGroup))\n        errMsg(err) << \"A membership in the group \" << portFileInfo.group()\n                    << \" would grant access.\";\n    }\n#endif\n    errMsg(err) << \"Cannot open serial port '\" << port.portName()\n                << \"': \" << this->errorString() << \".\";\n    return;\n  }\n\n  logDebug() << \"Opened serial port \" << this->portName() << \" with \"\n             << this->baudRate() << \"baud.\";\n\n  connect(this, SIGNAL(aboutToClose()), this, SLOT(onClose()));\n  connect(this, SIGNAL(errorOccurred(QSerialPort::SerialPortError)),\n          this, SLOT(onError(QSerialPort::SerialPortError)));\n  connect(this, SIGNAL(dataTerminalReadyChanged(bool)), this, SLOT(signalingChanged()));\n  connect(this, SIGNAL(requestToSendChanged(bool)), this, SLOT(signalingChanged()));\n}\n\nUSBSerial::~USBSerial() {\n  if (isOpen())\n    close();\n}\n\nbool\nUSBSerial::isOpen() const {\n  return QSerialPort::isOpen();\n}\n\nvoid\nUSBSerial::close() {\n  if (isOpen())\n    QSerialPort::close();\n}\n\nvoid\nUSBSerial::onError(QSerialPort::SerialPortError err) {\n  logError() << \"Serial port error: (\" << err << \") \" << errorString() << \".\";\n}\n\nvoid\nUSBSerial::onClose() {\n  logDebug() << \"Serial port will close now.\";\n}\n\nvoid\nUSBSerial::signalingChanged() {\n  logDebug() << \"Pinout signals changed to \" << formatPinoutSignals() << \".\";\n}\n\nQList<USBDeviceDescriptor>\nUSBSerial::detect(uint16_t vid, uint16_t pid, bool isSave) {\n  QList<USBDeviceDescriptor> interfaces;\n  // Find matching serial port by VID/PID.\n  logDebug() << \"Search for serial port with matching VID:PID \" <<\n                QString::number(vid, 16) << \":\" << QString::number(pid, 16) << \".\";\n  QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();\n  foreach (QSerialPortInfo port, ports) {\n    if (port.hasProductIdentifier() && (pid == port.productIdentifier()) &&\n        port.hasVendorIdentifier() && (vid == port.vendorIdentifier())) {\n      interfaces.append(Descriptor(vid, pid, port.portName(), isSave));\n      logDebug() << \"Found \" << port.portName() << \" (USB \"\n                 << QString::number(vid, 16) << \":\" << QString::number(pid, 16) << \").\";\n    }\n  }\n  return interfaces;\n}\n\n\nQList<USBDeviceDescriptor>\nUSBSerial::detect() {\n  QList<USBDeviceDescriptor> interfaces;\n  logDebug() << \"Search for serial ports.\";\n  QList<QSerialPortInfo> ports = QSerialPortInfo::availablePorts();\n  foreach (QSerialPortInfo port, ports) {\n    if (port.hasProductIdentifier() && port.hasVendorIdentifier()) {\n      interfaces.append(Descriptor(port.vendorIdentifier(), port.productIdentifier(), port.portName(), false));\n      logDebug() << \"Found \" << port.portName() << \" (USB \"\n                 << QString::number(port.vendorIdentifier(), 16) << \":\"\n                 << QString::number(port.productIdentifier(), 16) << \").\";\n    }\n  }\n  return interfaces;\n}\n\nQString\nUSBSerial::formatPinoutSignals() {\n  if (QSerialPort::NoSignal == pinoutSignals())\n    return \"None\";\n\n  QStringList res;\n  if (QSerialPort::DataTerminalReadySignal & pinoutSignals())\n    res.append(\"Data Terminal Ready\");\n  if (QSerialPort::DataCarrierDetectSignal & pinoutSignals())\n    res.append(\"Data Carrier Detect\");\n  if (QSerialPort::DataSetReadySignal & pinoutSignals())\n    res.append(\"Data Set Ready\");\n  if (QSerialPort::RingIndicatorSignal & pinoutSignals())\n    res.append(\"Ring Indicator\");\n  if (QSerialPort::RequestToSendSignal & pinoutSignals())\n    res.append(\"Request To Send\");\n  if (QSerialPort::ClearToSendSignal & pinoutSignals())\n    res.append(\"Clear To Send\");\n  if (QSerialPort::SecondaryTransmittedDataSignal & pinoutSignals())\n    res.append(\"Secondary Transmitted Data\");\n  if (QSerialPort::SecondaryReceivedDataSignal & pinoutSignals())\n    res.append(\"Secondary Received Data\");\n\n  return res.join(\", \");\n}\n"
  },
  {
    "path": "lib/usbserial.hh",
    "content": "#ifndef USBSERIAL_HH\n#define USBSERIAL_HH\n\n#include <QObject>\n#include <QSerialPort>\n#include \"radiointerface.hh\"\n#include \"errorstack.hh\"\n\n/** Implements a serial connection to a radio via USB.\n *\n * The correct serial port is selected by the given VID and PID to the constructor.\n *\n * @ingroup rif\n */\nclass USBSerial : public QSerialPort, public RadioInterface\n{\n  Q_OBJECT\n\npublic:\n  /** Specialization of radio interface info for serial ports. */\n  class Descriptor: public USBDeviceDescriptor {\n  public:\n    /** Constructor from VID, PID and device path. */\n    Descriptor(uint16_t vid, uint16_t pid, const QString &device, bool isSave=false);\n  };\n\nprotected:\n  /** Constructs an opens new serial interface to the devices identified by the given vendor and\n   * product IDs.\n   * @param descriptor Specifies the device to open.\n   * @param rate Specifies the transferrate in baud.\n   * @param err The error stack, messages are put onto.\n   * @param parent Specifies the parent object. */\n  explicit USBSerial(const USBDeviceDescriptor &descriptor,\n                     QSerialPort::BaudRate rate=QSerialPort::Baud115200,\n                     const ErrorStack &err=ErrorStack(), QObject *parent=nullptr);\n\npublic:\n  /** Destructor. */\n  virtual ~USBSerial();\n\n  /** If @c true, the device has been found and is open. */\n  bool isOpen() const override;\n  /** Closes the interface to the device. */\n  void close() override;\n\npublic:\n  /** Searches for all USB serial ports with the specified VID/PID. */\n  static QList<USBDeviceDescriptor> detect(uint16_t vid, uint16_t pid, bool isSave=true);\n  /** Searches for all USB serial ports */\n  static QList<USBDeviceDescriptor> detect();\n\nprotected slots:\n  /** Callback for serial interface errors. */\n  void onError(QSerialPort::SerialPortError error_t);\n  /** Callback when closing interface. */\n  void onClose();\n  /** Signaling callback. */\n  void signalingChanged();\n\nprotected:\n  /** Serializes the pinout signals. */\n  QString formatPinoutSignals();\n};\n\n#endif // USBSERIAL_HH\n"
  },
  {
    "path": "lib/userdatabase.cc",
    "content": "#include \"userdatabase.hh\"\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QStandardPaths>\n#include <QFile>\n#include <QDir>\n#include <QNetworkReply>\n#include <QtConcurrent>\n#include <algorithm>\n#include \"logger.hh\"\n#include <cmath>\n\n\n/* ********************************************************************************************* *\n * Implementation of User\n * ********************************************************************************************* */\nUserDatabase::User::User()\n  : id(0)\n{\n  // pass...\n}\n\nUserDatabase::User::User(const QJsonObject &obj)\n  : id(obj.value(\"id\").toInt()), call(obj.value(\"callsign\").toString()),\n    name(obj.value(\"fname\").toString()), surname(obj.value(\"surname\").toString()),\n    city(obj.value(\"city\").toString()), state(obj.value(\"state\").toString()),\n    country(obj.value(\"country\").toString()), comment(obj.value(\"remarks\").toString())\n{\n  // pass...\n}\n\nunsigned\nUserDatabase::User::distance(unsigned id) const {\n  // Fix number of digits\n  int a = this->id, b = id;\n  int ad = std::ceil(std::log10(a));\n  int bd = std::ceil(std::log10(b));\n  if (ad > bd)\n    b *= std::pow(10u, (ad-bd));\n  else if (bd > ad)\n    a *= std::pow(10u, (bd-ad));\n  // Distance is just the difference between these two numbers\n  // this ensures a small distance between two numbers with the same\n  // prefix.\n  return std::abs(a-b);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of UserDatabase\n * ********************************************************************************************* */\nUserDatabase::UserDatabase(bool parallel, unsigned updatePeriodDays, QObject *parent)\n  : QAbstractTableModel(parent), _user(), _network()\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(downloadFinished(QNetworkReply*)));\n\n  if ((! exists()) || (updatePeriodDays < dbAge()))\n    download();\n  else {\n    if (parallel) {\n      _parsing = QtConcurrent::run([this]() { QList<User> users; this->parse(users); return users;})\n                   .then(this, [this](const QList<User> &users){ return this->load(users); });\n    } else {\n      load();\n    }\n  }\n}\n\nqint64\nUserDatabase::count() const {\n  return _user.size();\n}\n\nbool\nUserDatabase::exists() const {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFileInfo info(path + \"/user.json\");\n  return info.isFile() && info.isReadable();\n}\n\nbool\nUserDatabase::ready() const {\n  return ! _user.empty();\n}\n\nbool\nUserDatabase::load() {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  return load(path+\"/user.json\");\n}\n\nconst UserDatabase::User &\nUserDatabase::user(int idx) const {\n  return _user[idx];\n}\n\nbool\nUserDatabase::load(const QString &filename) {\n  QList<User> users;\n  if (! parse(filename, users))\n    return false;\n\n  auto res =  load(users);\n  logDebug() << \"Loaded user database with \" << _user.size() << \" entries from \" << filename << \".\";\n  return res;\n}\n\nbool\nUserDatabase::load(const QList<User> &users) {\n  beginResetModel();\n  _user.clear(); emit readyChanged(false);\n  _user.reserve(users.size());\n  for (auto user: users)\n    _user.append(user);\n  endResetModel();\n\n  if (ready())\n    emit readyChanged(ready());\n  emit loaded();\n  return true;\n}\n\n\nbool\nUserDatabase::parse(QList<User> &users) {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  return parse(path+\"/user.json\", users);\n}\n\nbool\nUserDatabase::parse(const QString &filename, QList<User> &users)\n{\n  QFile file(filename);\n  if (! file.open(QIODevice::ReadOnly)) {\n    QString msg = QString(\"Cannot open user list '%1': %2\").arg(filename).arg(file.errorString());\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  QByteArray data = file.readAll();\n  file.close();\n\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(data, &err);\n  if (doc.isEmpty()) {\n    QString msg = \"Failed to load user DB: \" + err.errorString();\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  if (! doc.isObject()) {\n    QString msg = \"Failed to load user DB: JSON document is not an object!\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  if (! doc.object().contains(\"users\")) {\n    QString msg = \"Failed to load user DB: JSON object does not contain 'users' item.\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n  if (! doc.object()[\"users\"].isArray()) {\n    QString msg = \"Failed to load user DB: 'users' item is not an array.\";\n    logError() << msg;\n    emit error(msg);\n    return false;\n  }\n\n  users.clear();\n  QJsonArray array = doc.object()[\"users\"].toArray();\n  users.reserve(array.size());\n  for (int i=0; i<array.size(); i++) {\n    User user(array.at(i).toObject());\n    if (user.isValid())\n      users.append(user);\n  }\n  // Sort repeater w.r.t. their IDs\n  std::stable_sort(_user.begin(), _user.end(), [](const User &a, const User &b){ return a.id < b.id; });\n\n  return true;\n}\n\nvoid\nUserDatabase::sortUsers(unsigned id) {\n  // Sort repeater w.r.t. distance to ID\n  std::stable_sort(_user.begin(), _user.end(), [id](const User &a, const User &b){\n    return a.distance(id) < b.distance(id);\n  });\n}\n\nvoid\nUserDatabase::sortUsers(const QSet<unsigned> &ids) {\n  if (0 == ids.count())\n    return;\n\n  // Sort repeater w.r.t. distance to each ID\n  std::stable_sort(_user.begin(), _user.end(), [ids](const User &a, const User &b){\n    QSet<unsigned>::const_iterator id=ids.begin();\n    unsigned min_a = a.distance(*id), min_b = b.distance(*id);\n    id++;\n    for (; id!=ids.end(); id++) {\n      min_a = std::min(min_a, a.distance(*id));\n      min_b = std::min(min_b, b.distance(*id));\n    }\n    return min_a < min_b;\n  });\n}\n\nvoid\nUserDatabase::download() {\n  QUrl url(\"https://database.radioid.net/static/users.json\");\n  QNetworkRequest request(url);\n  _network.get(request);\n}\n\nvoid\nUserDatabase::downloadFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    QString msg = QString(\"Cannot download user database: %1\").arg(reply->errorString());\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n  QFile file(path+\"/user.json\");\n  QDir directory;\n  if ((! directory.exists(path)) && (!directory.mkpath(path))) {\n    QString msg = QString(\"Cannot create path '%1'.\").arg(path);\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n  if (! file.open(QIODevice::WriteOnly)) {\n    QString msg = QString(\"Cannot save user database at '%1'.\").arg(path+\"/user.json\");\n    logError() << msg;\n    emit error(msg);\n    return;\n  }\n\n  file.write(reply->readAll());\n  file.flush();\n  file.close();\n\n  load();\n  reply->deleteLater();\n}\n\nunsigned\nUserDatabase::dbAge() const {\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + \"/user.json\";\n  QFileInfo info(path);\n  if (! info.exists())\n    return -1;\n  return info.lastModified().daysTo(QDateTime::currentDateTime());\n}\n\nint\nUserDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return _user.size();\n}\n\nint\nUserDatabase::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return 3;\n}\n\nQVariant\nUserDatabase::data(const QModelIndex &index, int role) const {\n  if ((Qt::EditRole != role) && ((Qt::DisplayRole != role)))\n    return QVariant();\n\n  if (index.row() >= _user.size())\n    return QVariant();\n\n  if (0 == index.column()) {\n    // Call\n    if (Qt::DisplayRole == role) {\n      if (_user[index.row()].surname.isEmpty()) {\n        if (_user[index.row()].name.isEmpty()) {\n          return _user[index.row()].call;\n        } else {\n          return tr(\"%1 (%2)\")\n              .arg(_user[index.row()].call)\n              .arg(_user[index.row()].name);\n        }\n      } else {\n        return tr(\"%1 (%2, %3)\")\n            .arg(_user[index.row()].call)\n            .arg(_user[index.row()].name)\n            .arg(_user[index.row()].surname);\n      }\n    } else {\n      return _user[index.row()].call;\n    }\n  } else if (1 == index.column()) {\n    // ID\n    return _user[index.row()].id;\n  } else if (2 == index.column()) {\n    // Country\n    return _user[index.column()].country;\n  }\n\n  return QVariant();\n}\n\n"
  },
  {
    "path": "lib/userdatabase.hh",
    "content": "#ifndef USERDATABASE_HH\n#define USERDATABASE_HH\n\n#include <QObject>\n#include <QVector>\n#include <QHash>\n#include <QJsonObject>\n#include <QNetworkAccessManager>\n#include <QAbstractTableModel>\n#include <QSortFilterProxyModel>\n#include <QGeoPositionInfoSource>\n#include <QFuture>\n\n/** Auto-updating DMR user database.\n *\n * This class represents the complete DMR user database. The user database gets downloaded from\n * https://www.radioid.net/static/users.json and kept up-to-date by re-downloading it\n * periodically (by default every 30 days). This user database gets used in the GUI application\n * to help assemble private call contacts and to assemble so-called CSV callsign databases, that\n * are programmable to some DMR radios to resolve the DMR ID to callsigns and names.\n *\n * @ingroup util */\nclass UserDatabase : public QAbstractTableModel\n{\n  Q_OBJECT\n\n  /** Get notification, once the database has been loaded. */\n  Q_PROPERTY(bool ready READ ready NOTIFY readyChanged FINAL)\n\npublic:\n  /** Represents the user information within the @c UserDatabase. */\n  class User {\n  public:\n    /** Empty constructor. */\n    User();\n    /** Constructs entry from JSON object. */\n    User(const QJsonObject &obj);\n\n    /** Returns @c true if the entry is valid. */\n    inline bool isValid() const { return 0 != id; }\n\n    /** Returns the \"distance\" between this user and the given ID. */\n    unsigned distance(unsigned id) const;\n\n    /** The DMR ID of the user. */\n    unsigned id;\n    /** The callsign of the user. */\n    QString call;\n    /** The name of the user. */\n    QString name;\n    /** The surname of the user. */\n    QString surname;\n    /** The city of the user. */\n    QString city;\n    /** The state of the user. */\n    QString state;\n    /** The country of the user. */\n    QString country;\n    /** Some arbitrary comment or text. */\n    QString comment;\n  };\n\npublic:\n  /** Constructs the user-database.\n   * The constructor will download the current user database if it was not downloaded yet or\n   * if the downloaded version is older than @c updatePeriodDays days. */\n  explicit UserDatabase(bool parallel, unsigned updatePeriodDays=30, QObject *parent=nullptr);\n\n  /** Returns the number of users. */\n  qint64 count() const;\n\n  /** Retruns @c true, if the user database file exists. */\n  bool exists() const;\n\n  /** Loads all entries from the downloaded user database. */\n  bool load();\n  /** Loads all entries from the downloaded user database at the specified location. */\n  bool load(const QString &filename);\n  /** Returns @c true, if the database has been loaded. */\n  bool ready() const;\n\n  /** Sorts users with respect to the distance to the given ID. */\n  void sortUsers(unsigned id);\n  /** Sorts users with respect to the minimum distance to the given IDs. */\n  void sortUsers(const QSet<unsigned> &ids);\n\n  /** Returns the user with index @c idx. */\n  const User &user(int idx) const;\n\n  /** Returns the age of the database in days. */\n  unsigned dbAge() const;\n\n  /** Implements the QAbstractTableModel interface, returns the number of rows (number of entries). */\n  int rowCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface, returns the number of columns. */\n  int columnCount(const QModelIndex &parent=QModelIndex()) const;\n  /** Implements the QAbstractTableModel interface, return the entry data. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n\nsignals:\n  /** Gets emitted once the call-sign database has been loaded. */\n  void loaded();\n  /** Gets emitted if the loading of the call-sign database fails. */\n  void error(const QString &msg);\n  /** Gets emitted, once the database has been loaded or cleard.\n   * @warning This event might be emitted by another thread. */\n  void readyChanged(bool ready);\n\npublic slots:\n  /** Starts the download of the user database. */\n  void download();\n\nprivate slots:\n  /** Gets called whenever the download is complete. */\n  void downloadFinished(QNetworkReply *reply);\n  /** Parses cache and stores all users in given result list. */\n  bool parse(QList<User> &users);\n  /** Parses the given file and stores all users in given result list. */\n  bool parse(const QString &filename, QList<User> &users);\n  /** Loads all entries from the parsed file. */\n  bool load(const QList<User> &users);\n\nprivate:\n  /** Holds all users sorted by their ID. */\n  QVector<User>         _user;\n  /** The network access used for downloading. */\n  QNetworkAccessManager _network;\n  /** The current parallel task of parsing the database. */\n  QFuture<bool> _parsing;\n};\n\n\n#endif // USERDATABASE_HH\n"
  },
  {
    "path": "lib/utils.cc",
    "content": "#include \"utils.hh\"\n#include <QRegularExpression>\n#include <QVector>\n#include <QHash>\n#include <cmath>\n#include <QRegularExpression>\n#include <yaml-cpp/yaml.h>\n\n// Maps APRS icon number to code-char\nstatic QVector<char> aprsIconCodeTable{\n  '!','\"','#','$','%','&','\\'','(',')','*','+',',','-','.','/','0',\n  '1','2','3','4','5','6', '7','8','9',':',';','<','=','>','?','@',\n  'A','B','C','D','E','F', 'G','H','I','J','K','L','M','N','O','P',\n  'Q','R','S','T','U','V', 'W','X','Y','Z','[','/',']','^','_','`',\n  'a','b','c','d','e','f', 'g','h','i','j','k','l','m','n','o','p',\n  'q','r','s','t','u','v','w','x','y','z','{','|','}','~'};\n\nstatic QHash<unsigned, QString> aprsIconNameTable{\n  {(unsigned)FMAPRSSystem::Icon::None, \"\"},\n  {(unsigned)FMAPRSSystem::Icon::PoliceStation, \"Police station\"},\n  {(unsigned)FMAPRSSystem::Icon::Digipeater, \"Digipeater\"},\n  {(unsigned)FMAPRSSystem::Icon::Phone, \"Phone\"},\n  {(unsigned)FMAPRSSystem::Icon::DXCluster, \"DX cluster\"},\n  {(unsigned)FMAPRSSystem::Icon::HFGateway, \"HF gateway\"},\n  {(unsigned)FMAPRSSystem::Icon::SmallPlane, \"Small plane\"},\n  {(unsigned)FMAPRSSystem::Icon::MobileSatelliteStation, \"Mobile Satellite station\"},\n  {(unsigned)FMAPRSSystem::Icon::WheelChair, \"Wheel chair\"},\n  {(unsigned)FMAPRSSystem::Icon::Snowmobile, \"Snowmobile\"},\n  {(unsigned)FMAPRSSystem::Icon::RedCross, \"Red cross\"},\n  {(unsigned)FMAPRSSystem::Icon::BoyScout, \"Boy scout\"},\n  {(unsigned)FMAPRSSystem::Icon::Home,  \"Home\"},\n  {(unsigned)FMAPRSSystem::Icon::X, \"X\"},\n  {(unsigned)FMAPRSSystem::Icon::RedDot, \"Red dot\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle0, \"Circle 0\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle1, \"Circle 1\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle2, \"Circle 2\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle3, \"Circle 3\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle4, \"Circle 4\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle5, \"Circle 5\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle6, \"Circle 6\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle7, \"Circle 7\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle8, \"Circle 8\"},\n  {(unsigned)FMAPRSSystem::Icon::Circle9, \"Circle 9\"},\n  {(unsigned)FMAPRSSystem::Icon::Fire, \"Fire\"},\n  {(unsigned)FMAPRSSystem::Icon::Campground, \"Campground\"},\n  {(unsigned)FMAPRSSystem::Icon::Motorcycle, \"Motorcycle\"},\n  {(unsigned)FMAPRSSystem::Icon::RailEngine, \"Rail engine\"},\n  {(unsigned)FMAPRSSystem::Icon::Car, \"Car\"},\n  {(unsigned)FMAPRSSystem::Icon::FileServer, \"File server\"},\n  {(unsigned)FMAPRSSystem::Icon::HCFuture, \"HC future\"},\n  {(unsigned)FMAPRSSystem::Icon::AidStation, \"Aid station\"},\n  {(unsigned)FMAPRSSystem::Icon::BBS, \"BBS\"},\n  {(unsigned)FMAPRSSystem::Icon::Canoe, \"Canoe\"},\n  {(unsigned)FMAPRSSystem::Icon::Eyeball, \"Eyeball\"},\n  {(unsigned)FMAPRSSystem::Icon::Tractor, \"Tractor\"},\n  {(unsigned)FMAPRSSystem::Icon::GridSquare, \"Grid square\"},\n  {(unsigned)FMAPRSSystem::Icon::Hotel, \"Hotel\"},\n  {(unsigned)FMAPRSSystem::Icon::TCPIP, \"TCP/IP\"},\n  {(unsigned)FMAPRSSystem::Icon::School, \"School\"},\n  {(unsigned)FMAPRSSystem::Icon::Logon, \"Logon\"},\n  {(unsigned)FMAPRSSystem::Icon::MacOS, \"MacOS\"},\n  {(unsigned)FMAPRSSystem::Icon::NTSStation, \"NTS station\"},\n  {(unsigned)FMAPRSSystem::Icon::Balloon, \"Balloon\"},\n  {(unsigned)FMAPRSSystem::Icon::PoliceCar, \"Police car\"},\n  {(unsigned)FMAPRSSystem::Icon::TBD, \"TBD\"},\n  {(unsigned)FMAPRSSystem::Icon::RV, \"RV\"},\n  {(unsigned)FMAPRSSystem::Icon::Shuttle, \"Shuttle\"},\n  {(unsigned)FMAPRSSystem::Icon::SSTV, \"SSTV\"},\n  {(unsigned)FMAPRSSystem::Icon::Bus, \"Bus\"},\n  {(unsigned)FMAPRSSystem::Icon::ATV, \"ATV\"},\n  {(unsigned)FMAPRSSystem::Icon::WXService, \"WX service\"},\n  {(unsigned)FMAPRSSystem::Icon::Helo, \"Helo\"},\n  {(unsigned)FMAPRSSystem::Icon::Yacht, \"Yacht\"},\n  {(unsigned)FMAPRSSystem::Icon::Windows, \"Windows\"},\n  {(unsigned)FMAPRSSystem::Icon::Jogger, \"Jogger\"},\n  {(unsigned)FMAPRSSystem::Icon::Triangle, \"Triangle\"},\n  {(unsigned)FMAPRSSystem::Icon::PBBS, \"PBBS\"},\n  {(unsigned)FMAPRSSystem::Icon::LargePlane, \"Large plane\"},\n  {(unsigned)FMAPRSSystem::Icon::WXStation, \"WX station\"},\n  {(unsigned)FMAPRSSystem::Icon::DishAntenna, \"Dish antenna\"},\n  {(unsigned)FMAPRSSystem::Icon::Ambulance, \"Ambulance\"},\n  {(unsigned)FMAPRSSystem::Icon::Bike, \"Bike\"},\n  {(unsigned)FMAPRSSystem::Icon::ICP, \"ICP\"},\n  {(unsigned)FMAPRSSystem::Icon::FireStation, \"Fire station\"},\n  {(unsigned)FMAPRSSystem::Icon::Horse, \"Horse\"},\n  {(unsigned)FMAPRSSystem::Icon::FireTruck, \"Fire truck\"},\n  {(unsigned)FMAPRSSystem::Icon::Glider, \"Glider\"},\n  {(unsigned)FMAPRSSystem::Icon::Hospital, \"Hospital\"},\n  {(unsigned)FMAPRSSystem::Icon::IOTA, \"IOTA\"},\n  {(unsigned)FMAPRSSystem::Icon::Jeep, \"Jeep\"},\n  {(unsigned)FMAPRSSystem::Icon::SmallTruck, \"Small truck\"},\n  {(unsigned)FMAPRSSystem::Icon::Laptop, \"Laptop\"},\n  {(unsigned)FMAPRSSystem::Icon::MicE, \"Mic-E\"},\n  {(unsigned)FMAPRSSystem::Icon::Node, \"Node\"},\n  {(unsigned)FMAPRSSystem::Icon::EOC, \"EOC\"},\n  {(unsigned)FMAPRSSystem::Icon::Rover, \"Rover\"},\n  {(unsigned)FMAPRSSystem::Icon::Grid, \"Grid\"},\n  {(unsigned)FMAPRSSystem::Icon::Antenna, \"Antenna\"},\n  {(unsigned)FMAPRSSystem::Icon::PowerBoat, \"Power boat\"},\n  {(unsigned)FMAPRSSystem::Icon::TruckStop, \"Truck stop\"},\n  {(unsigned)FMAPRSSystem::Icon::TruckLarge, \"Truck large\"},\n  {(unsigned)FMAPRSSystem::Icon::Van, \"Van\"},\n  {(unsigned)FMAPRSSystem::Icon::Water, \"Water\"},\n  {(unsigned)FMAPRSSystem::Icon::XAPRS, \"XAPRS\"},\n  {(unsigned)FMAPRSSystem::Icon::Yagi, \"Yagi\"},\n  {(unsigned)FMAPRSSystem::Icon::Shelter, \"Shelter\"}};\n\nQString\ndecode_unicode(const uint16_t *data, size_t size, uint16_t fill) {\n  QString res; res.reserve(size);\n  for (size_t i=0; (i<size) && (fill!=data[i]); i++) {\n    res.append(QChar(data[i]));\n  }\n  return res;\n}\n\nvoid\nencode_unicode(uint16_t *data, const QString &text, size_t size, uint16_t fill) {\n  for (size_t i=0; (i<size); i++) {\n    if (i<size_t(text.size()))\n      data[i] = text.at(i).unicode();\n    else\n      data[i] = fill;\n  }\n}\n\nQString\ndecode_ascii(const uint8_t *data, size_t size, uint16_t fill) {\n  QString res; res.reserve(size);\n  for (size_t i=0; (i<size) && (0!=data[i]) && (fill!=data[i]); i++)\n    res.append(QChar(data[i]));\n  return res;\n}\n\nvoid\nencode_ascii(uint8_t *data, const QString &text, size_t size, uint16_t fill) {\n  for (size_t i=0; (i<size); i++) {\n    if (i<size_t(text.size()))\n      data[i] = text.at(i).toLatin1();\n    else\n      data[i] = fill;\n  }\n}\n\nQString\ndecode_utf8(const uint8_t *data, size_t size, uint16_t fill) {\n  Q_UNUSED(fill)\n  return QString::fromUtf8((const char *)data, size);\n}\n\nvoid\nencode_utf8(uint8_t *data, const QString &text, size_t size, uint16_t fill) {\n  QByteArray buffer = text.toUtf8();\n  memset(data, fill, size);\n  memcpy(data, buffer.data(), std::min(size_t(buffer.size()), size));\n}\n\ndouble\ndecode_frequency(uint32_t bcd) {\n  double freq =\n      1e2  * ((bcd >> 28) & 0xf) +\n      1e1  * ((bcd >> 24) & 0xf) +\n      1.0  * ((bcd >> 20) & 0xf) +\n      1e-1 * ((bcd >> 16) & 0xf) +\n      1e-2 * ((bcd >> 12) & 0xf) +\n      1e-3 * ((bcd >>  8) & 0xf) +\n      1e-4 * ((bcd >>  4) & 0xf) +\n      1e-5 * ((bcd >>  0) & 0xf);\n  return freq;\n}\n\nuint32_t\nencode_frequency(double freq) {\n  uint32_t hz = std::round(freq * 1e6);\n  uint32_t a  = (hz / 100000000) % 10;\n  uint32_t b  = (hz /  10000000) % 10;\n  uint32_t c  = (hz /   1000000) % 10;\n  uint32_t d  = (hz /    100000) % 10;\n  uint32_t e  = (hz /     10000) % 10;\n  uint32_t f  = (hz /      1000) % 10;\n  uint32_t g  = (hz /       100) % 10;\n  uint32_t h  = (hz /        10) % 10;\n\n  return (a << 28) + (b << 24) + (c << 20) + (d << 16) + (e << 12) + (f << 8) + (g << 4) + h;\n}\n\n\nuint32_t decode_dmr_id_bin(const uint8_t *id) {\n  return ( (id[0]) | (id[1] << 8) | (id[2] << 16) );\n}\n\nvoid encode_dmr_id_bin(uint8_t *id, uint32_t no) {\n  id[0] = no;\n  id[1] = no >> 8;\n  id[2] = no >> 16;\n}\n\n\nuint32_t decode_dmr_id_bcd(const uint8_t *id) {\n  return ((id[0] >> 4) * 10000000\n      + (id[0] & 15) * 1000000\n      + (id[1] >> 4) * 100000\n      + (id[1] & 15) * 10000\n      + (id[2] >> 4) * 1000\n      + (id[2] & 15) * 100\n      + (id[3] >> 4) * 10\n      + (id[3] & 15));\n}\n\nuint32_t decode_dmr_id_bcd_le(const uint8_t *id) {\n  return ((id[3] >> 4) * 10000000\n      + (id[3] & 15) * 1000000\n      + (id[2] >> 4) * 100000\n      + (id[2] & 15) * 10000\n      + (id[1] >> 4) * 1000\n      + (id[1] & 15) * 100\n      + (id[0] >> 4) * 10\n      + (id[0] & 15));\n}\n\nvoid encode_dmr_id_bcd(uint8_t *id, uint32_t no) {\n  id[0] = ((no / 10000000) << 4)    | ((no / 1000000) % 10);\n  id[1] = ((no / 100000 % 10) << 4) | ((no / 10000) % 10);\n  id[2] = ((no / 1000 % 10) << 4)   | ((no / 100) % 10);\n  id[3] = ((no / 10 % 10) << 4)     | (no % 10);\n}\n\nvoid encode_dmr_id_bcd_le(uint8_t *id, uint32_t no) {\n  id[3] = ((no / 10000000) << 4)    | ((no / 1000000) % 10);\n  id[2] = ((no / 100000 % 10) << 4) | ((no / 10000) % 10);\n  id[1] = ((no / 1000 % 10) << 4)   | ((no / 100) % 10);\n  id[0] = ((no / 10 % 10) << 4)     | (no % 10);\n}\n\nQVector<char> bin_dtmf_tab = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','*','#'};\n\nQString\ndecode_dtmf_bin(const uint8_t *num, int size, uint8_t fill) {\n  Q_UNUSED(fill);\n  QString number;\n  for (int i=0; (i<size)&&(num[i]<16); i++) {\n    number.append(bin_dtmf_tab[num[i]]);\n  }\n  return number;\n}\n\nbool\nencode_dtmf_bin(const QString &number, uint8_t *num, int size, uint8_t fill) {\n  QString tmp = number.simplified().toUpper();\n  for (int i=0; i<size; i++) {\n    num[i] = fill;\n    if (i>=tmp.size())\n      continue;\n    int idx = bin_dtmf_tab.indexOf(number.at(i).toLatin1());\n    if (idx<0)\n      continue;\n    num[i] = idx;\n  }\n  return true;\n}\n\nQString\ndecode_dtmf_bcd_be(const uint8_t *num, int digits) {\n  QString number;\n  for (int i=0; i<digits; i++) {\n    uint8_t d = (0 == (i%2)) ? (((num[i/2])>>4)&0xf) : ((num[i/2])&0xf);\n    number.append(bin_dtmf_tab[d]);\n  }\n  return number;\n}\n\nbool\nencode_dtmf_bcd_be(const QString &number, uint8_t *num, int size, uint8_t fill) {\n  memset(num, fill, size);\n  QString tmp = number.simplified().toUpper();\n  for (int i=0; i<tmp.size(); i++) {\n    if (0 == (i%2)) {\n      num[i/2] = ((bin_dtmf_tab.indexOf(tmp[i].toLatin1()))<<4);\n    } else {\n      num[i/2] |= bin_dtmf_tab.indexOf(tmp[i].toLatin1());\n    }\n  }\n  return true;\n}\n\nSelectiveCall\ndecode_ctcss_tone_table(uint16_t data) {\n  if (data == 0xffff)\n    return SelectiveCall();\n\n  unsigned tag = data >> 14;\n  unsigned a = (data >> 12) & 3;\n  unsigned b = (data >> 8) & 15;\n  unsigned c = (data >> 4) & 15;\n  unsigned d = data & 15;\n\n  switch (tag) {\n  case 2:\n    // DCS Normal\n    return SelectiveCall(100*b+10*c+1*d, false);\n  case 3:\n    // DCS Inverted\n    return SelectiveCall(100*b+10*c+1*d, true);\n  default:\n    break;\n  }\n\n  // CTCSS\n  return SelectiveCall(100.0*a+10.0*b+1.0*c+0.1*d);\n}\n\n\nuint16_t\nencode_ctcss_tone_table(const SelectiveCall &code)\n{\n  unsigned tag=0xff, a=0xf, b=0xf, c=0xf, d=0xf;\n\n  // Disabled\n  if (code.isInvalid())\n    return 0xffff;\n\n  if (code.isCTCSS()) {\n    // CTCSS tone\n    tag = 0;\n    unsigned val = code.Hz() * 10.0 + 0.5;\n    a = val / 1000;\n    b = (val / 100) % 10;\n    c = (val / 10) % 10;\n    d = val % 10;\n  } else if (code.isDCS()) {\n    // DCS normal\n    if (code.isInverted())\n      tag = 3;\n    else\n      tag = 2;\n    unsigned val = code.octalCode();\n    a = 0;\n    b = (val / 100) % 10;\n    c = (val / 10) % 10;\n    d = val % 10;\n  }\n\n  return (a << 12) | (b << 8) | (c << 4) | d | (tag << 14);\n}\n\n\nbool\nvalidDMRNumber(const QString &text) {\n  return QRegularExpression(\"^[0-9]+$\").match(text).hasMatch();\n}\n\nbool\nvalidDTMFNumber(const QString &text) {\n  return QRegularExpression(\"^[0-9a-dA-D\\\\*#]+$\").match(text).hasMatch();\n}\n\nQString\naprsicon2config(FMAPRSSystem::Icon icon) {\n  if ((FMAPRSSystem::Icon::None == icon) || (! aprsIconCodeTable.contains(unsigned(icon))))\n    return \"-\";\n  return QString(\"\\\"%1\\\"\").arg(aprsIconNameTable.value((unsigned)icon));\n}\n\nQString\naprsicon2name(FMAPRSSystem::Icon icon) {\n  if ((FMAPRSSystem::Icon::None == icon) || (! aprsIconCodeTable.contains(unsigned(icon))))\n    return \"\";\n  return aprsIconNameTable.value((unsigned)icon);\n}\n\nFMAPRSSystem::Icon\nname2aprsicon(const QString &name) {\n  if (name.isEmpty())\n    return FMAPRSSystem::Icon::None;\n\n  FMAPRSSystem::Icon icon = FMAPRSSystem::Icon::None;\n  int best = levDist(name, \"\");\n\n  QHash<unsigned, QString>::const_iterator item=aprsIconNameTable.constBegin();\n  for(; item != aprsIconNameTable.constEnd(); item++) {\n    int dist = levDist(name, item.value());\n    if (dist < best) {\n      icon = (FMAPRSSystem::Icon)item.key();\n      best = dist;\n    }\n  }\n\n  return icon;\n}\n\nchar\naprsicon2iconcode(FMAPRSSystem::Icon icon) {\n unsigned num = unsigned(FMAPRSSystem::ICON_MASK & unsigned(icon));\n if (num >= unsigned(aprsIconCodeTable.size()))\n   return '\"';\n return aprsIconCodeTable[num];\n}\n\nchar\naprsicon2tablecode(FMAPRSSystem::Icon icon) {\n  unsigned tab = (FMAPRSSystem::TABLE_MASK & unsigned(icon));\n  switch (tab) {\n  case FMAPRSSystem::SECONDARY_TABLE: return '\\\\';\n  case FMAPRSSystem::PRIMARY_TABLE: return '/';\n  }\n  return '/';\n}\n\nFMAPRSSystem::Icon\ncode2aprsicon(char table, char icon) {\n  unsigned num = (FMAPRSSystem::ICON_MASK & unsigned(FMAPRSSystem::Icon::None));\n  if (aprsIconCodeTable.contains(icon))\n    num = aprsIconCodeTable.indexOf(icon);\n  if ('/' == table)\n    num = num | FMAPRSSystem::PRIMARY_TABLE;\n  else if ('\\\\' == table)\n    num = num | FMAPRSSystem::PRIMARY_TABLE;\n  return FMAPRSSystem::Icon(num);\n}\n\nint\nlevDist(const QString &source, const QString &target, Qt::CaseSensitivity cs) {\n  // Mostly stolen from https://qgis.org/api/2.14/qgsstringutils_8cpp_source.html\n\n  if (0 == QString::compare(source,target, cs)) {\n    return 0;\n  }\n\n  const int sourceCount = source.size();\n  const int targetCount = target.size();\n\n  if (source.isEmpty())\n    return targetCount;\n  if (target.isEmpty())\n    return sourceCount;\n  if (sourceCount > targetCount)\n    return levDist(target, source, cs);\n\n\n  QVector<int> column;\n  column.fill(0, targetCount + 1);\n\n  QVector<int> previousColumn;\n  previousColumn.reserve(targetCount + 1);\n  for (int i = 0; i < targetCount + 1; i++)\n    previousColumn.append(i);\n\n  for (int i = 0; i < sourceCount; i++) {\n    column[0] = i + 1;\n    for (int j = 0; j < targetCount; j++) {\n      column[j + 1] = std::min(\n      {\n              1 + column.at(j),\n              1 + previousColumn.at(1 + j),\n              previousColumn.at(j) + (QString::compare(source.at(i),target.at(j), cs) ? 1 : 0)\n            });\n    }\n    column.swap(previousColumn);\n  }\n\n  return previousColumn.at(targetCount);\n}\n\nuint32_t\nalign_size(uint32_t size, uint32_t block) {\n  if (0 == (size % block))\n    return size;\n  return (size + (block - (size%block)));\n}\n\nuint32_t\nalign_addr(uint32_t addr, uint32_t block) {\n  if (0 == (addr % block))\n    return addr;\n  return (addr - (addr%block));\n}\n\n\nQGeoCoordinate\nloc2deg(const QString &loc) {\n  double lon = 0, lat = 0, dlon = 20, dlat = 10;\n  if (2 > loc.size())\n    return QGeoCoordinate();\n  QChar l = loc[0].toUpper();\n  QChar c = loc[1].toUpper();\n  lon += double(int(l.toLatin1())-'A')*dlon;\n  lat += double(int(c.toLatin1())-'A')*dlat;\n\n  if (4 > loc.size()) {\n    lon = lon - 180;\n    lat = lat - 90;\n    // Offset places coordinate in the middle of the square\n    return QGeoCoordinate(lat+dlat/2, lon+dlon/2);\n  }\n\n  dlon /= 10; dlat /= 10;\n  l = loc[2].toUpper();\n  c = loc[3].toUpper();\n  lon += double(int(l.toLatin1())-'0')*dlon;\n  lat += double(int(c.toLatin1())-'0')*dlat;\n\n  if (6 > loc.size()){\n    lon = lon - 180;\n    lat = lat - 90;\n    // Offset places coordinate in the middle of the square\n    return QGeoCoordinate(lat+dlat/2, lon+dlon/2,0);\n  }\n\n  dlon /= 24; dlat /= 24;\n  l = loc[4].toUpper();\n  c = loc[5].toUpper();\n  lon += double(int(l.toLatin1())-'A')*dlon;\n  lat += double(int(c.toLatin1())-'A')*dlat;\n\n  if (8 > loc.size()) {\n    lon = lon - 180;\n    lat = lat - 90;\n    // Offset places coordinate in the middle of the square\n    return QGeoCoordinate(lat+dlat/2, lon+dlon/2);\n  }\n\n  dlon /= 10; dlat /= 10;\n  l = loc[6].toUpper();\n  c = loc[7].toUpper();\n  lon += double(int(l.toLatin1())-'0')*dlon;\n  lat += double(int(c.toLatin1())-'0')*dlat;\n\n  if (10 > loc.size()) {\n    lon = lon - 180;\n    lat = lat - 90;\n    // Offset places coordinate in the middle of the square\n    return QGeoCoordinate(lat+dlat/2, lon+dlon/2);\n  }\n\n  dlon /= 24; dlat /= 24;\n  l = loc[8].toUpper();\n  c = loc[9].toUpper();\n  lon += double(int(l.toLatin1())-'A')*dlon;\n  lat += double(int(c.toLatin1())-'A')*dlat;\n\n  lon = lon - 180;\n  lat = lat - 90;\n  // Offset places coordinate in the middle of the square\n  return QGeoCoordinate(lat+dlat/2, lon+dlon/2);\n}\n\n\nQString\ndeg2loc(const QGeoCoordinate &coor, unsigned int size) {\n  if (!coor.isValid())\n    return QString();\n\n  QString loc;\n  double lon = (coor.longitude()+180)/360;\n  double lat = (coor.latitude()+90)/180;\n\n  size += (size % 2);\n  if (2 > size)\n    return loc;\n\n  lon *= 18; lat *= 18;\n  char l = lon; lon -= l;\n  char c = lat; lat -= c;\n  loc.append(QChar::fromLatin1(l+'A')); loc.append(QChar::fromLatin1(c+'A'));\n\n  if (4 > size)\n    return loc;\n\n  lon *= 10; lat *= 10;\n  l = lon; lon -= l;\n  c = lat; lat -= c;\n  loc.append(QChar::fromLatin1(l+'0')); loc.append(QChar::fromLatin1(c+'0'));\n\n  if (6 > size)\n    return loc;\n\n  lon *= 24; lat *= 24;\n  l = lon; lon -= l;\n  c = lat; lat -= c;\n  loc.append(QChar::fromLatin1(l+'a')); loc.append(QChar::fromLatin1(c+'a'));\n\n  if (8 > size)\n    return loc;\n\n  lon *= 10; lat *= 10;\n  l = lon; lon -= l;\n  c = lat; lat -= c;\n  loc.append(QChar::fromLatin1(l+'0')); loc.append(QChar::fromLatin1(c+'0'));\n\n  if (10 > size)\n    return loc;\n\n  lon *= 24; lat *= 24;\n  l = lon; //lon -= l;\n  c = lat; //lat -= c;\n  loc.append(QChar::fromLatin1(l+'a')); loc.append(QChar::fromLatin1(c+'a'));\n\n  return loc;\n}\n\n\n"
  },
  {
    "path": "lib/utils.hh",
    "content": "/** @defgroup util Utility functions and classes.\n * This module collects all utility functions and classes. That is, functions to encode some\n * data and also classes implementing the DFU file format.\n */\n\n#ifndef UTILS_HH\n#define UTILS_HH\n\n#include <QString>\n#include <inttypes.h>\n\n#include \"signaling.hh\"\n#include \"gpssystem.hh\"\n#include <QGeoCoordinate>\n\n/** Decodes the unicode string stored in @c data of size @c size. The @c fill code also defines the\n * end-of-string symbol.\n * @returns The decoded string. */\nQString decode_unicode(const uint16_t *data, size_t size, uint16_t fill=0x0000);\n/** Encodes the string @c text as unicode and stores the result into @c data using up-to @c size\n * 16bit words in data. The @c fill word specifies the fill and end-of-string word. */\nvoid encode_unicode(uint16_t *data, const QString &text, size_t size, uint16_t fill=0x0000);\n\n/** Decodes the ascii string in @c data into a @c QString of up-to size length. The @c fill word\n * specifies the fill and end-of-string word. */\nQString decode_ascii(const uint8_t *data, size_t size, uint16_t fill=0x00);\n/** Encodes the given QString @c text of up-to size length as ASCII into @c data using the\n * @c fill word as fill and end-of-string word. */\nvoid encode_ascii(uint8_t *data, const QString &text, size_t size, uint16_t fill=0x00);\n\n/** Decodes the UTF-8 string in @c data into a @c QString of up-to size length. The @c fill word\n * specifies the fill and end-of-string word. */\nQString decode_utf8(const uint8_t *data, size_t size, uint16_t fill=0x00);\n/** Encodes the given QString @c text of up-to size length as UTF-8 into @c data using the\n * @c fill word as fill and end-of-string word. */\nvoid encode_utf8(uint8_t *data, const QString &text, size_t size, uint16_t fill=0x00);\n\n/** Decodes an 8 digit BCD encoded frequency (in MHz). */\ndouble decode_frequency(uint32_t bcd);\n/** Eecodes an 8 digit BCD encoded frequency (in MHz). */\nuint32_t encode_frequency(double freq);\n\n/** Decodes binary (24bit) encoded DMR ID. */\nuint32_t decode_dmr_id_bin(const uint8_t *id);\n/** Encodes binary (24bit) encoded DMR ID. */\nvoid encode_dmr_id_bin(uint8_t *id, uint32_t num);\n/** Decodes bcd (32bit) encoded DMR ID, little endian. */\nuint32_t decode_dmr_id_bcd(const uint8_t *id);\n/** Decodes bcd (32bit) encoded DMR ID, big endian. */\nuint32_t decode_dmr_id_bcd_le(const uint8_t *id);\n/** Encodes bcd (32bit) encoded DMR ID, little endian. */\nvoid encode_dmr_id_bcd(uint8_t *id, uint32_t num);\n/** Encodes bcd (32bit) encoded DMR ID, big endian. */\nvoid encode_dmr_id_bcd_le(uint8_t *id, uint32_t num);\n\nQString decode_dtmf_bin(const uint8_t *num, int size=16, uint8_t fill=0xff);\nbool encode_dtmf_bin(const QString &number, uint8_t *num, int size=16, uint8_t fill=0xff);\n\nQString decode_dtmf_bcd_be(const uint8_t *num, int digits);\nbool encode_dtmf_bcd_be(const QString &number, uint8_t *num, int size, uint8_t fill);\n\n/** Decodes the CTCSS tone or DCS code to @c Signaling::Code.\n * @todo TyT specific, move to TyT codeplug. */\nSelectiveCall decode_ctcss_tone_table(uint16_t data);\n/** Encodes the CTCSS tone or DCS code from @c Signaling::Code.\n * @todo TyT specific, move to TyT codeplug. */\nuint16_t encode_ctcss_tone_table(const SelectiveCall &code);\n\n/** Validates a DMR ID number. */\nbool validDMRNumber(const QString &text);\n/** Validates a DTMF number. */\nbool validDTMFNumber(const QString &text);\n\nQString aprsicon2config(FMAPRSSystem::Icon icon);\nQString aprsicon2name(FMAPRSSystem::Icon icon);\nFMAPRSSystem::Icon name2aprsicon(const QString &name);\nchar aprsicon2iconcode(FMAPRSSystem::Icon icon);\nchar aprsicon2tablecode(FMAPRSSystem::Icon icon);\nFMAPRSSystem::Icon code2aprsicon(char table, char icon);\n\n/** Implements the Levenshtein distance between two strings.\n * That is, the number of edits (insert, delete or replace operations) needed to turn source\n * into target. */\nint levDist(const QString &source, const QString &target,\n            Qt::CaseSensitivity cs=Qt::CaseInsensitive);\n\n/** Increases the given size to be aligned with the given block size. */\nuint32_t align_size(uint32_t size, uint32_t block);\n/** Decreases the address to be aligned with the given block size. */\nuint32_t align_addr(uint32_t addr, uint32_t block);\n\nQGeoCoordinate loc2deg(const QString &loc);\nQString deg2loc(const QGeoCoordinate &coor, unsigned int size=6);\n\n#endif // UTILS_HH\n"
  },
  {
    "path": "lib/uv390.cc",
    "content": "#include \"uv390.hh\"\n#include \"uv390_limits.hh\"\n\n\nRadioLimits *UV390::_limits = nullptr;\n\n\nUV390::UV390(TyTInterface *device, QObject *parent)\n  : TyTRadio(device, parent), _name(\"TyT MD-UV390\")\n{\n  // pass...\n}\n\nUV390::~UV390() {\n  // pass...\n}\n\nconst QString &\nUV390::name() const {\n  return _name;\n}\n\nconst RadioLimits &\nUV390::limits() const {\n  if (nullptr == _limits)\n    _limits = new UV390Limits();\n  return *_limits;\n}\n\nconst Codeplug &\nUV390::codeplug() const {\n  return _codeplug;\n}\n\nCodeplug &\nUV390::codeplug() {\n  return _codeplug;\n}\n\nconst CallsignDB *\nUV390::callsignDB() const {\n  return &_callsigndb;\n}\n\nCallsignDB *\nUV390::callsignDB() {\n  return &_callsigndb;\n}\n\nRadioInfo\nUV390::defaultRadioInfo() {\n  return RadioInfo(\n        RadioInfo::UV390, \"uv390\", \"MD-UV390\", \"TyT\", {TyTInterface::interfaceInfo()},\n        QList<RadioInfo>{\n          RadioInfo(RadioInfo::UV380, \"MD-UV380\", \"TyT\", {TyTInterface::interfaceInfo()}),\n          RadioInfo(RadioInfo::RT3S, \"RT3S\", \"Retevis\", {TyTInterface::interfaceInfo()})\n        });\n}\n"
  },
  {
    "path": "lib/uv390.hh",
    "content": "/** @defgroup uv390 TYT MD-UV390, Retevis RT3S\n * Device specific classes for TyT MD-UV390 and Retevis RT3S.\n *\n * \\image html uv390.jpg \"MD-UV390\" width=200px\n * \\image latex uv390.jpg \"MD-UV390\" width=200px\n *\n * The TYT MD-UV390 and the identical Retevis RT3S are decent VHF/UHF FM and DMR handheld radios.\n * Both radios are available with and without an GPS option. @c libdmrconf will support that\n * feature. Non-GPS variants of that radio will simply ignore any GPS system settings.\n *\n * These radios support up to 3000 channels organized in 250 zones. Each zone may hold up to 64\n * channels for each VFO (64 for VFO A and 64 for VFO B). There are also up to 250 scanlists\n * holding up to 31(?) channels each.\n *\n * The radio can hold up to 3000 contacts (DMR contacts) and 250 RX group lists as well as up to 50\n * pre-programmed messages. Depending on the firmware programmed on the radio, it may also hold a\n * callsign database of up to 100000 entries. This can be used to resolve amlost any DMR ID assigned\n * (at the time of this writing, there are about 140k IDs assigned) to name and callsign.\n *\n * @ingroup tyt */\n#ifndef UV390_HH\n#define UV390_HH\n\n#include \"tyt_radio.hh\"\n#include \"uv390_codeplug.hh\"\n#include \"uv390_callsigndb.hh\"\n\nclass RadioLimits;\n\n/** Implements an USB interface to the TYT MD-UV390 & Retevis RT3S VHF/UHF 5W DMR (Tier I&II) radios.\n *\n * The TYT MD-UV390 and Retevis RT3S radios use the TyT typical DFU-style communication protocol\n * to read and write codeplugs onto the radio (see @c TyTRadio).\n *\n * @ingroup uv390 */\nclass UV390 : public TyTRadio\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor.\n   * @param device Specifies the DFU device to use for communication with the device.\n   * @param parent The QObject parent. */\n  UV390(TyTInterface *device=nullptr, QObject *parent=nullptr);\n  /** Desturctor. */\n  virtual ~UV390();\n\n  const QString &name() const;\n  const RadioLimits &limits() const;\n\n  const Codeplug &codeplug() const;\n  Codeplug &codeplug();\n\n  const CallsignDB *callsignDB() const;\n  CallsignDB *callsignDB();\n\n  /** Returns the default radio information. The actual instance may have different properties\n   * due to variants of the same radio. */\n  static RadioInfo defaultRadioInfo();\n\nprivate:\n  /** Holds the name of the device. */\n  QString _name;\n  /** The codeplug object. */\n  UV390Codeplug _codeplug;\n  /** The callsign DB object. */\n  UV390CallsignDB _callsigndb;\n\nprivate:\n  /** Holds the singleton instance of the radio limits. */\n  static RadioLimits *_limits;\n};\n\n#endif // MD2017_HH\n"
  },
  {
    "path": "lib/uv390_callsigndb.cc",
    "content": "#include \"uv390_callsigndb.hh\"\n\nUV390CallsignDB::UV390CallsignDB(QObject *parent)\n  : TyTCallsignDB(parent)\n{\n  image(0).setName(\"TYT MD-UV390 Callsign database.\");\n}\n\nUV390CallsignDB::~UV390CallsignDB() {\n  // pass...\n}\n\n\n"
  },
  {
    "path": "lib/uv390_callsigndb.hh",
    "content": "#ifndef UV390_CALLSIGNDB_HH\n#define UV390_CALLSIGNDB_HH\n\n#include \"tyt_callsigndb.hh\"\n\n/** Device specific implementation of the call-sign DB for the TyT MD-UV390.\n *\n * In fact this callsign DB is identical to the generic @c TyTCallsignDB.\n *\n * @ingroup uv390 */\nclass UV390CallsignDB : public TyTCallsignDB\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit UV390CallsignDB(QObject *parent=nullptr);\n  /** Destructor. */\n  virtual ~UV390CallsignDB();\n};\n\n#endif // UV390_CALLSIGNDB_HH\n"
  },
  {
    "path": "lib/uv390_codeplug.cc",
    "content": "#include \"uv390_codeplug.hh\"\n#include \"logger.hh\"\n#include \"config.hh\"\n#include \"tyt_extensions.hh\"\n#include <QTimeZone>\n\n\n#define NUM_CONTACTS               10000\n#define ADDR_CONTACTS           0x140000\n#define CONTACT_SIZE            0x000024\n\n#define NUM_ZONES                    250\n#define ADDR_ZONES              0x0149e0\n#define ZONE_SIZE               0x000040\n#define ADDR_ZONEEXTS           0x031000\n#define ZONEEXT_SIZE            0x0000e0\n\n#define NUM_GROUPLISTS               250\n#define ADDR_GROUPLISTS         0x00ec20\n#define GROUPLIST_SIZE          0x000060\n\n#define NUM_SCANLISTS                250\n#define ADDR_SCANLISTS          0x018860\n#define SCANLIST_SIZE           0x000068\n\n#define ADDR_TIMESTAMP          0x002000\n#define ADDR_SETTINGS           0x002040\n#define SETTINGS_SIZE           0x0000b0\n#define ADDR_BOOTSETTINGS       0x02f000\n#define ADDR_MENUSETTINGS       0x0020f0\n#define ADDR_BUTTONSETTINGS     0x002100\n#define ADDR_PRIVACY_KEYS       0x0059c0\n\n#define NUM_GPSSYSTEMS                16\n#define ADDR_GPSSYSTEMS         0x03ec40\n#define GPSSYSTEM_SIZE          0x000010\n\n#define ADDR_EMERGENCY_SETTINGS 0x005a50\n#define NUM_EMERGENCY_SYSTEMS         32\n#define ADDR_EMERGENCY_SYSTEMS  0x005a60\n#define EMERGENCY_SYSTEM_SIZE   0x000028\n\n#define ADDR_VFO_CHANNEL_A      0x02ef00\n#define ADDR_VFO_CHANNEL_B      0x02ef40\n\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::ChannelElement\n * ******************************************************************************************** */\nUV390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nUV390Codeplug::ChannelElement::ChannelElement(uint8_t *ptr)\n  : TyTCodeplug::ChannelElement(ptr, size())\n{\n  // pass...\n}\n\nvoid\nUV390Codeplug::ChannelElement::clear() {\n  TyTCodeplug::ChannelElement::clear();\n\n  enableTalkaround(false);\n  clearBit(5,0);\n  setInCallCriteria(TyTChannelExtension::InCallCriterion::Always);\n  setTurnOffFreq(TyTChannelExtension::KillTone::Off);\n  setSquelch(Level::fromValue(1));\n  setPower(Channel::Power::High);\n  enableAllowInterrupt(true);\n  enableDualCapacityDirectMode(false);\n  enableDCDMLeader(true);\n  enablePrivacySwitch(false);\n}\n\nbool\nUV390Codeplug::ChannelElement::talkaround() const {\n  return getBit(Offset::talkaround());\n}\nvoid\nUV390Codeplug::ChannelElement::enableTalkaround(bool enable) {\n  setBit(Offset::talkaround(), enable);\n}\n\nbool\nUV390Codeplug::ChannelElement::privacySwitch() const {\n  return getBit(Offset::privacySwitch());\n}\nvoid\nUV390Codeplug::ChannelElement::enablePrivacySwitch(bool enable) {\n  setBit(Offset::privacySwitch(), enable);\n}\n\nvoid\nUV390Codeplug::ChannelElement::setPrivacyType(PrivacyType type) {\n  enablePrivacySwitch(PrivacyType::PRIV_NONE != type);\n  TyTCodeplug::ChannelElement::setPrivacyType(type);\n}\n\n\nTyTChannelExtension::InCallCriterion UV390Codeplug::ChannelElement::inCallCriteria() const {\n  return TyTChannelExtension::InCallCriterion(getUInt2(Offset::inCallCriteria()));\n}\nvoid\nUV390Codeplug::ChannelElement::setInCallCriteria(TyTChannelExtension::InCallCriterion crit) {\n  setUInt2(Offset::inCallCriteria(), uint8_t(crit));\n}\n\nTyTChannelExtension::KillTone UV390Codeplug::ChannelElement::turnOffFreq() const {\n  return TyTChannelExtension::KillTone(getUInt2(Offset::turnOffFreq()));\n}\nvoid\nUV390Codeplug::ChannelElement::setTurnOffFreq(TyTChannelExtension::KillTone freq) {\n  setUInt2(Offset::turnOffFreq(), uint8_t(freq));\n}\n\nLevel\nUV390Codeplug::ChannelElement::squelch() const {\n  return Level::fromValue(getUInt8(Offset::squelch()));\n}\nvoid\nUV390Codeplug::ChannelElement::setSquelch(Level value) {\n  if (value.isInvalid())\n    setUInt8(Offset::squelch(), value.value());\n}\n\nChannel::Power\nUV390Codeplug::ChannelElement::power() const {\n  switch (getUInt2(Offset::power())) {\n  case 0: return Channel::Power::Low;\n  case 2: return Channel::Power::Mid;\n  case 3: return Channel::Power::High;\n  default: break;\n  }\n  return Channel::Power::Low;\n}\nvoid\nUV390Codeplug::ChannelElement::setPower(Channel::Power pwr) {\n  switch (pwr) {\n  case Channel::Power::Min:\n  case Channel::Power::Low:\n    setUInt2(Offset::power(), 0);\n    break;\n  case Channel::Power::Mid:\n    setUInt2(Offset::power(), 2);\n    break;\n  case Channel::Power::High:\n  case Channel::Power::Max:\n    setUInt2(Offset::power(), 3);\n  }\n}\n\nbool\nUV390Codeplug::ChannelElement::allowInterrupt() const {\n  return !getBit(Offset::allowInterrupt());\n}\nvoid\nUV390Codeplug::ChannelElement::enableAllowInterrupt(bool enable) {\n  setBit(Offset::allowInterrupt(), !enable);\n}\n\nbool\nUV390Codeplug::ChannelElement::dualCapacityDirectMode() const {\n  return !getBit(Offset::dualCapacityDirectMode());\n}\nvoid\nUV390Codeplug::ChannelElement::enableDualCapacityDirectMode(bool enable) {\n  setBit(Offset::dualCapacityDirectMode(), !enable);\n}\n\nbool\nUV390Codeplug::ChannelElement::dcdmLeader() const {\n  return !getBit(Offset::dcdmLeader());\n}\nvoid\nUV390Codeplug::ChannelElement::enableDCDMLeader(bool enable) {\n  setBit(Offset::dcdmLeader(), !enable);\n}\n\n\nChannel *\nUV390Codeplug::ChannelElement::toChannelObj(const ErrorStack &err) const {\n  if (! isValid()) {\n    errMsg(err) << \"Cannot decode invalid channel.\";\n    return nullptr;\n  }\n\n  Channel *ch = TyTCodeplug::ChannelElement::toChannelObj(err);\n  if (nullptr == ch) {\n    errMsg(err) << \"Cannot decode base TyT channel element.\";\n    return nullptr;\n  }\n\n  // decode squelch setting\n  if (auto ach = ch->as<FMChannel>()) {\n    ach->setSquelch(squelch());\n  } else if (auto dch = ch->as<DMRChannel>()) {\n    dch->extended()->enableDCDM(dualCapacityDirectMode());\n  }\n\n  // Common settings\n  ch->setPower(power());\n\n  // assemble extension\n  if (TyTChannelExtension *ex = ch->tytChannelExtension()) {\n    ex->setKillTone(turnOffFreq());\n    ex->setInCallCriterion(inCallCriteria());\n    ex->enableAllowInterrupt(allowInterrupt());\n    ex->enableDCDMLeader(dcdmLeader());\n    if (ch->is<DMRChannel>())\n      ex->setDMRSquelch(squelch());\n  }\n\n  return ch;\n}\n\nvoid\nUV390Codeplug::ChannelElement::fromChannelObj(const Channel *chan, Context &ctx) {\n  TyTCodeplug::ChannelElement::fromChannelObj(chan, ctx);\n  // encode power setting\n  if (chan->defaultPower())\n    setPower(ctx.config()->settings()->power());\n  else\n    setPower(chan->power());\n  // By default, set to global default value.\n  setSquelch(ctx.config()->settings()->audio()->squelch());\n\n  if (auto achan = chan->as<const FMChannel>()) {\n    if (! achan->defaultSquelch())\n      setSquelch(achan->squelch());\n  } else if (auto dch = chan->as<const DMRChannel>()) {\n    enableDualCapacityDirectMode(dch->extended()->dcdm());\n  }\n\n  // apply extensions\n  if (TyTChannelExtension *ex = chan->tytChannelExtension()) {\n    setTurnOffFreq(ex->killTone());\n    setInCallCriteria(ex->inCallCriterion());\n    enableAllowInterrupt(ex->allowInterrupt());\n    enableDCDMLeader(ex->dcdmLeader());\n    if (chan->is<DMRChannel>())\n      setSquelch(ex->dmrSquelch());\n  }\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::VFOChannelElement\n * ******************************************************************************************** */\nUV390Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr, size_t size)\n  : ChannelElement(ptr, size)\n{\n  // pass...\n}\n\nUV390Codeplug::VFOChannelElement::VFOChannelElement(uint8_t *ptr)\n  : ChannelElement(ptr, size())\n{\n  // pass...\n}\n\nUV390Codeplug::VFOChannelElement::~VFOChannelElement() {\n  // pass...\n}\n\nQString\nUV390Codeplug::VFOChannelElement::name() const {\n  return \"\";\n}\nvoid\nUV390Codeplug::VFOChannelElement::setName(const QString &txt) {\n  Q_UNUSED(txt)\n  // pass...\n}\n\nunsigned\nUV390Codeplug::VFOChannelElement::stepSize() const {\n  return (getUInt8(32)+1)*2500;\n}\nvoid\nUV390Codeplug::VFOChannelElement::setStepSize(unsigned ss_Hz) {\n  ss_Hz = std::min(50000U, std::max(ss_Hz, 2500U));\n  setUInt8(32, ss_Hz/2500-1);\n  setUInt8(33, 0xff);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::GeneralSettingsElement\n * ******************************************************************************************** */\nUV390Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr, size_t size)\n  : DM1701Codeplug::GeneralSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nUV390Codeplug::GeneralSettingsElement::GeneralSettingsElement(uint8_t *ptr)\n  : DM1701Codeplug::GeneralSettingsElement(ptr, SETTINGS_SIZE)\n{\n  // pass...\n}\n\nvoid\nUV390Codeplug::GeneralSettingsElement::clear() {\n  TyTCodeplug::GeneralSettingsElement::clear();\n\n  setTransmitMode(DESIGNED_AND_HAND_CH);\n  enableChannelVoiceAnnounce(false);\n  setBit(0x43,0, 1); setBit(0x43,1, 1);\n  setUInt4(0x43,3, 0xf);\n\n  setBit(0x6b, 2, 1);\n\n  setUInt8(0x91, 0xff); setUInt2(0x92, 0, 0x03);\n  enablePublicZone(true);\n  setUInt5(0x92, 3, 0x1f);\n  setUInt8(0x93, 0xff);\n  setAdditionalDMRId(0, 1); setUInt8(0x97, 0);\n  setAdditionalDMRId(1, 2); setUInt8(0x9b, 0);\n  setAdditionalDMRId(2, 3); setUInt8(0x9f, 0);\n  setUInt3(0xa0, 0, 0b111);\n  setMICLevel(Level::fromValue(2));\n  enableEditRadioID(true);\n  setBit(0xa0, 7, true);\n  memset(_data+0xa1, 0xff, 15);\n}\n\nUV390Codeplug::GeneralSettingsElement::TransmitMode\nUV390Codeplug::GeneralSettingsElement::transmitMode() const {\n  return TransmitMode(getUInt2(0x40,6));\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::setTransmitMode(TransmitMode mode) {\n  setUInt2(0x40,6, mode);\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::channelVoiceAnnounce() const {\n  return getBit(0x42,1);\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::enableChannelVoiceAnnounce(bool enable) {\n  setBit(0x42,1, enable);\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::keypadTones() const {\n  return getBit(0x42,5);\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::enableKeypadTones(bool enable) {\n  setBit(0x42,5, enable);\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::publicZone() const {\n  return getBit(0x92, 2);\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::enablePublicZone(bool enable) {\n  setBit(0x92, 2, enable);\n}\n\nuint32_t\nUV390Codeplug::GeneralSettingsElement::additionalDMRId(unsigned n) const {\n  return getUInt24_le(0x94+4*n);\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::setAdditionalDMRId(unsigned n, uint32_t id) {\n  setUInt24_le(0x94+4*n, id);\n}\n\nLevel\nUV390Codeplug::GeneralSettingsElement::micLevel() const {\n  return Level::fromValue(getUInt3(Offset::micGain()), Limit::micGain());\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::setMICLevel(Level level) {\n  setUInt3(Offset::micGain(), level.mapTo(Limit::micGain()));\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::editRadioID() const {\n  return !getBit(0xa0, 6);\n}\nvoid\nUV390Codeplug::GeneralSettingsElement::enableEditRadioID(bool enable) {\n  setBit(0xa0,6, !enable);\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::fromConfig(const Config *config) {\n  if (! DM1701Codeplug::GeneralSettingsElement::fromConfig(config))\n    return false;\n\n  setTimeZone(QTimeZone::systemTimeZone());\n  setMICLevel(config->settings()->audio()->micGain());\n  enableChannelVoiceAnnounce(config->settings()->audio()->speechSynthesisEnabled());\n  return true;\n}\n\nbool\nUV390Codeplug::GeneralSettingsElement::updateConfig(Config *config) {\n  if (! DM1701Codeplug::GeneralSettingsElement::updateConfig(config))\n    return false;\n  config->settings()->audio()->setMicGain(micLevel());\n  config->settings()->audio()->enableSpeechSynthesis(channelVoiceAnnounce());\n  return true;\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::BootSettingsElement\n * ******************************************************************************************** */\nUV390Codeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr, size_t size)\n  : Codeplug::Element(ptr, size)\n{\n  // pass...\n}\n\nUV390Codeplug::BootSettingsElement::BootSettingsElement(uint8_t *ptr)\n  : Codeplug::Element(ptr, 0x0010)\n{\n  // pass...\n}\n\nUV390Codeplug::BootSettingsElement::~BootSettingsElement() {\n  // pass...\n}\n\nvoid\nUV390Codeplug::BootSettingsElement::clear() {\n  setUInt24_le(0, 0xffffff);\n  setZoneIndex(1);\n  setChannelIndexA(1);\n  setUInt8(0x05, 0xff);\n  setChannelIndexB(1);\n  setUInt16_le(0x07, 0xffff);\n  setUInt16_le(0x09, 0x0001);\n  setUInt8(0x0b, 0xff);\n  setUInt32_le(0x0c, 0xffffffff);\n}\n\nunsigned\nUV390Codeplug::BootSettingsElement::zoneIndex() const {\n  return getUInt8(0x03);\n}\nvoid\nUV390Codeplug::BootSettingsElement::setZoneIndex(unsigned idx) {\n  setUInt8(0x03, idx);\n}\n\nunsigned\nUV390Codeplug::BootSettingsElement::channelIndexA() const {\n  return getUInt8(0x04);\n}\nvoid\nUV390Codeplug::BootSettingsElement::setChannelIndexA(unsigned idx) {\n  setUInt8(0x04, idx);\n}\n\nunsigned\nUV390Codeplug::BootSettingsElement::channelIndexB() const {\n  return getUInt8(0x06);\n}\nvoid\nUV390Codeplug::BootSettingsElement::setChannelIndexB(unsigned idx) {\n  setUInt8(0x06, idx);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug::MenuSettingsElement\n * ******************************************************************************************** */\nUV390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr, size_t size)\n  : TyTCodeplug::MenuSettingsElement(ptr, size)\n{\n  // pass...\n}\n\nUV390Codeplug::MenuSettingsElement::MenuSettingsElement(uint8_t *ptr)\n  : TyTCodeplug::MenuSettingsElement(ptr)\n{\n  // pass...\n}\n\nvoid\nUV390Codeplug::MenuSettingsElement::clear() {\n  TyTCodeplug::MenuSettingsElement::clear();\n\n  enableGPSSettings(true);\n  enableRecording(true);\n\n  enableGroupCallMatch(true);\n  enablePrivateCallMatch(true);\n  enableMenuHangtimeItem(true);\n  enableTXMode(true);\n  enableZoneSettings(true);\n  enableNewZone(true);\n\n  enableEditZone(true);\n  enableNewScanList(true);\n\n  setBit(0x05, 0, true);\n  setBit(0x05, 1, true);\n  enableGroupCallMatch(true);\n  enablePrivateCallMatch(true);\n  enableMenuHangtimeItem(true);\n  enableTXMode(true);\n  enableZoneSettings(true);\n  enableNewZone(true);\n  enableEditZone(true);\n  enableNewScanList(true);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::gpsSettings() const {\n  return !getBit(0x04, 3);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableGPSSettings(bool enable) {\n  setBit(0x04, 3, !enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::recording() const {\n  return getBit(0x04, 5);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableRecording(bool enable) {\n  setBit(0x04, 5, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::groupCallMatch() const {\n  return getBit(0x05, 2);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableGroupCallMatch(bool enable) {\n  setBit(0x05, 2, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::privateCallMatch() const {\n  return getBit(0x05, 3);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enablePrivateCallMatch(bool enable) {\n  setBit(0x05, 3, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::menuHangtimeItem() const {\n  return getBit(0x05, 4);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableMenuHangtimeItem(bool enable) {\n  setBit(0x05, 4, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::txMode() const {\n  return getBit(0x05, 5);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableTXMode(bool enable) {\n  setBit(0x05, 5, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::zoneSettings() const {\n  return getBit(0x05, 6);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableZoneSettings(bool enable) {\n  setBit(0x05, 6, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::newZone() const {\n  return getBit(0x05, 7);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableNewZone(bool enable) {\n  setBit(0x05, 7, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::editZone() const {\n  return getBit(0x06, 0);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableEditZone(bool enable) {\n  setBit(0x06, 0, enable);\n}\n\nbool\nUV390Codeplug::MenuSettingsElement::newScanList() const {\n  return getBit(0x06, 1);\n}\nvoid\nUV390Codeplug::MenuSettingsElement::enableNewScanList(bool enable) {\n  setBit(0x06, 1, enable);\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of UV390Codeplug\n * ******************************************************************************************** */\nUV390Codeplug::UV390Codeplug(QObject *parent)\n  : TyTCodeplug(parent)\n{\n  addImage(\"TYT MD-UV390 Codeplug\");\n  image(0).addElement(0x002000, 0x3e000);\n  image(0).addElement(0x110000, 0x90000);\n\n  // Clear entire codeplug\n  UV390Codeplug::clear();\n}\n\nUV390Codeplug::~UV390Codeplug() {\n  // pass...\n}\n\nvoid\nUV390Codeplug::clear() {\n  TyTCodeplug::clear();\n\n  UV390Codeplug::clearBootSettings();\n  UV390Codeplug::clearVFOSettings();\n}\n\nvoid\nUV390Codeplug::clearTimestamp() {\n  TimestampElement(data(ADDR_TIMESTAMP)).clear();\n}\n\nbool\nUV390Codeplug::encodeTimestamp() {\n  TimestampElement ts(data(ADDR_TIMESTAMP));\n  ts.setTimestamp(QDateTime::currentDateTime());\n  return true;\n}\n\nvoid\nUV390Codeplug::clearGeneralSettings() {\n  GeneralSettingsElement(data(ADDR_SETTINGS)).clear();\n}\n\nbool\nUV390Codeplug::encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nUV390Codeplug::decodeGeneralSettings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return GeneralSettingsElement(data(ADDR_SETTINGS)).updateConfig(ctx.config());\n}\n\nvoid\nUV390Codeplug::clearChannels() {\n  // Clear channels\n  for (unsigned int i=0; i<Limit::channels(); i++)\n    ChannelElement(data(Offset::channels()+i*Offset::betweenChannels())).clear();\n}\n\nbool\nUV390Codeplug::encodeChannels(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Channels\n  for (unsigned int i=0; i<Limit::channels(); i++) {\n    ChannelElement chan(data(Offset::channels()+i*Offset::betweenChannels()));\n    chan.clear();\n    if (i < (unsigned int)ctx.config()->channelList()->count()) {\n      chan.fromChannelObj(ctx.config()->channelList()->channel(i), ctx);\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::channels(); i++) {\n    ChannelElement chan(data(Offset::channels()+i*Offset::betweenChannels()));\n    if (! chan.isValid())\n      continue;\n    if (Channel *obj = chan.toChannelObj()) {\n      ctx.config()->channelList()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid channel at index  \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::linkChannels(Context &ctx, const ErrorStack &err) {\n  for (unsigned int i=0; i<Limit::channels(); i++) {\n    ChannelElement chan(data(Offset::channels()+i*Offset::betweenChannels()));\n    if (! chan.isValid())\n      continue;\n    if (! chan.linkChannelObj(ctx.get<Channel>(i+1), ctx, err)) {\n      errMsg(err) << \"Cannot link channel at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nUV390Codeplug::clearContacts() {\n  // Clear contacts\n  for (int i=0; i<NUM_CONTACTS; i++)\n    ContactElement(data(ADDR_CONTACTS+i*CONTACT_SIZE)).clear();\n}\n\nbool\nUV390Codeplug::encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode contacts\n  for (unsigned int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (i < ctx.count<DigitalContact>())\n      cont.fromContactObj(ctx.get<DMRContact>(i+1));\n    else\n      cont.clear();\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createContacts(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_CONTACTS; i++) {\n    ContactElement cont(data(ADDR_CONTACTS+i*CONTACT_SIZE));\n    if (! cont.isValid())\n      continue;\n    if (DMRContact *obj = cont.toContactObj()) {\n      ctx.config()->contacts()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid contact at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nUV390Codeplug::clearZones() {\n  // Clear zones & zone extensions\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement(data(ADDR_ZONES+i*ZONE_SIZE)).clear();\n    ZoneExtElement(data(ADDR_ZONEEXTS+i*ZONEEXT_SIZE)).clear();\n  }\n}\n\nbool\nUV390Codeplug::encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement elm(data(ADDR_ZONES + i*ZONE_SIZE));\n    ZoneExtElement ext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    elm.clear();\n    ext.clear();\n    if (i < ctx.config()->zones()->count()) {\n      elm.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n      if (ctx.config()->zones()->zone(i)->B()->count() || (16 < ctx.config()->zones()->zone(i)->A()->count()))\n        ext.fromZoneObj(ctx.config()->zones()->zone(i), ctx);\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (Zone *obj = zone.toZoneObj()) {\n      ctx.config()->zones()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid zone at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nUV390Codeplug::linkZones(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_ZONES; i++) {\n    ZoneElement zone(data(ADDR_ZONES+i*ZONE_SIZE));\n    if (! zone.isValid())\n      continue;\n    if (! zone.linkZone(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone at index \" << i << \".\";\n      return false;\n    }\n\n    ZoneExtElement zoneext(data(ADDR_ZONEEXTS + i*ZONEEXT_SIZE));\n    if (! zoneext.linkZoneObj(ctx.get<Zone>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link zone extension at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nUV390Codeplug::clearGroupLists() {\n  for (int i=0; i<NUM_GROUPLISTS; i++)\n    GroupListElement(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE)).clear();\n}\n\nbool\nUV390Codeplug::encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (i < ctx.config()->rxGroupLists()->count())\n      glist.fromGroupListObj(ctx.config()->rxGroupLists()->list(i), ctx);\n    else\n      glist.clear();\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (RXGroupList *obj = glist.toGroupListObj(ctx)) {\n      ctx.config()->rxGroupLists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid RX group list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::linkGroupLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GROUPLISTS; i++) {\n    GroupListElement glist(data(ADDR_GROUPLISTS+i*GROUPLIST_SIZE));\n    if (! glist.isValid())\n      continue;\n    if (! glist.linkGroupListObj(ctx.get<RXGroupList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link group-list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nvoid\nUV390Codeplug::clearScanLists() {\n  // Clear scan lists\n  for (int i=0; i<NUM_SCANLISTS; i++)\n    ScanListElement(data(ADDR_SCANLISTS + i*SCANLIST_SIZE)).clear();\n}\n\nbool\nUV390Codeplug::encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  // Define Scanlists\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (i < ctx.config()->scanlists()->count())\n      scan.fromScanListObj(ctx.config()->scanlists()->scanlist(i), ctx);\n    else\n      scan.clear();\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (ScanList *obj = scan.toScanListObj(ctx)) {\n      ctx.config()->scanlists()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::linkScanLists(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_SCANLISTS; i++) {\n    ScanListElement scan(data(ADDR_SCANLISTS + i*SCANLIST_SIZE));\n    if (! scan.isValid())\n      continue;\n    if (! scan.linkScanListObj(ctx.get<ScanList>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link scan list at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nUV390Codeplug::clearPositioningSystems() {\n  // Clear GPS systems\n  for (int i=0; i<NUM_GPSSYSTEMS; i++)\n    GPSSystemElement(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE)).clear();\n}\n\nbool\nUV390Codeplug::encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err)\n  for (unsigned int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (i < ctx.count<DMRAPRSSystem>()) {\n      logDebug() << \"Encode GPS system #\" << i << \" '\" <<\n                    ctx.get<DMRAPRSSystem>(i+1)->name() << \"'.\";\n      gps.fromGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx);\n    } else {\n      gps.clear();\n    }\n  }\n  return true;\n}\n\nbool\nUV390Codeplug::createPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (DMRAPRSSystem *obj = gps.toGPSSystemObj()) {\n      ctx.config()->posSystems()->add(obj); ctx.add(obj, i+1);\n    } else {\n      errMsg(err) << \"Invalid GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nUV390Codeplug::linkPositioningSystems(Context &ctx, const ErrorStack &err) {\n  for (int i=0; i<NUM_GPSSYSTEMS; i++) {\n    GPSSystemElement gps(data(ADDR_GPSSYSTEMS+i*GPSSYSTEM_SIZE));\n    if (! gps.isValid())\n      continue;\n    if (! gps.linkGPSSystemObj(ctx.get<DMRAPRSSystem>(i+1), ctx)) {\n      errMsg(err) << \"Cannot link GPS system at index \" << i << \".\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nvoid\nUV390Codeplug::clearButtonSettings() {\n  ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).clear();\n}\n\nbool\nUV390Codeplug::encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(ctx); Q_UNUSED(err)\n  // Encode settings\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).fromConfig(ctx.config());\n}\n\nbool\nUV390Codeplug::decodeButtonSetttings(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(err)\n  return ButtonSettingsElement(data(ADDR_BUTTONSETTINGS)).updateConfig(ctx.config());\n}\n\n\nvoid\nUV390Codeplug::clearPrivacyKeys() {\n  EncryptionElement(data(ADDR_PRIVACY_KEYS)).clear();\n}\n\nbool\nUV390Codeplug::encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(flags); Q_UNUSED(err);\n  // First, reset keys\n  clearPrivacyKeys();\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  return keys.fromCommercialExt(ctx.config()->commercialExtension(), ctx);\n}\n\nbool\nUV390Codeplug::decodePrivacyKeys(Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx.config())\n  // Get keys\n  EncryptionElement keys(data(ADDR_PRIVACY_KEYS));\n  // Decode element\n  if (! keys.updateCommercialExt(ctx)) {\n    errMsg(err) << \"Cannot create encryption extension.\";\n    return false;\n  }\n  return true;\n}\n\n\nvoid\nUV390Codeplug::clearBootSettings() {\n  BootSettingsElement(data(ADDR_BOOTSETTINGS)).clear();\n}\n\nvoid\nUV390Codeplug::clearMenuSettings() {\n  MenuSettingsElement(data(ADDR_MENUSETTINGS)).clear();\n}\n\n\nvoid\nUV390Codeplug::clearTextMessages() {\n  MessageBankElement(data(Offset::messages())).clear();\n}\n\nbool\nUV390Codeplug::encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).encode(ctx, flags, err);\n}\n\nbool\nUV390Codeplug::decodeTextMessages(Context &ctx, const ErrorStack &err) {\n  return MessageBankElement(data(Offset::messages())).decode(ctx, err);\n}\n\n\nvoid\nUV390Codeplug::clearEmergencySystems() {\n  EmergencySettingsElement(data(ADDR_EMERGENCY_SETTINGS)).clear();\n  for (int i=0; i<NUM_EMERGENCY_SYSTEMS; i++)\n    EmergencySystemElement(data(ADDR_EMERGENCY_SYSTEMS + i*EMERGENCY_SYSTEM_SIZE)).clear();\n}\n\nvoid\nUV390Codeplug::clearVFOSettings() {\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_A)).clear();\n  VFOChannelElement(data(ADDR_VFO_CHANNEL_B)).clear();\n}\n"
  },
  {
    "path": "lib/uv390_codeplug.hh",
    "content": "#ifndef UV390_CODEPLUG_HH\n#define UV390_CODEPLUG_HH\n\n#include \"dm1701_codeplug.hh\"\n\n/** Device specific implementation of the codeplug for the TyT MD-UV390.\n *\n * The codeplug consists of two segments. The first segment starts at address @c 0x002000 and ends at\n * address @c 0x040000. The second segment starts at address @c 0x110000 and ends at @c 0x1a0000. The\n * segments must align with @c 0x400 (1024 bytes).\n *\n * @section uv390cpl Codeplug structure within radio\n * The codeplug structure is reverse engineered almost completely and can be programmed from\n * scratch. That is, it is not necessary to update an existing codeplug on the radio.\n * <table>\n *  <tr><th>Start</th>    <th>End</th>      <th>Size</th>    <th>Content</th></tr>\n *  <tr><th colspan=\"4\">First segment 0x002000-0x040000</th></tr>\n *  <tr><td>0x002000</td> <td>0x00200c</td> <td>0x0000c</td> <td>Timestamp see @c TyTCodeplug::TimestampElement.</td></tr>\n *  <tr><td>0x00200c</td> <td>0x002040</td> <td>0x00034</td> <td>Reserved, filled with 0xff. </td></tr>\n *  <tr><td>0x002040</td> <td>0x0020f0</td> <td>0x000b0</td> <td>General settings see @c GeneralSettingsElement.</td></tr>\n *  <tr><td>0x0020f0</td> <td>0x002100</td> <td>0x00010</td> <td>Menu settings, see @c MenuSettingsElement.</td></tr>\n *  <tr><td>0x002100</td> <td>0x002140</td> <td>0x00040</td> <td>Button config, see @c TyTCodeplug::ButtonSettingsElement.</td></tr>\n *  <tr><td>0x002140</td> <td>0x002180</td> <td>0x00040</td> <td>Reserved, filled with 0xff.</td></tr>\n *  <tr><td>0x002180</td> <td>0x0059c0</td> <td>0x03840</td> <td>50 Text messages @ 0x120 bytes each.</td></tr>\n *  <tr><td>0x0059c0</td> <td>0x005a70</td> <td>0x000b0</td> <td>Privacy keys, see @c TyTCodeplug::EncryptionElement.</td></tr>\n *  <tr><td>0x005a70</td> <td>0x005a80</td> <td>0x00010</td> <td>Emergency system settings, see @c TyTCodeplug::EmergencySettingsElement.</td></td>\n *  <tr><td>0x005a80</td> <td>0x005f80</td> <td>0x00500</td> <td>Emergency systems, see @c TyTCodeplug::EmergencySystemElement.</td></td>\n *  <tr><td>0x005f80</td> <td>0x00ec20</td> <td>0x08ca0</td> <td>Reserved, filled with 0xff.</td></td>\n *  <tr><td>0x00ec20</td> <td>0x0149e0</td> <td>0x05dc0</td> <td>250 RX Group lists @ 0x60 bytes each, see @c TyTCodeplug::GroupListElement.</td></tr>\n *  <tr><td>0x0149e0</td> <td>0x018860</td> <td>0x03e80</td> <td>250 Zones @ 0x40 bytes each, see @c TyTCodeplug::ZoneElement.</td></tr>\n *  <tr><td>0x018860</td> <td>0x01edf0</td> <td>0x06590</td> <td>250 Scanlists @ 0x68 bytes each, see @c TyTCodeplug::ScanListElement.</td></tr>\n *  <tr><td>0x01edf0</td> <td>0x02ef00</td> <td>0x10110</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x02ef00</td> <td>0x02ef40</td> <td>0x00040</td> <td>VFO A channel, see @c VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef40</td> <td>0x02ef80</td> <td>0x00040</td> <td>VFO B channel, see @c VFOChannelElement.</td></tr>\n *  <tr><td>0x02ef80</td> <td>0x02f000</td> <td>0x00080</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x02f000</td> <td>0x02f010</td> <td>0x00010</td> <td>Boot settings, see @c BootSettingsElement.</td></tr>\n *  <tr><td>0x02f010</td> <td>0x031000</td> <td>0x01ff0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x031000</td> <td>0x03eac0</td> <td>0x0dac0</td> <td>250 Zone-extensions @ 0xe0 bytes each, see @c DM1701Codeplug::ZoneExtElement.</td></tr>\n *  <tr><td>0x03eac0</td> <td>0x03ec40</td> <td>0x00180</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x03ec40</td> <td>0x03ed40</td> <td>0x00100</td> <td>16 GPS systems @ 0x10 bytes each, see @c TyTCodeplug::GPSSystemElement.</td></tr>\n *  <tr><td>0x03ed40</td> <td>0x040000</td> <td>0x012c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><th colspan=\"4\">Second segment 0x110000-0x1a0000</th></tr>\n *  <tr><td>0x110000</td> <td>0x13ee00</td> <td>0x2ee00</td> <td>3000 Channels @ 0x40 bytes each, see @c ChannelElement.</td></tr>\n *  <tr><td>0x13ee00</td> <td>0x140000</td> <td>0x01200</td> <td>Reserved, filled with @c 0xff. </td></tr>\n *  <tr><td>0x140000</td> <td>0x197e40</td> <td>0x57e40</td> <td>10000 Contacts @ 0x24 bytes each, see @c TyTCodeplug::ContactElement.</td></tr>\n *  <tr><td>0x197e40</td> <td>0x1a0000</td> <td>0x081c0</td> <td>Reserved, filled with @c 0xff. </td></tr>\n * </table>\n *\n * @ingroup uv390 */\nclass UV390Codeplug : public TyTCodeplug\n{\n  Q_OBJECT\n\npublic:\n  /** Extends the @c TyTCodeplug::ChannelElement for the TyT MD-UV390 and Retevis RT3S.\n   *\n   * Memory layout of encoded channel:\n   * @verbinclude uv390_channel.txt */\n  class ChannelElement: public TyTCodeplug::ChannelElement\n  {\n  protected:\n    /** Constructs a channel from the given memory. */\n    ChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructs a channel from the given memory. */\n    explicit ChannelElement(uint8_t *ptr);\n\n    /** Clears/resets the channel and therefore disables it. */\n    void clear() override;\n\n    bool talkaround() const override;\n    void enableTalkaround(bool enable) override;\n\n    /** Returns @c true, if the privacy switch is enabled. */\n    virtual bool privacySwitch() const;\n    /** Enables/disables the privacy switch. */\n    virtual void enablePrivacySwitch(bool enable);\n\n    /** Overrides TyT basic implementation to enable privacy switch, whenever encryption\n     *  is enabled for this channel. */\n    void setPrivacyType(PrivacyType type) override;\n\n    /** Returns the in-call criterion for this channel. */\n    virtual TyTChannelExtension::InCallCriterion inCallCriteria() const;\n    /** Sets the in-call criterion for this channel. */\n    virtual void setInCallCriteria(TyTChannelExtension::InCallCriterion crit);\n\n    /** Returns the remote turn-off/kill frequency for this channel. */\n    virtual TyTChannelExtension::KillTone turnOffFreq() const;\n    /** Sets the remote turn-off/kill frequency for this channel. */\n    virtual void setTurnOffFreq(TyTChannelExtension::KillTone freq);\n\n    /** Returns the squelch level [0-10]. */\n    virtual Level squelch() const;\n    /** Sets the squelch level [0-10]. */\n    virtual void setSquelch(Level value);\n\n    /** Returns the power of this channel. */\n    virtual Channel::Power power() const;\n    /** Sets the power of this channel. */\n    virtual void setPower(Channel::Power pwr);\n\n    /** Returns @c true if the channel allows interruption enabled. */\n    virtual bool allowInterrupt() const;\n    /** Enables/disables interruption for this channel. */\n    virtual void enableAllowInterrupt(bool enable);\n\n    /** Returns @c true if the channel has dual-capacity direct mode enabled. */\n    virtual bool dualCapacityDirectMode() const;\n    /** Enables/disables dual-capacity direct mode for this channel. */\n    virtual void enableDualCapacityDirectMode(bool enable);\n\n    /** Returns @c true if the radio acts as the leader for this DCDM channel. */\n    virtual bool dcdmLeader() const;\n    /** Enables/disables this radio to be the leader for this DCDM channel. */\n    virtual void enableDCDMLeader(bool enable);\n\n    /** Constructs a generic @c Channel object from the codeplug channel. */\n    virtual Channel *toChannelObj(const ErrorStack &err=ErrorStack()) const override;\n    /** Initializes this codeplug channel from the given generic configuration. */\n    virtual void fromChannelObj(const Channel *c, Context &ctx) override;\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: public TyTCodeplug::ChannelElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit privacySwitch()          { return {0x0005, 2}; }\n      static constexpr Bit inCallCriteria()         { return {0x0005, 4}; }\n      static constexpr Bit turnOffFreq()            { return {0x0005, 6}; }\n      static constexpr unsigned int squelch()       { return 0x000f; }\n      static constexpr Bit power()                  { return {0x001e, 0}; }\n      static constexpr Bit allowInterrupt()         { return {0x001f, 2}; }\n      static constexpr Bit dualCapacityDirectMode() { return {0x001f, 3}; }\n      static constexpr Bit dcdmLeader()             { return {0x001f, 4}; }\n      /// @endcond\n    };\n  };\n\n  /** Implements a VFO channel for TyT radios.\n   * This class is an extension of the normal ChannelElement that only implements the step-size\n   * feature and encodes it where the name used to be. Thus the memory layout and size is identical\n   * to the normal channel. */\n  class VFOChannelElement: public ChannelElement\n  {\n  protected:\n    /** Constructor from pointer to memory. */\n    VFOChannelElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor from pointer to memory. */\n    VFOChannelElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~VFOChannelElement();\n\n    QString name() const;\n    void setName(const QString &txt);\n\n    /** Returns the step-size for the VFO channel. */\n    virtual unsigned stepSize() const;\n    /** Sets the step-size for the VFO channel in Hz. */\n    virtual void setStepSize(unsigned ss_hz);\n  };\n\n  /** Reuse zone extension from DM1701. */\n  typedef DM1701Codeplug::ZoneExtElement ZoneExtElement;\n\n  /** Extends the common @c TyTCodeplug::GeneralSettings to implement the MD-UV390 specific\n   * settings.\n   *\n   * Memory layout of the settings (size 0x00b0 bytes):\n   * @verbinclude uv390_settings.txt */\n  class GeneralSettingsElement: public DM1701Codeplug::GeneralSettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    GeneralSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    GeneralSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Defines all possible transmit modes. */\n    enum TransmitMode {\n      LAST_CALL_CH = 0,\n      LAST_CALL_AND_HAND_CH = 1,\n      DESIGNED_CH = 2,\n      DESIGNED_AND_HAND_CH = 3,\n    };\n\n    /** Returns the transmit mode. */\n    virtual TransmitMode transmitMode() const;\n    /** Sets the transmit mode. */\n    virtual void setTransmitMode(TransmitMode mode);\n\n    /** Returns @c true, if the speech synthesis is enabled. */\n    virtual bool channelVoiceAnnounce() const;\n    /** Enables/disables the speech synthesis. */\n    virtual void enableChannelVoiceAnnounce(bool enable);\n\n    /** Returns @c true, if keypad tones are enabled. */\n    virtual bool keypadTones() const;\n    /** Enables/disables the keypad tones. */\n    virtual void enableKeypadTones(bool enable);\n\n    /** Returns @c true, if public zone is enabled. */\n    virtual bool publicZone() const;\n    /** Enables/disables public zone. */\n    virtual void enablePublicZone(bool enable);\n\n    /** Returns the n-th DMR id. */\n    virtual uint32_t additionalDMRId(unsigned n) const;\n    /** Sets the n-th DMR id. */\n    virtual void setAdditionalDMRId(unsigned n, uint32_t id);\n\n    /** Returns the microphone gain. */\n    virtual Level micLevel() const;\n    /** Sets the microphone gain. */\n    virtual void setMICLevel(Level val);\n\n    /** If @c true, radio ID editing is enabled. */\n    virtual bool editRadioID() const;\n    /** Enable/disable radio ID editing. */\n    virtual void enableEditRadioID(bool enable);\n\n    /** Encodes the general settings. */\n    virtual bool fromConfig(const Config *config);\n    /** Updates config from general settings. */\n    virtual bool updateConfig(Config *config);\n\n  public:\n    /** Some limits. */\n    struct Limit: DM1701Codeplug::GeneralSettingsElement::Limit {\n      /** Specifies the valid range for mic gain. */\n      static constexpr Range<unsigned int> micGain() { return {0,6}; }\n    };\n\n  protected:\n    /** Some internal offsets. */\n    struct Offset: DM1701Codeplug::GeneralSettingsElement::Offset {\n      /// @cond DO_NOT_DOCUMENT\n      static constexpr Bit micGain() { return {0x00a0, 3}; }\n      /// @endcond\n    };\n  };\n\n  /** Represents the boot-time settings (selected zone and channels) within the UV390 code-plug.\n   *\n   * Memory layout of encoded boot settings:\n   * @verbinclude uv390_bootsettings.txt */\n  class BootSettingsElement: public Codeplug::Element\n  {\n  protected:\n    /** Hidden constructor. */\n    BootSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit BootSettingsElement(uint8_t *ptr);\n    /** Destructor. */\n    virtual ~BootSettingsElement();\n\n    void clear();\n\n    /** Returns the boot zone index. */\n    virtual unsigned zoneIndex() const;\n    /** Sets the boot zone index. */\n    virtual void setZoneIndex(unsigned idx);\n    /** Returns the channel index (within zone) for VFO A. */\n    virtual unsigned channelIndexA() const;\n    /** Sets the channel index (within zone) for VFO A. */\n    virtual void setChannelIndexA(unsigned idx);\n    /** Returns the channel index (within zone) for VFO B. */\n    virtual unsigned channelIndexB() const;\n    /** Sets the channel index (within zone) for VFO B. */\n    virtual void setChannelIndexB(unsigned idx);\n  };\n\n  /** Represents the menu settings (selected zone and channels) within the UV390 code-plug.\n   *\n   * Memory layout of encoded boot settings:\n   * @verbinclude uv390_menusettings.txt */\n  class MenuSettingsElement: public TyTCodeplug::MenuSettingsElement\n  {\n  protected:\n    /** Hidden constructor. */\n    MenuSettingsElement(uint8_t *ptr, size_t size);\n\n  public:\n    /** Constructor. */\n    explicit MenuSettingsElement(uint8_t *ptr);\n\n    void clear();\n\n    /** Returns @c true if GPS settings menu is enabled. */\n    virtual bool gpsSettings() const;\n    /** Enables/disables GPS settings menu. */\n    virtual void enableGPSSettings(bool enable);\n    /** Returns @c true if recording menu is enabled. */\n    virtual bool recording() const;\n    /** Enables/disables recording menu. */\n    virtual void enableRecording(bool enable);\n\n    /** Returns @c true if group call match menu is enabled. */\n    virtual bool groupCallMatch() const;\n    /** Enables/disables group call match menu. */\n    virtual void enableGroupCallMatch(bool enable);\n    /** Returns @c true if private call match menu is enabled. */\n    virtual bool privateCallMatch() const;\n    /** Enables/disables private call match menu. */\n    virtual void enablePrivateCallMatch(bool enable);\n    /** Returns @c true if menu hang time item is enabled. */\n    virtual bool menuHangtimeItem() const;\n    /** Enables/disables menu hang time item. */\n    virtual void enableMenuHangtimeItem(bool enable);\n    /** Returns @c true if TX mode menu is enabled. */\n    virtual bool txMode() const;\n    /** Enables/disables TX mode menu. */\n    virtual void enableTXMode(bool enable);\n    /** Returns @c true if zone settings menu is enabled. */\n    virtual bool zoneSettings() const;\n    /** Enables/disables zone settings menu. */\n    virtual void enableZoneSettings(bool enable);\n    /** Returns @c true if new zone menu is enabled. */\n    virtual bool newZone() const;\n    /** Enables/disables new zone menu. */\n    virtual void enableNewZone(bool enable);\n\n    /** Returns @c true if edit zone menu is enabled. */\n    virtual bool editZone() const;\n    /** Enables/disables edit zone menu. */\n    virtual void enableEditZone(bool enable);\n    /** Returns @c true if new scan list menu is enabled. */\n    virtual bool newScanList() const;\n    /** Enables/disables new scan list menu. */\n    virtual void enableNewScanList(bool enable);\n  };\n\n\npublic:\n  /** Constructor. */\n  explicit UV390Codeplug(QObject *parent = nullptr);\n  /** Destructor. */\n  virtual ~UV390Codeplug();\n\n  void clear();\n\npublic:\n  void clearTimestamp();\n  bool encodeTimestamp();\n\n  void clearGeneralSettings();\n  bool encodeGeneralSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeGeneralSettings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearChannels();\n  bool encodeChannels( const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkChannels(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearContacts();\n  bool encodeContacts(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createContacts(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearZones();\n  bool encodeZones(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createZones(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkZones(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearGroupLists();\n  bool encodeGroupLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkGroupLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearScanLists();\n  bool encodeScanLists(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkScanLists(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPositioningSystems();\n  bool encodePositioningSystems(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool createPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n  bool linkPositioningSystems(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearButtonSettings();\n  bool encodeButtonSettings(const Flags &flags, Context &ctx, const ErrorStack &err=ErrorStack());\n  bool decodeButtonSetttings(Context &ctx, const ErrorStack &err=ErrorStack());\n\n  void clearPrivacyKeys();\n  bool encodePrivacyKeys(const Flags &flags, Context &ctx, const ErrorStack &err);\n  bool decodePrivacyKeys(Context &ctx, const ErrorStack &err);\n\n  void clearTextMessages();\n  bool encodeTextMessages(Context &ctx, const Flags &flags, const ErrorStack &err);\n  bool decodeTextMessages(Context &ctx, const ErrorStack &err);\n\n  /** Resets the boot setting, e.g. initial channels and zone at bootup. */\n  virtual void clearBootSettings();\n  void clearMenuSettings();\n  void clearEmergencySystems();\n  /** Clears the VFO A & B. */\n  virtual void clearVFOSettings();\n\npublic:\n  /** Some limits for the codeplug. */\n  struct Limit: public Element::Limit {\n    /// Number of channels.\n    static constexpr unsigned int channels() { return 3000; }\n  };\n\nprotected:\n  /** Some internal offsets within the codeplug. */\n  struct Offset {\n    /// @cond DO_NOT_DOCUMENT\n    static constexpr unsigned int messages() { return 0x002180; }\n    static constexpr unsigned int channels() { return 0x110000; }\n    static constexpr unsigned int betweenChannels() { return ChannelElement::size(); }\n    /// @endcond\n  };\n};\n\n#endif // UV390CODEPLUG_HH\n"
  },
  {
    "path": "lib/uv390_filereader.cc",
    "content": "#include \"uv390_filereader.hh\"\n#include <QFile>\n#include <QFileInfo>\n\n#define SEGMENT0_FILE_ADDR   0x00002225\n#define SEGMENT0_TARGET_ADDR 0x00002000\n#define SEGMENT0_SIZE        0x0003e000\n#define SEGMENT1_FILE_ADDR   0x00040235\n#define SEGMENT1_TARGET_ADDR 0x00110000\n#define SEGMENT1_SIZE        0x00090000\n\nbool\nUV390FileReader::read(const QString &filename, UV390Codeplug *codeplug, const ErrorStack &err)\n{\n  // Check file properties\n  QFileInfo info(filename);\n  if (! info.exists()) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': File does not exisist.\";\n    return false;\n  }\n  if (852533 != info.size()) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename << \"': File size is not 852533 bytes.\";\n    return false;\n  }\n\n  // Open file\n  QFile file(filename);\n  if (! file.open(QFile::ReadOnly)) {\n    errMsg(err) << \"Cannot open file '\" << filename << \"': \" << file.errorString() << \".\";\n    return false;\n  }\n\n  // Read file content\n\n  if (! file.seek(SEGMENT0_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  char *ptr = (char *)codeplug->data(SEGMENT0_TARGET_ADDR);\n  size_t n = SEGMENT0_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  if (! file.seek(SEGMENT1_FILE_ADDR)) {\n    errMsg(err) << \"Cannot read codeplug file '\" << filename\n                << \"': Cannot seek within file: \" << file.errorString() << \".\";\n    file.close();\n    return false;\n  }\n  ptr = (char *)codeplug->data(SEGMENT1_TARGET_ADDR);\n  n = SEGMENT1_SIZE;\n  while (0 < n) {\n    int nread = file.read(ptr, n);\n    if (0 > nread) {\n      errMsg(err) << \"Cannot read codeplug file '\" << filename\n                  << \"': \" << file.errorString() << \".\";\n      file.close();\n      return false;\n    }\n    n -= nread;\n    ptr += nread;\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "lib/uv390_filereader.hh",
    "content": "#ifndef UV390FILEREADER_HH\n#define UV390FILEREADER_HH\n\n#include \"uv390_codeplug.hh\"\n\n/** Methods to read manufacturer codeplug files.\n *\n * The file format of the stock CPS is still pretty simple. The first part of the file consists of\n * a mal-formed DFU file. This contains a single image with a single element containing the\n * first section of the memory written to the device. The second section is then added as-is\n * to the end of the file. Due to the DFU header/footer, the file and memory offsets differ.\n *\n * <table>\n *  <tr><th>File Start</th> <th>Memory Start</th> <th>Size</th></tr>\n *  <tr><td>0x002225</td>   <td>0x002000</td>     <td>0x03e000</td></tr>\n *  <tr><td>0x040235</td>   <td>0x110000</td>     <td>0x090000</td></tr>\n * </table>\n *\n * @ingroup uv390 */\nclass UV390FileReader\n{\npublic:\n  /** Reads manufacturer codeplug file into given codeplug object.\n   * @param filename Specifies the file to read.\n   * @param codeplug Specifies the codeplug object to store read codeplug.\n   * @param err Error stack.\n   * @returns @c true on success and @c false on error. */\n  static bool read(const QString &filename, UV390Codeplug *codeplug, const ErrorStack &err = ErrorStack());\n};\n\n#endif // UV390FILEREADER_HH\n"
  },
  {
    "path": "lib/uv390_limits.cc",
    "content": "#include \"uv390_limits.hh\"\n#include \"uv390_codeplug.hh\"\n#include \"channel.hh\"\n#include \"radioid.hh\"\n#include \"contact.hh\"\n#include \"rxgrouplist.hh\"\n#include \"scanlist.hh\"\n#include \"zone.hh\"\n#include \"gpssystem.hh\"\n#include \"roamingzone.hh\"\n\n\nUV390Limits::UV390Limits(QObject *parent)\n  : RadioLimits(false, parent)\n{\n  // Define limits for call-sign DB\n  _hasCallSignDB          = true;\n  _callSignDBImplemented  = true;\n  _numCallSignDBEntries   = 122197;\n\n  // Define limits for satellite config\n  _hasSatelliteConfig          = false;\n  _satelliteConfigImplemented  = false;\n  _numSatellites               = 0;\n\n  add(\"settings\", new RadioLimitItem {\n        { \"introLine1\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"introLine2\", new RadioLimitString(-1, 10, RadioLimitString::Unicode) },\n        { \"micLevel\", new RadioLimitLevel({1, 10}, false) },\n        { \"speech\", new RadioLimitIgnoredBool() },\n        { \"power\", new RadioLimitEnum {\n            unsigned(Channel::Power::Low),\n            unsigned(Channel::Power::Mid),\n            unsigned(Channel::Power::High) } },\n        { \"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n        { \"tot\", new RadioLimitInterval() },\n        { \"boot\", new RadioLimitItem {\n          {\"passwordEnabled\", new RadioLimitIgnored(RadioLimitIssue::Silent) },\n          {\"password\", new RadioLimitPin(UV390Codeplug::GeneralSettingsElement::Limit::bootPasswordLength(), RadioLimitIssue::Critical) } } }\n        /// @todo check default radio ID.\n      } );\n\n  /* Define limits for radio IDs. */\n  add(\"radioIDs\", new RadioLimitList{\n        { DMRRadioID::staticMetaObject, 1, 1, new RadioLimitObject {\n            {\"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            {\"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Critical)}\n          } }\n      } );\n\n  /* Define limits for contacts. */\n  add(\"contacts\", new RadioLimitList{\n        { DMRContact::staticMetaObject, 1, 10000, new RadioLimitObject {\n            { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n            { \"ring\", new RadioLimitBool() },\n            { \"type\", new RadioLimitEnum {\n                (unsigned) DMRContact::PrivateCall,\n                (unsigned) DMRContact::GroupCall,\n                (unsigned) DMRContact::AllCall\n              }},\n            { \"number\", new RadioLimitDMRId(RadioLimitIssue::Severity::Hint) }\n          } },\n        { DTMFContact::staticMetaObject, -1, -1, new RadioLimitIgnored() }\n      } );\n\n  /* Define limits for group lists. */\n  add(\"groupLists\", new RadioLimitList(\n        RXGroupList::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"contacts\", new RadioLimitGroupCallRefList(1, 32) }\n        }) );\n\n  /* Define limits for channel list. */\n  add(\"channels\", new RadioLimitList(\n        Channel::staticMetaObject, 1, UV390Codeplug::Limit::channels(),\n        new RadioLimitObjects {\n          { FMChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, UV390Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}}, RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.),\n                                                                  Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.),\n                                                                  Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum{unsigned(Channel::Power::Low), unsigned(Channel::Power::High)}},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef({ScanList::staticMetaObject})},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"admit\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Admit::Always,\n                 (unsigned)FMChannel::Admit::Free,\n                 (unsigned)FMChannel::Admit::Tone\n               } },\n              {\"squelch\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"bandwidth\", new RadioLimitEnum{\n                 (unsigned)FMChannel::Bandwidth::Narrow,\n                 (unsigned)FMChannel::Bandwidth::Wide\n               }},\n              {\"aprs\", new RadioLimitObjRefIgnored(nullptr, RadioLimitIssue::Hint)}\n            } },\n          { DMRChannel::staticMetaObject,\n            new RadioLimitObject {\n              {\"name\", new RadioLimitString(1, UV390Codeplug::ChannelElement::Limit::nameLength(), RadioLimitString::Unicode)},\n              {\"rxFrequency\", new RadioLimitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                         {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}},RadioLimitIssue::Severity::Critical)},\n              {\"txFrequency\", new RadioLimitTransmitFrequencies({{Frequency::fromMHz(136.), Frequency::fromMHz(174.)},\n                                                                 {Frequency::fromMHz(400.), Frequency::fromMHz(480.)}})},\n              {\"power\", new RadioLimitEnum {\n                 unsigned(Channel::Power::Low),\n                 unsigned(Channel::Power::Mid),\n                 unsigned(Channel::Power::High),\n               }},\n              {\"timeout\", new RadioLimitInterval()},\n              {\"scanlist\", new RadioLimitObjRef(ScanList::staticMetaObject)},\n              {\"vox\", new RadioLimitIgnored(RadioLimitIssue::Silent)},\n              {\"rxOnly\", new RadioLimitBool()},\n              {\"admit\", new RadioLimitEnum {\n                 unsigned(DMRChannel::Admit::Always),\n                 unsigned(DMRChannel::Admit::Free),\n                 unsigned(DMRChannel::Admit::ColorCode) } },\n              {\"colorCode\", new RadioLimitUInt(0,16)},\n              {\"timeSlot\", new RadioLimitEnum {\n                 unsigned(DMRChannel::TimeSlot::TS1),\n                 unsigned(DMRChannel::TimeSlot::TS2) } },\n              {\"radioID\", new RadioLimitObjRef(RadioID::staticMetaObject, true)},\n              {\"groupList\", new RadioLimitObjRef(RXGroupList::staticMetaObject, false)},\n              {\"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, true)},\n              {\"aprs\", new RadioLimitObjRefIgnored()},\n              {\"roaming\", new RadioLimitObjRefIgnored(DefaultRoamingZone::get())},\n              {\"openGD77\", new RadioLimitIgnored(RadioLimitIssue::Hint)},\n              {\"tyt\", new RadioLimitIgnored(RadioLimitIssue::Hint)}\n            } }\n        } ) );\n\n  /* Define limits for zone list. */\n  add(\"zones\", new RadioLimitList(\n        Zone::staticMetaObject, 1, 250, new RadioLimitObject {\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) }, // 16 ASCII chars in name\n          { \"A\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"B\", new RadioLimitRefList(0, 64, Channel::staticMetaObject) },\n          { \"anytone\", new RadioLimitIgnored(RadioLimitIssue::Hint) }     // ignore AnyTone extensions\n        } ) );\n\n  /* Define limits for scan lists. */\n  add(\"scanlists\", new RadioLimitList(\n        ScanList::staticMetaObject, 0, 250, new RadioLimitObject{\n          { \"name\", new RadioLimitString(1, 16, RadioLimitString::Unicode) },\n          { \"primary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"secondary\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"revert\", new RadioLimitObjRef(Channel::staticMetaObject, true) },\n          { \"channels\", new RadioLimitRefList(0, 31, Channel::staticMetaObject) }\n        }) );\n\n  /* Define limits for positioning systems in case, this is a MD-380G/MD-390G. */\n  add(\"positioning\", new RadioLimitList({\n        { DMRAPRSSystem::staticMetaObject, 0, 16, new RadioLimitObject {\n          { \"name\", new RadioLimitStringIgnored() },\n          { \"period\", new RadioLimitInterval({Interval::null(), Interval::fromMinutes(127)}) },\n          { \"contact\", new RadioLimitObjRef(DMRContact::staticMetaObject, false) },\n          { \"revert\", new RadioLimitObjRef({SelectedChannel::staticMetaObject, DMRChannel::staticMetaObject}, true) }\n          } },\n        { FMAPRSSystem::staticMetaObject, 0, -1, new RadioLimitIgnored() } }) );\n\n  /* Check encryption keys. */\n  add(\"commercial\", new RadioLimitItem {\n        {\"encryptionKeys\", new RadioLimitList {\n           {BasicEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::basicKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{4}\")}\n            }},\n           {AESEncryptionKey::staticMetaObject,\n            0, TyTCodeplug::EncryptionElement::Limit::advancedKeys(),\n            new RadioLimitObject {\n              {\"name\", new RadioLimitIgnored()},\n              {\"key\", new RadioLimitStringRegEx(\"[0-9a-fA-F]{32}\")}\n            }} }\n        }\n      });\n\n  /* Ignore roaming zones. */\n  add(\"roaming\", new RadioLimitList(\n        ConfigObject::staticMetaObject, -1, -1, new RadioLimitIgnored(RadioLimitIssue::Hint)\n        ) );\n}\n"
  },
  {
    "path": "lib/uv390_limits.hh",
    "content": "#ifndef UV390LIMITS_HH\n#define UV390LIMITS_HH\n\n#include \"radiolimits.hh\"\n\n/** Implements the configuration limits for the TyT MD-UV390 and Retevis RT3S.\n * @ingroup uv390 */\nclass UV390Limits : public RadioLimits\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit UV390Limits(QObject *parent=nullptr);\n};\n\n#endif // UV390LIMITS_HH\n"
  },
  {
    "path": "lib/visitor.cc",
    "content": "#include \"visitor.hh\"\n#include \"config.hh\"\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n#include \"logger.hh\"\n\nVisitor::Visitor()\n{\n  // Pass...\n}\n\nVisitor::~Visitor() {\n  // pass...\n}\n\nbool\nVisitor::process(Config *config, const ErrorStack &err) {\n  return this->processItem(config, err);\n}\n\nbool\nVisitor::processItem(ConfigItem *item, const ErrorStack &err) {\n  // Process all properties\n  const QMetaObject *meta = item->metaObject();\n  for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n    QMetaProperty prop = meta->property(p);\n    if (! prop.isValid()) {\n      logWarn() << \"Found invalid property at index \" << p << \" in an instance of '\"\n                << meta->className() << \"'. Skip.\";\n      continue;\n    }\n\n    if (! this->processProperty(item, prop, err)) {\n      errMsg(err) << \"While processing property '\" << prop.name() << \"' of '\"\n                  << meta->className() << \"'.\";\n      return false;\n    }\n  }\n  return true;\n}\n\nbool\nVisitor::processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err) {\n  if (prop.isFlagType()) {\n    if (! this->processFlags(item, prop, err)) {\n      errMsg(err) << \"While processing flags '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (prop.isEnumType()) {\n    if (! this->processEnum(item, prop, err)) {\n      errMsg(err) << \"While processing enum '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::Bool == prop.typeId()) {\n    if (! this->processBool(item, prop, err)) {\n      errMsg(err) << \"While processing boolean '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::Int == prop.typeId()) {\n    if (! this->processInt(item, prop, err)) {\n      errMsg(err) << \"While processing integer '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::UInt == prop.typeId()) {\n    if (! this->processUInt(item, prop, err)) {\n      errMsg(err) << \"While processing unsigned integer '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::Double == prop.typeId()) {\n    if (! this->processDouble(item, prop, err)) {\n      errMsg(err) << \"While processing double '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::QString == prop.typeId()) {\n    if (! this->processString(item, prop, err)) {\n      errMsg(err) << \"While processing string '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n    if (! this->processFrequency(item, prop, err)) {\n      errMsg(err) << \"While processing frequency '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n    if (! this->processInterval(item, prop, err)) {\n      errMsg(err) << \"While processing interval '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n    if (! this->processLevel(item, prop, err)) {\n      errMsg(err) << \"While processing level '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::fromType<SelectiveCall>() == prop.metaType()) {\n    if (! this->processSelectiveCall(item, prop, err)) {\n      errMsg(err) << \"While processing selective call '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (QMetaType::fromType<QGeoCoordinate>() == prop.metaType()) {\n    if (! this->processGeoCoordinate(item, prop, err)) {\n      errMsg(err) << \"While processing geo coordinate '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (ConfigObjectReference *ref = prop.read(item).value<ConfigObjectReference *>()) {\n    if (! this->processReference(ref, err)) {\n      errMsg(err) << \"While processing reference '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (ConfigObjectRefList *refs = prop.read(item).value<ConfigObjectRefList *>()) {\n    if (! this->processList(refs, err)) {\n      errMsg(err) << \"While processing reference list '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (propIsInstance<ConfigItem>(prop)) {\n    ConfigItem *pitem = prop.read(item).value<ConfigItem *>();\n    // Some items, held as writeable properties might be null (e.g., extensions)\n    if (prop.isWritable() && (nullptr == pitem))\n      return true;\n    // Go for it\n    if (! this->processItem(pitem, err)) {\n      errMsg(err) << \"While processing item '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else if (ConfigObjectList *lst = prop.read(item).value<ConfigObjectList *>()) {\n    if (! this->processList(lst, err)) {\n      errMsg(err) << \"While processing reference list '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"'.\";\n      return false;\n    }\n  } else {\n    if (! this->processUnknownType(item, prop, err)) {\n      errMsg(err) << \"While processing property '\" << prop.name() << \"' of '\"\n                  << item->metaObject()->className() << \"' of unknown type.\";\n      return false;\n\n    }\n  }\n\n  return true;\n}\n\nbool\nVisitor::processBool(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processFlags(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processEnum(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processInt(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processUInt(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processDouble(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processString(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processFrequency(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processInterval(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processLevel(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processSelectiveCall(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processGeoCoordinate(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  Q_UNUSED(parent); Q_UNUSED(prop); Q_UNUSED(err)\n  // Does nothing, return true;\n  return true;\n}\n\nbool\nVisitor::processUnknownType(ConfigItem *parent, const QMetaProperty &prop, const ErrorStack &err) {\n  errMsg(err) << \"Cannot handle property '\" << prop.name() << \"' of '\"\n              << parent->metaObject()->className() << \"': Unknown type '\"\n              << prop.typeName()  << \"'.\";\n  return false;\n}\n\nbool\nVisitor::processReference(ConfigObjectReference* ref, const ErrorStack &err) {\n  Q_UNUSED(ref); Q_UNUSED(err)\n  // Does nothing, returns true;\n  return true;\n}\n\nbool\nVisitor::processList(AbstractConfigObjectList *list, const ErrorStack &err) {\n  if (ConfigObjectList *objList = qobject_cast<ConfigObjectList *>(list)) {\n    for (int i=0; i<objList->count(); i++) {\n      if (! this->processItem(objList->get(i), err)) {\n        errMsg(err) << \"While processing object list.\";\n        return false;\n      }\n    }\n  }\n  return true;\n}\n\n"
  },
  {
    "path": "lib/visitor.hh",
    "content": "#ifndef VISITOR_HH\n#define VISITOR_HH\n\n#include <QObject>\n#include \"errorstack.hh\"\n\n// Forward declarations\nclass Config;\nclass ConfigItem;\nclass ConfigObjectReference;\nclass AbstractConfigObjectList;\n\n\n/** Base visitor class for the config tree.\n *\n *  This class can be used to implement a convenient tree taversal for the entry configuration.\n *\n * @ingroup config */\nclass Visitor\n{\nprotected:\n  /** Hidden constructor. */\n  Visitor();\n\npublic:\n  /** Destructor. */\n  virtual ~Visitor();\n\n  /** Traverses the properties of the configuration recursively.\n   * @returns @c true on success. Error information can be found in the error stack, if passed. */\n  virtual bool process(Config *config, const ErrorStack &err=ErrorStack());\n\n  /** Processes the specified property of the item.\n   * This method dispatches to the type-specific methods like @c processEnum, @c processBool,\n   * @c processInt, @c processUInt, @c processDouble, @c processString, @c processItem,\n   * @c processReference, @c processList, and @c processUnknownType, depending on the type of the\n   * property. Do not override this method unless you need to handle all properties differently. */\n  virtual bool processProperty(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n\n  /** Handles a flag typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processFlags(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles an enum typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processEnum(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a boolean typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processBool(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles an integer typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processInt(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles an unsigned integer typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processUInt(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a double precision float typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processDouble(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a string typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processString(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a @c Frequency typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processFrequency(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a @c Interval typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processInterval(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a @c Level typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processLevel(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a @c SelectiveCall typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processSelectiveCall(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a @c QGeoCoordinate typed property.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processGeoCoordinate(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n  /** Handles a property of unknown type.\n   * Returns always @c false.\n   * @param item Specifies the config item holding this property.\n   * @param prop Specifies the property.\n   * @param err Specifies the error stack to pass on. */\n  virtual bool processUnknownType(ConfigItem *item, const QMetaProperty &prop, const ErrorStack &err=ErrorStack());\n\n  /** Traverses the specified config item.\n   * This method calls @c processProperty on all properties of the item. */\n  virtual bool processItem(ConfigItem *item, const ErrorStack &err=ErrorStack());\n\n  /** Traverses the list of objects or references.\n   * If @c list is a list of @c ConfigItem, the visitor will traverse those by calling\n   * @c processItem on every of these element. If it is a list of @c ConfigObjectRef references,\n   * the visitor stop here and simply return @c true. By default, the visitor does not follow\n   * references. */\n  virtual bool processList(AbstractConfigObjectList *list, const ErrorStack &err=ErrorStack());\n\n  /** Handles references to config objects.\n   * By default, the method will simply return @c true. The visitor does not follow references. */\n  virtual bool processReference(ConfigObjectReference*, const ErrorStack &err=ErrorStack());\n};\n\n#endif // VISITOR_HH\n"
  },
  {
    "path": "lib/xmodem.cc",
    "content": "#include \"xmodem.hh\"\n#include \"logger.hh\"\n\n\nXModem::XModem(OpenRTXLinkStream *link, QObject *parent)\n  : QObject(parent), _link(link), _state(State::Init), _maxRetry(10)\n{\n  // pass...\n}\n\n\nbool\nXModem::receive(QByteArray &buffer, int timeout, const ErrorStack &err) {\n  if (State::Init != _state) {\n    errMsg(err) << \"Cannot start reception of data. Not in INIT state.\";\n    return false;\n  }\n\n  // Clear buffer\n  buffer.clear();\n\n  // Init transfer\n  if (! this->txByte(CRC, timeout, err)) {\n    _state = State::Error;\n    errMsg(err) << \"Cannot initialize transfer.\";\n    return false;\n  }\n\n  // Expected sequence number\n  uint8_t seqNum = 1;\n  QByteArray payload;\n  unsigned int retry_count = 0;\n\n  while (true) {\n    uint8_t rsp, seq, seq1c;\n    uint16_t crc;\n\n    if (! this->rxByte(rsp, timeout, err)) {\n      _state = State::Error;\n      errMsg(err) << \"Cannot initialize transfer.\";\n      return false;\n    }\n\n    switch (_state) {\n    case State::Init:\n    case State::Transfer:\n      switch (rsp) {\n      case SOH:\n      case STX:\n        // receive sequence number\n        if ((! rxByte(seq, timeout)) || (! rxByte(seq1c, timeout))) {\n          _state = State::Error;\n          errMsg(err) << \"Cannot receive sequence numbers.\";\n          return false;\n        }\n\n        // allocate payload buffer based on type\n        payload.clear();\n        if (SOH == rsp)\n          payload.resize(128);\n        else\n          payload.resize(1024);\n\n        // receive payload\n        if (! rxBytes((uint8_t *)payload.data(), payload.size(), timeout)) {\n          _state = State::Error;\n          errMsg(err) << \"Cannot receive payload.\";\n          return false;\n        }\n\n        // receive CRC\n        if (! rxBytes((uint8_t *)&crc, 2, timeout)) {\n          _state = State::Error;\n          errMsg(err) << \"Cannot receive CRC16.\";\n          return false;\n        }\n\n        // check sequence number. On mismatch -> cancel transfer\n        if ((seq != seqNum) || ((255-seq) != seq1c)) {\n          _state = State::Init;\n          if (! txByte(CAN, timeout, err)) {\n            _state = State::Error;\n            return false;\n          }\n          if (! txByte(CAN, timeout, err)) {\n            _state = State::Error;\n            return false;\n          }\n          return false;\n        }\n        seqNum++;\n\n        // Check CRC\n        if (crc != crc_ccitt(payload)) {\n          if (! txByte(NAK, timeout, err)) {\n            _state = State::Error;\n            return false;\n          }\n          retry_count++;\n          if (retry_count > _maxRetry) {\n            errMsg(err) << \"Maximum retries (\" << _maxRetry<<\") reached.\";\n            _state = State::Error;\n            return false;\n          }\n        } else {\n          if( !txByte(ACK, timeout, err)) {\n            _state = State::Error;\n            return false;\n          }\n        }\n\n        if (State::Init == _state)\n          _state = State::Transfer;\n        // State updated to transfer -> continue transfer\n        retry_count = 0;\n        break;\n\n      case EOT:\n        // Once EOT received -> state back to init\n        _state = State::Init;\n        // ACK\n        if (! txByte(ACK, timeout, err)) {\n          _state = State::Error;\n          return false;\n        }\n        // Done.\n        return true;\n\n      default:\n        // Unknown command:\n        _state = State::Error;\n        errMsg(err) << \"Unknown command \" << rsp << \".\";\n        return false;\n      }      \n    case State::Error:\n      return false;\n    }\n  }\n\n  // Should never be reached.\n}\n\nbool\nXModem::send(const QByteArray &buffer, int timeout, const ErrorStack &err) {\n  if (State::Init != _state) {\n    errMsg(err) << \"Cannot start reception of data. Not in INIT state.\";\n    return false;\n  }\n\n  // Wait for CRC\n  uint8_t cmd;\n  if ((! rxByte(cmd, timeout, err)) || (CRC != cmd)) {\n    _state = State::Error;\n    errMsg(err) << \"Cannot start transfer.\";\n    return false;\n  }\n\n  uint8_t seqNum = 1;\n  quint64 offset = 0;\n  quint64 length = buffer.length();\n  QByteArray payload; payload.resize(1024);\n\n  while (length) {\n    _state = State::Transfer;\n    // Copy a chunk\n    payload.fill(0);\n    quint64 n_send = std::min(1024ULL, length);\n    memcpy(payload.data(), buffer.constData()+offset, n_send);\n\n    // Transmit header\n    if ((!txByte(STX, timeout, err)) || (!txByte(seqNum, timeout, err)) || (!txByte(255-seqNum, timeout, err))) {\n      _state = State::Error;\n      errMsg(err) << \"Cannot send header.\";\n      return false;\n    }\n\n    // Transmit data\n    if (!txBytes((const uint8_t *)payload.constData(), payload.length(), timeout, err)) {\n      _state = State::Error;\n      errMsg(err) << \"Cannot send payload\";\n      return false;\n    }\n\n    // Transmit CRC\n    uint16_t crc = crc_ccitt(payload);\n    if (!txBytes((const uint8_t *)&crc, 2, timeout, err)) {\n      _state = State::Error;\n      errMsg(err) << \"Cannot send CRC.\";\n      return false;\n    }\n\n    // Receive ACK/NACK/CAN\n    if (! rxByte(cmd, timeout, err)) {\n      _state = State::Error;\n      errMsg(err) << \"Cannot receive response.\";\n      return false;\n    }\n\n    switch (cmd) {\n    case ACK:\n      // Increment sequence number\n      seqNum++;\n      // Update length and offset\n      length -= n_send; offset += n_send;\n      break;\n    case NAK:\n      // Resend\n      break;\n    case CAN:\n      // Canceled by receiver\n      _state = State::Init;\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nbool\nXModem::txByte(uint8_t byte, int timeout, const ErrorStack &err) {\n  Q_UNUSED(timeout)\n  if (1 != _link->write((const char *)&byte, 1)) {\n    errMsg(err) << \"Cannot send byte: \" << _link->errorString();\n    return false;\n  }\n  return true;\n}\n\nbool\nXModem::rxByte(uint8_t &byte, int timeout, const ErrorStack &err) {\n  if ((! _link->bytesAvailable()) && (!_link->waitForReadyRead(timeout))) {\n    errMsg(err) << \"Read time-out.\";\n    return false;\n  }\n  if (! _link->getChar((char *)&byte)) {\n    errMsg(err) << \"Cannot receive byte: \" << _link->errorString();\n    return false;\n  }\n  return true;\n}\n\nbool\nXModem::txBytes(const uint8_t *buffer, unsigned int length, int timeout, const ErrorStack &err) {\n  while (length) {\n    qint64 nb_written = _link->write((const char *)buffer, length);\n    if (0 > nb_written) {\n      errMsg(err) << \"Cannot write to interface: \" << _link->errorString();\n      return false;\n    }\n    length -= nb_written;\n    if (! _link->waitForBytesWritten(timeout)) {\n      errMsg(err) << \"Read time-out.\";\n      return false;\n    }\n  }\n\n  return true;\n}\n\nbool\nXModem::rxBytes(uint8_t *buffer, unsigned int length, int timeout, const ErrorStack &err) {\n  while (length) {\n    if ((! _link->bytesAvailable()) && (!_link->waitForReadyRead(timeout))) {\n      errMsg(err) << \"Read time-out.\";\n      return false;\n    }\n    qint64 nb_read = _link->read((char *)buffer, length);\n    if (0 > nb_read) {\n      errMsg(err) << \"Cannot read from interface: \" << _link->errorString();\n      return false;\n    }\n    length -= nb_read;\n  }\n\n  return true;\n}\n\n\nuint16_t\nXModem::crc_ccitt(const QByteArray &data) {\n  uint16_t x   = 0;\n  uint16_t crc = 0;\n  const uint8_t *buf = ((const uint8_t *) data.constData());\n\n  for (int i = 0; i < data.length(); i++) {\n    x   = (crc >> 8) ^ buf[i];\n    x  ^= x >> 4;\n    crc = (crc << 8) ^ (x << 12) ^ (x << 5) ^ x;\n  }\n\n  return crc ^ 0xFFFF;\n}\n"
  },
  {
    "path": "lib/xmodem.hh",
    "content": "#ifndef XMODEM_HH\n#define XMODEM_HH\n\n#include \"openrtx_link.hh\"\n\n/** Implements the XMODEM protocol (1k + crc16 variant) for a packet stream.\n *\n * Provides two methods to send and receive an entire \"file\".\n *\n * @ingroup rif */\nclass XModem : public QObject\n{\n  Q_OBJECT\n\nprotected:\n  /** Possible states of the state machine. */\n  enum class State {\n    Init, Transfer, Error\n  };\n\n  /** Possible XMODEM control bytes. */\n  enum CtrlByte {\n    SOH = 0x01, STX = 0x02, EOT = 0x04, ACK = 0x06, NAK = 0x15, CAN = 0x18, CRC = 0x43\n  };\n\npublic:\n  /** Constructs a xmodem connection via the USB device specified by @c descriptor. */\n  explicit XModem(OpenRTXLinkStream *transferLayer, QObject *parent=nullptr);\n\n  /** Receives an entire file from the device.\n   * @param buffer The buffer to store the data in. The contents of the buffer will be cleared.\n   * @param timeout Specifies the time-out in milliseconds. If a negative number is set, no\n   *        time-out applies.\n   * @param err Optional stack for error messages. */\n  bool receive(QByteArray &buffer, int timeout=-1, const ErrorStack &err=ErrorStack());\n\n  /** Sends the contents of @c buffer to the device.\n   *  @param buffer [in] Specifies the data to send.\n   *  @param timeout Specifies the time-out in milliseconds. If a negativ number is passed, no\n   *         time-out applies.\n   *  @param err Specifies the stack for error messages. */\n  bool send(const QByteArray& buffer, int timeout=-1, const ErrorStack &err=ErrorStack());\n\nprotected:\n  /** Recives a single byte from the device. */\n  bool rxByte(uint8_t &b, int timeout=-1, const ErrorStack &err=ErrorStack());\n  /** Sends a single byte to the device. */\n  bool txByte(uint8_t b, int timeout=-1, const ErrorStack &err=ErrorStack());\n  /** Receives exactly @c size bytes from the device. */\n  bool rxBytes(uint8_t *data, unsigned int size, int timeout=-1, const ErrorStack &err=ErrorStack());\n  /** Sends exactly @c size bytes to the device. */\n  bool txBytes(const uint8_t *data, unsigned int size, int timeout=-1, const ErrorStack &err=ErrorStack());\n  /** Computes the CRC16 checksum. */\n  static uint16_t crc_ccitt(const QByteArray &data);\n\nprivate:\n  /// A weak reference to the transfer layer.\n  OpenRTXLinkStream *_link;\n  /// State of the state machine.\n  State _state;\n  /// Maximum number of retires.\n  unsigned int _maxRetry;\n};\n\n#endif // XMODEM_HH\n"
  },
  {
    "path": "lib/zone.cc",
    "content": "#include \"zone.hh\"\n#include \"channel.hh\"\n#include \"config.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of Zone\n * ********************************************************************************************* */\nZone::Zone(QObject *parent)\n  : ConfigObject(parent), _A(), _B(), _anytone(nullptr)\n{\n  connect(&_A, SIGNAL(elementAdded(int)), this, SIGNAL(modified()));\n  connect(&_A, SIGNAL(elementRemoved(int)), this, SIGNAL(modified()));\n  connect(&_B, SIGNAL(elementAdded(int)), this, SIGNAL(modified()));\n  connect(&_B, SIGNAL(elementRemoved(int)), this, SIGNAL(modified()));\n}\n\nZone::Zone(const QString &name, QObject *parent)\n  : ConfigObject(name, parent), _A(), _B(), _anytone(nullptr)\n{\n  connect(&_A, SIGNAL(elementAdded(int)), this, SIGNAL(modified()));\n  connect(&_A, SIGNAL(elementRemoved(int)), this, SIGNAL(modified()));\n  connect(&_B, SIGNAL(elementAdded(int)), this, SIGNAL(modified()));\n  connect(&_B, SIGNAL(elementRemoved(int)), this, SIGNAL(modified()));\n}\n\nZone &\nZone::operator =(const Zone &other) {\n  copy(other);\n  return *this;\n}\n\nConfigItem *\nZone::clone() const {\n  Zone *z = new Zone();\n  if (! z->copy(*this)) {\n    z->deleteLater();\n    return nullptr;\n  }\n  return z;\n}\n\nvoid\nZone::clear() {\n  _name.clear();\n  _A.clear();\n  _B.clear();\n}\n\nconst ChannelRefList *\nZone::A() const {\n  return &_A;\n}\nChannelRefList *\nZone::A() {\n  return &_A;\n}\n\nconst ChannelRefList *\nZone::B() const {\n  return &_B;\n}\nChannelRefList *\nZone::B() {\n  return &_B;\n}\n\nbool\nZone::contains(Channel *obj) const {\n  return _A.has(obj) || _B.has(obj);\n}\n\nAnytoneZoneExtension *\nZone::anytoneExtension() const {\n  return _anytone;\n}\nvoid\nZone::setAnytoneExtension(AnytoneZoneExtension *ext) {\n  if (_anytone == ext)\n    return;\n  if (_anytone) {\n    disconnect(_anytone, SIGNAL(modified(ConfigItem*)), this, SIGNAL(modified(ConfigItem*)));\n    _anytone->deleteLater();\n  }\n  _anytone = ext;\n  if (_anytone) {\n    _anytone->setParent(this);\n    connect(_anytone, SIGNAL(modified(ConfigItem*)), this, SIGNAL(modified(ConfigItem*)));\n  }\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneList\n * ********************************************************************************************* */\nZoneList::ZoneList(QObject *parent)\n  : ConfigObjectList(Zone::staticMetaObject, parent)\n{\n  // pass...\n}\n\nZone *\nZoneList::zone(int idx) const {\n  if (ConfigItem *obj = get(idx))\n    return obj->as<Zone>();\n  return nullptr;\n}\n\nint\nZoneList::add(ConfigObject *obj, int row, bool unique) {\n  if (obj && obj->is<Zone>())\n    return ConfigObjectList::add(obj, row, unique);\n  return -1;\n}\n\nConfigItem *\nZoneList::allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err) {\n  Q_UNUSED(ctx)\n  if (! node)\n    return nullptr;\n\n  if (! node.IsMap()) {\n    errMsg(err) << node.Mark().line << \":\" << node.Mark().column\n                << \": Cannot create zone: Expected object.\";\n    return nullptr;\n  }\n\n  return new Zone();\n}\n"
  },
  {
    "path": "lib/zone.hh",
    "content": "#ifndef ZONE_HH\n#define ZONE_HH\n\n#include <QVector>\n#include \"configobject.hh\"\n#include \"configreference.hh\"\n\n#include \"anytone_extension.hh\"\n\nclass Config;\n\n/** Represents a zone within the generic configuration.\n * @ingroup conf */\nclass Zone : public ConfigObject\n{\n  Q_OBJECT\n  Q_CLASSINFO(\"IdPrefix\", \"zone\")\n\n  /** The A channels. */\n  Q_PROPERTY(ChannelRefList* A READ A)\n  /** The B channels. */\n  Q_PROPERTY(ChannelRefList* B READ B)\n\n  /** The AnyTone extensions. */\n  Q_PROPERTY(AnytoneZoneExtension* anytone READ anytoneExtension WRITE setAnytoneExtension)\n\npublic:\n  /** Default constructor. */\n  Q_INVOKABLE explicit Zone(QObject *parent=nullptr);\n  /** Constructs an empty Zone with the given name. */\n  Zone(const QString &name, QObject *parent = nullptr);\n\n  /** Copies the given zone. */\n  Zone &operator =(const Zone &other);\n\n  ConfigItem *clone() const;\n\n  /** Clears this zone. */\n  void clear();\n\n  /** Returns the list of channels for VFO A in this zone. */\n  const ChannelRefList *A() const;\n  /** Returns the list of channels for VFO A in this zone. */\n  ChannelRefList* A();\n  /** Returns the list of channels for VFO B in this zone. */\n  const ChannelRefList *B() const;\n  /** Returns the list of channels for VFO B in this zone. */\n  ChannelRefList* B();\n\n  /** Returns @c true, if the zone contains the given channel. */\n  bool contains(Channel *obj) const;\n\n  /** Returns the AnyTone extension. */\n  AnytoneZoneExtension *anytoneExtension() const;\n  /** Sets the AnyTone extension. */\n  void setAnytoneExtension(AnytoneZoneExtension *ext);\n\nsignals:\n  /** Gets emitted whenever the zone gets modified. */\n\tvoid modified();\n\nprotected:\n  /** List of channels for VFO A. */\n  ChannelRefList _A;\n  /** List of channels for VFO B. */\n  ChannelRefList _B;\n  /** Owns the AnyTone extensions. */\n  AnytoneZoneExtension *_anytone;\n};\n\n\n/** Represents the list of zones within the generic configuration.\n * @ingroup conf */\nclass ZoneList : public ConfigObjectList\n{\n\tQ_OBJECT\n\npublic:\n  /** Constructs an empty list of zones. */\n\texplicit ZoneList(QObject *parent = nullptr);\n\n  /** Returns the zone at the given index. */\n\tZone *zone(int idx) const;\n\n  int add(ConfigObject *obj, int row=-1, bool unique=true);\n\npublic:\n  ConfigItem *allocateChild(const YAML::Node &node, ConfigItem::Context &ctx, const ErrorStack &err=ErrorStack());\n};\n\n\n#endif // ZONE_HH\n"
  },
  {
    "path": "shared/icons/dark/index.theme",
    "content": "[Icon Theme]\nName = \"Light Theme\"\nComment = \"Default light icon theme.\"\nDirectories = 16x16/emblems 16x16/actions, 16x16/apps, 32x32/emblems 32x32/actions, 32x32/apps 48x48/apps, 64x64/apps, scalable/emblems scalable/actions, scalable/apps\n\n[16x16/emblems]\nSize=16\nContext=Emblems\n\n[16x16/actions]\nSize=16\nContext=Actions\n\n[16x16/apps]\nSize=16\nContext=Applications\n\n[32x32/emblems]\nSize=32\nContext=Emblems\n\n[32x32/actions]\nSize=32\nContext=Actions\n\n[32x32/apps]\nSize=32\nContext=Applications\n\n[48x48/apps]\nSize=46\nContext=Applications\n\n[64x64/apps]\nSize=64\nContext=Applications\n\n[scalable/emblems]\nSize=8\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Emblems\n\n[scalable/actions]\nSize=8\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Actions\n\n[scalable/apps]\nSize=128\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Applications\n"
  },
  {
    "path": "shared/icons/light/index.theme",
    "content": "[Icon Theme]\nName = \"Light Theme\"\nComment = \"Default light icon theme.\"\nDirectories = 16x16/emblems 16x16/actions, 16x16/apps, 32x32/emblems 32x32/actions, 32x32/apps 48x48/apps, 64x64/apps, scalable/emblems scalable/actions, scalable/apps\n\n[16x16/emblems]\nSize=16\nContext=Emblems\n\n[16x16/actions]\nSize=16\nContext=Actions\n\n[16x16/apps]\nSize=16\nContext=Applications\n\n[32x32/emblems]\nSize=32\nContext=Emblems\n\n[32x32/actions]\nSize=32\nContext=Actions\n\n[32x32/apps]\nSize=32\nContext=Applications\n\n[48x48/apps]\nSize=46\nContext=Applications\n\n[64x64/apps]\nSize=64\nContext=Applications\n\n[scalable/emblems]\nSize=8\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Emblems\n\n[scalable/actions]\nSize=8\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Actions\n\n[scalable/apps]\nSize=128\nType=Scalable\nMinSize=1\nMaxSize=1024\nContext=Applications\n"
  },
  {
    "path": "shared/resources.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/icons\">\n        <file alias=\"splash.png\">logo256.png</file>\n    </qresource>\n    <qresource prefix=\"/ui\">\n        <file alias=\"aboutdialog.ui\">ui/aboutdialog.ui</file>\n    </qresource>\n    <qresource prefix=\"/icons/aprs\">\n        <file alias=\"table0.png\">aprs/aprs-symbols-24-0.png</file>\n        <file alias=\"table1.png\">aprs/aprs-symbols-24-1.png</file>\n        <file alias=\"table2.png\">aprs/aprs-symbols-24-2.png</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "shared/ui/aboutdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AboutDialog</class>\n <widget class=\"QDialog\" name=\"AboutDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>540</width>\n    <height>371</height>\n   </rect>\n  </property>\n  <property name=\"focusPolicy\">\n   <enum>Qt::NoFocus</enum>\n  </property>\n  <property name=\"windowTitle\">\n   <string>About qdmr</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset>\n    <normaloff>:/icons/about.png</normaloff>:/icons/about.png</iconset>\n  </property>\n  <property name=\"modal\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"aboutTab\">\n      <attribute name=\"title\">\n       <string>About qdmr</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QTextEdit\" name=\"textEdit\">\n         <property name=\"documentTitle\">\n          <string>About qdmr</string>\n         </property>\n         <property name=\"undoRedoEnabled\">\n          <bool>false</bool>\n         </property>\n         <property name=\"html\">\n          <string notr=\"true\">&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;\n&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;title&gt;About qdmr&lt;/title&gt;&lt;style type=&quot;text/css&quot;&gt;\np, li { white-space: pre-wrap; }\nhr { height: 1px; border-width: 0; }\nli.unchecked::marker { content: &quot;\\2610&quot;; }\nli.checked::marker { content: &quot;\\2612&quot;; }\n&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;\n&lt;p align=&quot;center&quot; style=&quot; margin-top:18px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt; font-weight:600;&quot;&gt;qdmr&lt;/span&gt;&lt;/p&gt;\n&lt;p align=&quot;center&quot; style=&quot; margin-top:14px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt; font-weight:600;&quot;&gt;Version %1&lt;/span&gt;&lt;/p&gt;\n&lt;p align=&quot;center&quot; style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt; font-weight:600;&quot;&gt;Hannes Matuschek, DM3MAT&lt;br /&gt; dm3mat@darc.de&lt;/span&gt;&lt;/p&gt;\n&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt; font-weight:600;&quot;&gt;qdmr&lt;/span&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt; – A platform independent configuration and programming tool for codeplugs of cheap DMR radios.&lt;/span&gt;&lt;/p&gt;\n&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt;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.&lt;/span&gt;&lt;/p&gt;\n&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt;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.&lt;/span&gt;&lt;/p&gt;\n&lt;p style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt;You should have received a copy of the GNU General Public License along with this program.  If not, see &lt;/span&gt;&lt;a href=&quot;https://www.gnu.org/licenses/&quot;&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt; text-decoration: underline; color:#0000ff;&quot;&gt;https://www.gnu.org/licenses/&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-family:'.SF NS Text'; font-size:13pt;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"textInteractionFlags\">\n          <set>Qt::NoTextInteraction</set>\n         </property>\n         <property name=\"placeholderText\">\n          <string notr=\"true\"/>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"tab_2\">\n      <attribute name=\"title\">\n       <string>Supported Radios</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <property name=\"leftMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"topMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"rightMargin\">\n        <number>0</number>\n       </property>\n       <property name=\"bottomMargin\">\n        <number>0</number>\n       </property>\n       <item>\n        <widget class=\"QTreeWidget\" name=\"radioTable\">\n         <property name=\"font\">\n          <font>\n           <pointsize>11</pointsize>\n          </font>\n         </property>\n         <attribute name=\"headerVisible\">\n          <bool>false</bool>\n         </attribute>\n         <column>\n          <property name=\"text\">\n           <string notr=\"true\">1</string>\n          </property>\n         </column>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n     <property name=\"centerButtons\">\n      <bool>false</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>AboutDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>AboutDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "qt6_add_resources(qdmr_RCC_SOURCES ../shared/resources.qrc)\n\nqt6_add_executable(qdmr WIN32\n  main.cc\n  configitemwrapper.cc configitemwrapper.hh\n  application.cc application.hh\n  settings.cc settings.hh settingsdialog.ui\n  dmrcontactdialog.cc dmrcontactdialog.hh dmrcontactdialog.ui\n  dtmfcontactdialog.cc dtmfcontactdialog.hh dtmfcontactdialog.ui\n  m17contactdialog.cc m17contactdialog.hh m17contactdialog.ui\n  rxgrouplistdialog.cc rxgrouplistdialog.hh rxgrouplistdialog.ui\n  channeldialog.hh channeldialog.cc channeldialog.ui\n  squelchedit.hh squelchedit.cc squelchedit.ui\n  fmchanneldialog.cc fmchanneldialog.hh\n  amchanneldialog.hh amchanneldialog.cc\n  dmrchanneldialog.cc dmrchanneldialog.hh\n  m17channeldialog.cc m17channeldialog.hh\n  channelvalidator.cc channelvalidator.hh\n  channelcombobox.cc channelcombobox.hh\n  channelselectiondialog.cc channelselectiondialog.hh\n  zonedialog.cc zonedialog.hh zonedialog.ui\n  scanlistdialog.cc scanlistdialog.hh scanlistdialog.ui\n  verifydialog.cc verifydialog.hh verifydialog.ui\n  gpssystemdialog.cc gpssystemdialog.hh gpssystemdialog.ui\n  contactselectiondialog.cc contactselectiondialog.hh\n  searchpopup.cc searchpopup.hh\n  aprssystemdialog.cc aprssystemdialog.hh aprssystemdialog.ui\n  releasenotes.cc releasenotes.hh\n  roamingzonedialog.cc roamingzonedialog.hh roamingzonedialog.ui\n  roamingchannellistview.cc roamingchannellistview.hh roamingchannellistview.ui\n  roamingchanneldialog.cc roamingchanneldialog.hh roamingchanneldialog.ui\n  roamingchannelselectiondialog.cc roamingchannelselectiondialog.hh\n  configobjectlistview.cc configobjectlistview.hh configobjectlistview.ui\n  configobjecttableview.cc configobjecttableview.hh configobjecttableview.ui\n  generalsettingsview.cc generalsettingsview.hh generalsettingsview.ui\n  radioidlistview.cc radioidlistview.hh radioidlistview.ui\n  contactlistview.cc contactlistview.hh contactlistview.ui\n  grouplistsview.cc grouplistsview.hh grouplistsview.ui\n  channellistview.cc channellistview.hh channellistview.ui\n  zonelistview.cc zonelistview.hh zonelistview.ui\n  scanlistsview.cc scanlistsview.hh scanlistsview.ui\n  positioningsystemlistview.cc positioningsystemlistview.hh positioningsystemlistview.ui\n  roamingzonelistview.cc roamingzonelistview.hh roamingzonelistview.ui\n  collapsablewidget.cc collapsablewidget.hh\n  extensionview.cc extensionview.hh extensionview.ui\n  extensionwrapper.cc extensionwrapper.hh\n  propertydelegate.cc propertydelegate.hh\n  errormessageview.cc errormessageview.hh errormessageview.ui\n  deviceselectiondialog.cc deviceselectiondialog.hh deviceselectiondialog.ui\n  radioselectiondialog.cc radioselectiondialog.hh radioselectiondialog.ui\n  dmriddialog.cc dmriddialog.hh dmriddialog.ui\n  configobjecttypeselectiondialog.cc configobjecttypeselectiondialog.hh configobjecttypeselectiondialog.ui\n  configmergedialog.cc configmergedialog.hh configmergedialog.ui\n  satellitedatabasedialog.cc satellitedatabasedialog.hh satellitedatabasedialog.ui\n  satelliteselectiondialog.cc satelliteselectiondialog.hh satelliteselectiondialog.ui\n  repeaterdatabase.cc repeaterdatabase.hh\n  repeatercompleter.cc repeatercompleter.hh\n  repeaterbooksource.cc repeaterbooksource.hh\n  repeatermapsource.cc repeatermapsource.hh\n  hearhamrepeatersource.cc hearhamrepeatersource.hh\n  radioidrepeatersource.cc radioidrepeatersource.hh\n  selectivecallbox.cc selectivecallbox.hh\n  transponderfrequencydelegate.cc transponderfrequencydelegate.hh\n  mainwindow.cc mainwindow.hh mainwindow.ui\n  flageditdialog.hh flageditdialog.cc flageditdialog.ui\n  ${qdmr_RCC_SOURCES}\n  satellitetransponderdialog.hh satellitetransponderdialog.cc satellitetransponderdialog.ui\n  admitselect.hh admitselect.cc\n  bandwidthselect.hh bandwidthselect.cc\n  aprsselect.hh aprsselect.cc\n  idselect.hh idselect.cc\n  timeslotselect.hh timeslotselect.cc\n  melody_edit.hh melody_edit.cc\n\t\tchannel_type_edit.cc\n\t\tchannel_type_edit.hh\n        melody_player.cc\n        melody_player.hh\n)\n\n#\n# Translations\n#\nqt6_add_translations(qdmr TS_FILES\n  ${PROJECT_SOURCE_DIR}/i18n/de.ts ${PROJECT_SOURCE_DIR}/i18n/en_US.ts\n  ${PROJECT_SOURCE_DIR}/i18n/sv.ts ${PROJECT_SOURCE_DIR}/i18n/it.ts\n  ${PROJECT_SOURCE_DIR}/i18n/nl.ts ${PROJECT_SOURCE_DIR}/i18n/pl.ts\n  ${PROJECT_SOURCE_DIR}/i18n/fr.ts ${PROJECT_SOURCE_DIR}/i18n/pt_BR.ts\n  ${PROJECT_SOURCE_DIR}/i18n/ru.ts)\n\n#\n# Icon generation\n#\ngenerate_icons(\n  DIRECTORY ${CMAKE_SOURCE_DIR}/shared/icons\n  ICONS application-qdmr\n  THEMES light dark\n  CONTEXT apps\n  SIZES 16 32 48 64)\n\ngenerate_icons(\n  DIRECTORY ${CMAKE_SOURCE_DIR}/shared/icons\n  ICONS application-exit application-settings\n        device-read device-write-callsign device-write device-search device-write-satellites\n        document-new document-open document-verify document-save-as document-download\n        document-import document-export\n        edit-move-10-down edit-move-10-up edit-move-down edit-move-up edit-move-bottom edit-move-top\n        edit-satellites edit-clear item-filter edit-edit\n\t\tmedia-play\n        help-contents help-about\n        symbol-minus symbol-plus symbol-none\n  THEMES light dark\n  CONTEXT actions\n  SIZES 16 32)\n\ngenerate_icons(\n  DIRECTORY ${CMAKE_SOURCE_DIR}/shared/icons\n  ICONS symbol-info symbol-warning symbol-error\n  THEMES light dark\n  CONTEXT emblems\n  SIZES 16 32)\n\nqt_add_resources(qdmr \"icons\"\n  PREFIX \"icons\"\n  BASE \"${CMAKE_CURRENT_BINARY_DIR}/icons\"\n  FILES ${GENERATE_ICONS_OUTPUT_FILES})\n\nqt_add_resources(qdmr \"icons-themes\"\n  PREFIX \"icons\"\n  BASE \"${CMAKE_SOURCE_DIR}/shared/icons\"\n  FILES \"${CMAKE_SOURCE_DIR}/shared/icons/light/index.theme\"\n        \"${CMAKE_SOURCE_DIR}/shared/icons/dark/index.theme\")\n\n\nif (UNIX AND APPLE AND INSTALL_BUNDLE)\n\tset_target_properties(qdmr PROPERTIES\n\t\tMACOSX_BUNDLE ON\n\t\tMACOSX_BUNDLE_BUNDLE_NAME ${CMAKE_PROJECT_NAME}\n\t\tMACOSX_BUNDLE_BUNDLE_VERSION ${CMAKE_PROJECT_VERSION}\n\t\tMACOSX_BUNDLE_GUI_IDENTIFIER \"de.darc.dm3mat.qdmr\"\n\t\tMACOSX_BUNDLE_ICON_FILE \"Resources/qdmr.icns\"\n\t\tMACOSX_BUNDLE_LONG_VERSION_STRING ${CMAKE_PROJECT_VERSION}\n\t\tMACOSX_BUNDLE_SHORT_VERSION_STRING \"${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}\"\n    RESOURCE \"${CMAKE_SOURCE_DIR}/dist/macosx/qdmr.icns\" ${qdmr_QM_FILES})\nendif(UNIX AND APPLE AND INSTALL_BUNDLE)\n\ntarget_link_libraries(qdmr PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::Positioning Qt6::UiTools\n  Qt6::Concurrent Qt6::Multimedia ${LIBUSB_1_LIBS} libdmrconf)\ntarget_include_directories(qdmr PUBLIC \"${CMAKE_CURRENT_BINARY_DIR}\")\n\n# Install binary only if not a bundle under MacOS X\nif (UNIX AND APPLE AND INSTALL_BUNDLE)\n  install(TARGETS qdmr\n    BUNDLE DESTINATION ${BUNDLE_PATH}/\n    RESOURCE DESTINATION \"${BUNDLE_PATH}/${CMAKE_PROJECT_NAME}.app/Contents/Resources\")\nelse()\n  install(TARGETS qdmr DESTINATION ${CMAKE_INSTALL_FULL_BINDIR})\nendif(UNIX AND APPLE AND INSTALL_BUNDLE)\n"
  },
  {
    "path": "src/aboutdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>AboutDialog</class>\n <widget class=\"QDialog\" name=\"AboutDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>About qdmr</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTextEdit\" name=\"textEdit\"/>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n     <property name=\"centerButtons\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>AboutDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>AboutDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/admitselect.cc",
    "content": "#include \"admitselect.hh\"\n\n/* ****************************************************************************************** *\n * FMAdmitSelect\n * ****************************************************************************************** */\nFMAdmitSelect::FMAdmitSelect(QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"Always\"), QVariant::fromValue(FMChannel::Admit::Always));\n  addItem(tr(\"Channel Free\"), QVariant::fromValue(FMChannel::Admit::Free));\n  addItem(tr(\"Other Tone\"), QVariant::fromValue(FMChannel::Admit::Tone));\n}\n\nvoid\nFMAdmitSelect::setAdmit(FMChannel::Admit admit) {\n  for (int i=0; i<count(); i++)\n    if (itemData(i).value<FMChannel::Admit>() == admit)\n      setCurrentIndex(i);\n}\n\n\nFMChannel::Admit\nFMAdmitSelect::admit() const {\n  return currentData().value<FMChannel::Admit>();\n}\n\n\n\n/* ****************************************************************************************** *\n * DMRAdmitSelect\n * ****************************************************************************************** */\nDMRAdmitSelect::DMRAdmitSelect(QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"Always\"), QVariant::fromValue(DMRChannel::Admit::Always));\n  addItem(tr(\"Channel Free\"), QVariant::fromValue(DMRChannel::Admit::Free));\n  addItem(tr(\"Other Color-code\"), QVariant::fromValue(DMRChannel::Admit::ColorCode));\n}\n\nvoid\nDMRAdmitSelect::setAdmit(DMRChannel::Admit admit) {\n  for (int i=0; i<count(); i++)\n    if (itemData(i).value<DMRChannel::Admit>() == admit)\n      setCurrentIndex(i);\n}\n\n\nDMRChannel::Admit\nDMRAdmitSelect::admit() const {\n  return currentData().value<DMRChannel::Admit>();\n}\n\n"
  },
  {
    "path": "src/admitselect.hh",
    "content": "#ifndef ADMITSELECT_HH\n#define ADMITSELECT_HH\n\n#include <QComboBox>\n#include \"channel.hh\"\n\n\nclass FMAdmitSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  explicit FMAdmitSelect(QWidget *parent=nullptr);\n\n  void setAdmit(FMChannel::Admit admit);\n  FMChannel::Admit admit() const;\n};\n\n\nclass DMRAdmitSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  explicit DMRAdmitSelect(QWidget *parent=nullptr);\n\n  void setAdmit(DMRChannel::Admit admit);\n  DMRChannel::Admit admit() const;\n};\n\n\n#endif // ADMITSELECT_HH\n"
  },
  {
    "path": "src/amchanneldialog.cc",
    "content": "#include \"amchanneldialog.hh\"\n#include \"channel.hh\"\n#include \"ui_channeldialog.h\"\n#include \"squelchedit.hh\"\n#include \"config.hh\"\n\n\nAMChannelDialog::AMChannelDialog(Config *config, QWidget *parent)\n  : ChannelDialog(config, parent), _clone(nullptr), _orig(nullptr), _squelch(nullptr)\n{\n  ui->rightForm->addRow(tr(\"Squelch\"),\n    _squelch = new ChannelSquelchEdit(_config->settings()->audio()->squelch()));\n}\n\nvoid\nAMChannelDialog::setChannel(AMChannel *am) {\n  if (_clone) {\n    delete _clone;\n    _clone = nullptr;\n  }\n\n  _orig = am;\n  if (_orig.isNull())\n    return;\n\n  _clone = _orig->clone()->as<AMChannel>();\n  _clone->setParent(this);\n  ChannelDialog::setChannel(_clone);\n\n  _squelch->setChannel(_clone);\n}\n\nvoid\nAMChannelDialog::accept() {\n  _squelch->accept();\n\n  ChannelDialog::accept();\n\n  _orig->copy(*_clone);\n}\n\n"
  },
  {
    "path": "src/amchanneldialog.hh",
    "content": "#ifndef AMCHANNELDIALOG_HH\n#define AMCHANNELDIALOG_HH\n\n#include \"channeldialog.hh\"\n\nclass Config;\nclass AMChannel;\nclass ChannelSquelchEdit;\n\n\nclass AMChannelDialog : public ChannelDialog\n{\n  Q_OBJECT\n\npublic:\n  AMChannelDialog(Config *config, QWidget *parent=nullptr);\n\n  void setChannel(AMChannel *am);\n\npublic slots:\n  void accept() override;\n\nprotected:\n  AMChannel *_clone;\n  QPointer<AMChannel> _orig;\n  ChannelSquelchEdit *_squelch;\n};\n\n#endif // AMCHANNELDIALOG_HH\n"
  },
  {
    "path": "src/application.cc",
    "content": "#include \"application.hh\"\n#include <QMainWindow>\n#include <QtUiTools>\n#include <QDesktopServices>\n#include <QTranslator>\n#include <QStandardPaths>\n\n#include \"logger.hh\"\n#include \"radio.hh\"\n#include \"codeplug.hh\"\n#include \"config.h\"\n#include \"settings.hh\"\n#include \"radiolimits.hh\"\n#include \"verifydialog.hh\"\n#include \"rxgrouplistdialog.hh\"\n#include \"zonedialog.hh\"\n#include \"scanlistdialog.hh\"\n#include \"roamingzonedialog.hh\"\n#include \"repeaterdatabase.hh\"\n#include \"repeaterbooksource.hh\"\n#include \"repeatermapsource.hh\"\n#include \"hearhamrepeatersource.hh\"\n#include \"radioidrepeatersource.hh\"\n#include \"userdatabase.hh\"\n#include \"talkgroupdatabase.hh\"\n#include \"satellitedatabase.hh\"\n#include \"generalsettingsview.hh\"\n#include \"radioidlistview.hh\"\n#include \"contactlistview.hh\"\n#include \"grouplistsview.hh\"\n#include \"channellistview.hh\"\n#include \"zonelistview.hh\"\n#include \"scanlistsview.hh\"\n#include \"positioningsystemlistview.hh\"\n#include \"roamingchannellistview.hh\"\n#include \"roamingzonelistview.hh\"\n#include \"errormessageview.hh\"\n#include \"deviceselectiondialog.hh\"\n#include \"radioselectiondialog.hh\"\n#include \"chirpformat.hh\"\n#include \"configmergedialog.hh\"\n#include \"configmergevisitor.hh\"\n#include \"satellitedatabasedialog.hh\"\n#include \"mainwindow.hh\"\n\n\n\ninline QStringList getLanguages() {\n  QStringList languages = {QLocale::system().name()};\n  if (languages.last().contains(\"_\")) {\n    languages.append(languages.last().split(\"_\").first());\n  }\n  return languages;\n}\n\nApplication::Application(int &argc, char *argv[])\n  : QApplication(argc, argv), _config(nullptr), _mainWindow(nullptr), _translator(nullptr),\n    _repeater(nullptr), _satellites(nullptr), _lastDevice()\n{\n  setApplicationName(\"qdmr\");\n  setOrganizationName(\"DM3MAT\");\n  setOrganizationDomain(\"hmatuschek.github.io\");\n  setApplicationVersion(VERSION_STRING);\n\n  // open logfile\n  QString logdir = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);\n  // Log all messages to file\n  Logger::get().addHandler(new FileLogHandler(logdir+\"/qdmr.log\", LogMessage::Level::DEBUG));\n\n  // register icon themes\n  QStringList iconPaths = QIcon::themeSearchPaths();\n  iconPaths.prepend(\":/icons\");\n  QIcon::setThemeSearchPaths(iconPaths);\n  onPaletteChanged(palette());\n\n  // handle translations\n  _translator = new QTranslator(this);\n  foreach (QString language, getLanguages()) {\n    logDebug() << \"Search for translation in ':/i18n/\" << language << \".qm'.\";\n    if (_translator->load(language, \":/i18n/\", \"\", \".qm\")) {\n      this->installTranslator(_translator);\n      logDebug() << \"Installed translator for locale '\" << QLocale::system().name() << \"'.\";\n      break;\n    }\n  }\n\n  // load settings\n  Settings settings;\n  // load databases\n  _repeater   = new RepeaterDatabase(this);\n  if (settings.repeaterBookSourceEnabled())\n    _repeater->addSource(new RepeaterBookSource());\n  if (settings.repeaterMapSourceEnabled() && !settings.repeaterMapAPIToken().isEmpty())\n    _repeater->addSource(new RepeaterMapSource(settings.repeaterMapAPIToken(),\n      settings.position(), settings.repeaterSearchRadius()));\n  if (settings.hearhamSourceEnabled())\n    _repeater->addSource(new HearhamRepeaterSource());\n  if (settings.radioIdRepeaterSourceEnabled())\n    _repeater->addSource(new RadioidRepeaterSource());\n  _users      = new UserDatabase(true, 30, this);\n  _talkgroups = new TalkGroupDatabase(30, this);\n  _satellites = new SatelliteDatabase(7, this);\n\n  // create empty codeplug\n  _config     = new Config(this);\n\n  // load position\n  _currentPosition = settings.position();\n  _source = QGeoPositionInfoSource::createDefaultSource(this);\n  if (_source) {    \n    connect(_source, SIGNAL(positionUpdated(QGeoPositionInfo)),\n            this, SLOT(positionUpdated(QGeoPositionInfo)));\n    if (settings.queryPosition()) {\n      _source->startUpdates();\n      _currentPosition = _source->lastKnownPosition().coordinate();\n    }\n  }\n\n  // Check if updated\n  _releaseNotes.checkForUpdate();\n\n  logDebug() << \"Last known position: \" << _currentPosition.toString();\n  connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModifed()));\n}\n\nApplication::~Application() {\n  if (_mainWindow)\n    delete _mainWindow;\n  _mainWindow = nullptr;\n}\n\nbool\nApplication::isDarkMode() const {\n  return isDarkMode(palette());\n}\n\nbool\nApplication::isDarkMode(const QPalette &palette) const {\n  int text_hsv_value = palette.color(QPalette::WindowText).value(),\n      bg_hsv_value = palette.color(QPalette::Base).value();\n  return text_hsv_value > bg_hsv_value;\n}\n\n\nbool\nApplication::isModified() const {\n  return _config->isModified();\n}\n\nMainWindow *\nApplication::mainWindow() {\n  if (_mainWindow)\n    return _mainWindow;\n  return (_mainWindow = new MainWindow(_config));\n}\n\nUserDatabase *\nApplication::user() const {\n  return _users;\n}\n\nTalkGroupDatabase *\nApplication::talkgroup() const {\n  return _talkgroups;\n}\n\nRepeaterDatabase *\nApplication::repeater() const{\n  return _repeater;\n}\n\nSatelliteDatabase *\nApplication::satellite() const {\n  return _satellites;\n}\n\n\nvoid\nApplication::newCodeplug() {\n  if (_config->isModified()) {\n    if (QMessageBox::Ok != QMessageBox::question(0, tr(\"Unsaved changes to codeplug.\"),\n                                                 tr(\"There are unsaved changes to the current codeplug. \"\n                                                    \"These changes are lost if you proceed.\"),\n                                                 QMessageBox::Cancel|QMessageBox::Ok))\n      return;\n  }\n\n  _config->clear();\n  _config->setModified(false);\n}\n\n\nvoid\nApplication::loadCodeplug() {\n  if (! _mainWindow)\n    return;\n\n  if (_config->isModified()) {\n    if (QMessageBox::Ok != QMessageBox::question(nullptr, tr(\"Unsaved changes to codeplug.\"),\n                                                 tr(\"There are unsaved changes to the current codeplug. \"\n                                                    \"These changes are lost if you proceed.\"),\n                                                 QMessageBox::Cancel|QMessageBox::Ok))\n      return;\n  }\n\n  Settings settings;\n  QString filename = QFileDialog::getOpenFileName(\n        nullptr, tr(\"Open codeplug\"),\n        settings.lastDirectory().absolutePath(),\n        tr(\"Codeplug Files (*.yaml);;Codeplug Files, old format (*.conf *.csv *.txt);;All Files (*)\"));\n  if (filename.isEmpty())\n    return;\n\n\n  ErrorStack err;\n  if (! loadCodeplug(filename, err)) {\n    QMessageBox::critical(nullptr, tr(\"Cannot read codeplug.\"),\n                          tr(\"Cannot read codeplug from file '%1': %2\")\n                              .arg(filename).arg(err.format()));\n    return;\n  }\n\n  processEvents();\n  _config->setModified(false);\n}\n\n\nbool\nApplication::loadCodeplug(const QString &filename, const ErrorStack &err) {\n  QFile file(filename);\n  if (!file.open(QIODevice::ReadOnly)) {\n    errMsg(err) << tr(\"Cannot read codeplug from file '%1': %2\").arg(filename).arg(file.errorString());\n    return false;;\n  }\n\n  logDebug() << \"Load codeplug from '\" << filename << \"'.\";\n\n  QFileInfo info(filename);\n  Settings().setLastDirectoryDir(info.absoluteDir());\n\n  if ((\"yaml\" == info.suffix()) || (\"yml\" == info.suffix())) {\n    ErrorStack err;\n    if (! _config->readYAML(filename, err)) {\n      _config->clear();\n      return false;\n    }\n  } else {\n    QString errorMessage;\n    QTextStream stream(&file);\n    if (!_config->readCSV(stream, errorMessage)) {\n      errMsg(err) << tr(\"Cannot read codeplug from file '%1': %2\").arg(filename).arg(errorMessage);\n      _config->clear();\n      return false;\n    }\n  }\n\n  return true;\n}\n\n\nvoid\nApplication::saveCodeplug() {\n  if (! _mainWindow)\n    return;\n\n  Settings settings;\n  QString filename = QFileDialog::getSaveFileName(\n        nullptr, tr(\"Save codeplug\"), settings.lastDirectory().absolutePath(),\n        tr(\"Codeplug Files (*.yaml *.yml)\"));\n\n  if (filename.isEmpty())\n    return;\n\n  if (filename.endsWith(\".conf\") || filename.endsWith(\"csv\")){\n    QMessageBox::critical(nullptr, tr(\"Please use new YAML format.\"),\n                          tr(\"Saving in the old table-based conf format was disabled with 0.9.0. \"\n                             \"Reading these files still works.\"));\n    return;\n  }\n\n  // append .yaml if missing\n  if ((!filename.endsWith(\".yaml\")) && (!filename.endsWith(\".yml\")))\n    filename.append(\".yaml\");\n\n  QFile file(filename);\n  if (! file.open(QIODevice::WriteOnly)) {\n    QMessageBox::critical(nullptr, tr(\"Cannot open file\"),\n                          tr(\"Cannot save codeplug to file '%1': %2\").arg(filename).arg(file.errorString()));\n    return;\n  }\n\n  QTextStream stream(&file);\n  QFileInfo info(filename);\n  if (_config->toYAML(stream)) {\n    _config->setModified(false);\n  } else {\n    QMessageBox::critical(nullptr, tr(\"Cannot save codeplug\"),\n                          tr(\"Cannot save codeplug to file '%1'.\").arg(filename));\n  }\n\n  file.flush();\n  file.close();\n\n  settings.setLastDirectoryDir(info.absoluteDir());\n}\n\n\nvoid\nApplication::exportCodeplugToChirp() {\n  if (! _mainWindow)\n    return;\n\n  Settings settings;\n  QString filename = QFileDialog::getSaveFileName(\n        nullptr, tr(\"Export codeplug\"), settings.lastDirectory().absolutePath(),\n        tr(\"CHIRP CSV Files (*.csv)\"));\n\n  if (filename.isEmpty())\n    return;\n\n\n  QFile file(filename);\n  if (! file.open(QIODevice::WriteOnly)) {\n    QMessageBox::critical(nullptr, tr(\"Cannot open file\"),\n                          tr(\"Cannot save codeplug to file '%1': %2\").arg(filename).arg(file.errorString()));\n    return;\n  }\n\n  QTextStream stream(&file);\n  QFileInfo info(filename);\n  ErrorStack err;\n  if (! ChirpWriter::write(stream, _config, err))\n    QMessageBox::critical(nullptr, tr(\"Cannot export codeplug\"),\n                          tr(\"Cannot export codeplug to file '%1':\\n%2\").arg(filename).arg(err.format()));\n\n  file.flush();\n  file.close();\n\n  settings.setLastDirectoryDir(info.absoluteDir());\n}\n\n\nvoid\nApplication::importCodeplug() {\n  if (! _mainWindow)\n    return;\n\n  Settings settings;\n  QString filename = QFileDialog::getOpenFileName(\n        nullptr, tr(\"Import codeplug\"), settings.lastDirectory().absolutePath(),\n        tr(\"CHIRP CSV Files (*.csv);;YAML Files (*.yaml *.yml)\"));\n\n  if (filename.isEmpty())\n    return;\n\n  Config merging;\n  ErrorStack err;\n\n  if (filename.endsWith(\".csv\")) {\n    // import CHIRP CSV file\n    QFile file(filename);\n    if (! file.open(QIODevice::ReadOnly)) {\n      QMessageBox::critical(nullptr, tr(\"Cannot open file\"),\n                            tr(\"Cannot read codeplug from file '%1': %2\").arg(filename).arg(file.errorString()));\n      return;\n    }\n\n    QTextStream stream(&file);\n    if (! ChirpReader::read(stream, &merging, err)) {\n      QMessageBox::critical(nullptr, tr(\"Cannot import codeplug\"),\n                            tr(\"Cannot import codeplug from '%1': %2\")\n                            .arg(filename).arg(err.format()));\n      return;\n    }\n  } else if (filename.endsWith(\".yaml\") || filename.endsWith(\".yml\")) {\n    // import QDMR YAML codeplug\n    if (! merging.readYAML(filename, err)) {\n      QMessageBox::critical(nullptr, tr(\"Cannot import codeplug\"),\n                            tr(\"Cannot import codeplug from '%1': %2\")\n                            .arg(filename).arg(err.format()));\n      return;\n    }\n  } else {\n    QMessageBox::critical(nullptr, tr(\"Cannot import codeplug\"),\n                          tr(\"Do not know, how to handle file '%1'.\")\n                          .arg(filename));\n    return;\n  }\n\n  ConfigMergeDialog mergeDialog;\n  if (QDialog::Accepted != mergeDialog.exec())\n    return;\n\n  logDebug() << \"Merging codeplugs ...\";\n  if (! ConfigMerge::mergeInto(_config, &merging, mergeDialog.itemStrategy(),\n                               mergeDialog.setStrategy(), err)) {\n    QMessageBox::critical(nullptr, tr(\"Cannot import codeplug\"),\n                          tr(\"Cannot import codeplug from '%1': %2\")\n                          .arg(filename).arg(err.format()));\n    return;\n  }\n}\n\n\nRadio *\nApplication::autoDetect(const ErrorStack &err) {\n  Settings settings;\n  // If the last detected device is still valid\n  //  -> skip interface detection and selection\n  if ((! _lastDevice.isValid()) || settings.disableAutoDetect()) {\n    logDebug() << \"Last device is invalid, search for new one.\";\n    // First get all devices that are known by VID/PID\n    QList<USBDeviceDescriptor> interfaces = USBDeviceDescriptor::detect();\n    if (interfaces.isEmpty()) {\n      logDebug() << \"No save device found, continue searching for unsave ones.\";\n      interfaces = USBDeviceDescriptor::detect(false);\n    }\n    if (interfaces.isEmpty()) {\n      errMsg(err) << tr(\"No matching devices found.\");\n      return nullptr;\n    } else if ((1 != interfaces.count()) || (! interfaces.first().isSave()) || settings.disableAutoDetect()) {\n      // More than one device found, or device not save -> select by user\n      DeviceSelectionDialog dialog(interfaces);\n      if (QDialog::Accepted != dialog.exec()) {\n        return nullptr;\n      }\n      _lastDevice = dialog.device();\n    } else {\n      _lastDevice = interfaces.first();\n    }\n  }\n\n  // Check if device supports identification\n  RadioInfo radioInfo;\n  if ((! _lastDevice.isSave()) || settings.disableAutoDetect()){\n    RadioSelectionDialog dialog(_lastDevice);\n    if (QDialog::Accepted != dialog.exec()) {\n      return nullptr;\n    }\n    radioInfo = dialog.radioInfo();\n  }\n\n  Radio *radio = Radio::detect(_lastDevice, radioInfo, err);\n  if (nullptr == radio) {\n    QMessageBox::critical(nullptr, tr(\"Cannot connect to radio\"),\n                          tr(\"Cannot connect to radio: %1\").arg(err.format()));\n    return nullptr;\n  }\n\n  return radio;\n}\n\nvoid\nApplication::detectRadio() {\n  if (Radio *radio = autoDetect()) {\n    QMessageBox::information(nullptr, tr(\"Radio found\"), tr(\"Found device '%1'.\").arg(radio->name()));\n    delete radio;\n  } else {\n    QMessageBox::information(nullptr, tr(\"No radio found\"),\n                             tr(\"No matching device was found.\"));\n  }\n}\n\n\nbool\nApplication::verifyCodeplug(Radio *radio, bool showSuccess) {\n  Radio *myRadio = radio;\n\n  // If no radio is given -> try to detect the radio\n  if (nullptr == myRadio)\n    myRadio = autoDetect();\n\n  if (nullptr == myRadio) {\n    if (showSuccess) {\n      QMessageBox::warning(nullptr, tr(\"No radio found\"),\n                           tr(\"No matching device was found.\"));\n    }\n    return false;\n  }\n\n  ErrorStack err;\n  Config *intermediate = myRadio->codeplug().preprocess(_config, err);\n  if (nullptr == intermediate) {\n    ErrorMessageView(err).exec();\n    return false;\n  }\n\n  Settings settings;\n  RadioLimitContext ctx(settings.ignoreFrequencyLimits());\n  myRadio->limits().verifyConfig(intermediate, ctx);\n\n  bool verified = true;\n  if ( (settings.ignoreVerificationWarning() && (ctx.maxSeverity()>RadioLimitIssue::Warning)) ||\n       ((!settings.ignoreVerificationWarning()) && (ctx.maxSeverity()>=RadioLimitIssue::Warning)) ) {\n    VerifyDialog dialog(ctx, (nullptr != radio));\n    if (QDialog::Accepted != dialog.exec())\n      verified = false;\n  } else if (showSuccess) {\n    QMessageBox::information(\n          nullptr, tr(\"Verification success\"),\n          tr(\"The codeplug was successfully verified with the radio '%1'\").arg(myRadio->name()));\n  }\n\n  // Delete intermediate representation\n  delete intermediate;\n\n  // If no radio was given -> close connection to radio again\n  if (nullptr == radio)\n    myRadio->deleteLater();\n\n  return verified;\n}\n\n\nvoid\nApplication::downloadCodeplug() {\n  if (! _mainWindow)\n    return;\n\n  if (_config->isModified()) {\n    if (QMessageBox::Ok != QMessageBox::question(nullptr, tr(\"Unsaved changes to codeplug.\"),\n                                                 tr(\"There are unsaved changes to the current codeplug. \"\n                                                    \"These changes are lost if you proceed.\"),\n                                                 QMessageBox::Cancel|QMessageBox::Ok))\n      return;\n  }\n\n  Radio *radio = autoDetect();\n  if (nullptr == radio) {\n    QMessageBox::warning(nullptr, tr(\"No radio found\"),\n                         tr(\"No matching device was found.\"));\n    return;\n  }\n\n  QProgressBar *progress = _mainWindow->findChild<QProgressBar *>(\"progress\");\n  progress->setMaximum(0); progress->setVisible(true);\n  connect(radio, &Radio::downloadProgress, this, &Application::onProgress);\n  connect(radio, SIGNAL(downloadError(Radio *)), this, SLOT(onCodeplugDownloadError(Radio *)));\n  connect(radio, SIGNAL(downloadFinished(Radio *, Codeplug *)), this, SLOT(onCodeplugDownloaded(Radio *, Codeplug *)));\n\n  TransferFlags flags; flags.setBlocking(false);\n\n  ErrorStack err;\n  if (radio->startDownload(flags, err)) {\n    _mainWindow->statusBar()->showMessage(tr(\"Read ...\"));\n    _mainWindow->setEnabled(false);\n  } else {\n    ErrorMessageView(err).exec();\n    progress->setVisible(false);\n  }\n}\n\nvoid\nApplication::onCodeplugDownloadError(Radio *radio) {\n  _mainWindow->statusBar()->showMessage(tr(\"Read error\"));\n  ErrorMessageView(radio->errorStack()).exec();\n  _mainWindow->findChild<QProgressBar *>(\"progress\")->setVisible(false);\n  _mainWindow->setEnabled(true);\n\n  if (radio->wait(250))\n    radio->deleteLater();\n\n  _mainWindow->setWindowModified(false);\n}\n\n\nvoid\nApplication::onCodeplugDownloaded(Radio *radio, Codeplug *codeplug) {\n  _config->clear();\n  _mainWindow->setWindowModified(false);\n\n  ErrorStack err;\n  if (codeplug->decode(_config, err)) {\n    _mainWindow->statusBar()->showMessage(tr(\"Read complete\"));\n    _mainWindow->findChild<QProgressBar *>(\"progress\")->setVisible(false);\n    _config->setModified(false);\n  } else {\n    ErrorMessageView(err).exec();\n    _config->clear();\n  }\n\n  if (! codeplug->postprocess(_config, err)) {\n    ErrorMessageView(err).exec();\n    _config->clear();\n  }\n\n  _mainWindow->setEnabled(true);\n\n  if (radio->wait(250))\n    radio->deleteLater();\n}\n\n\nvoid\nApplication::uploadCodeplug() {\n  // Start upload\n  Settings settings;\n\n  Radio *radio = autoDetect();\n  if (nullptr == radio) {\n    QMessageBox::warning(nullptr, tr(\"No radio found\"),\n                         tr(\"No matching device was found.\"));\n    return;\n  }\n\n  if (! verifyCodeplug(radio, false)) {\n    radio->deleteLater();\n    return;\n  }\n\n  QProgressBar *progress = _mainWindow->findChild<QProgressBar *>(\"progress\");\n  progress->setValue(0); progress->setMaximum(0); progress->setVisible(true);\n  connect(radio, &Radio::uploadProgress, this, &Application::onProgress);\n\n  connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *)));\n  connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *)));\n\n  ErrorStack err;\n  Config *intermediate = radio->codeplug().preprocess(_config, err);\n  if (nullptr == intermediate) {\n    ErrorMessageView(err).exec();\n    progress->setVisible(false);\n    return;\n  }\n\n  Codeplug::Flags flags = settings.codePlugFlags();\n  flags.setBlocking(false);\n\n  if (radio->startUpload(intermediate, flags, err)) {\n     _mainWindow->statusBar()->showMessage(tr(\"Upload ...\"));\n     _mainWindow->setEnabled(false);\n  } else {\n    ErrorMessageView(err).exec();\n    progress->setVisible(false);\n  }\n}\n\n\nvoid\nApplication::uploadCallsignDB() {\n  // Start upload\n  Radio *radio = autoDetect();\n  if (nullptr == radio) {\n    QMessageBox::warning(nullptr, tr(\"No radio found\"),\n                         tr(\"No matching device was found.\"));\n    return;\n  }\n\n  if (! radio->limits().hasCallSignDB()) {\n    logDebug() << \"Radio \" << radio->name() << \" does not support call-sign DB.\";\n    QMessageBox::information(nullptr, tr(\"Cannot write call-sign DB.\"),\n                             tr(\"The detected radio '%1' does not support \"\n                                \"a call-sign DB.\")\n                             .arg(radio->name()));\n    radio->deleteLater();\n    return;\n  }\n  if (! radio->limits().callSignDBImplemented()) {\n    logDebug() << \"Radio \" << radio->name()\n               << \" does support call-sign DB but it is not implemented yet.\";\n    QMessageBox::critical(nullptr, tr(\"Cannot write call-sign DB.\"),\n                          tr(\"The detected radio '%1' does support a call-sign DB. \"\n                             \"This feature, however, is not implemented yet.\").arg(radio->name()));\n    radio->deleteLater();\n    return;\n  }\n\n  // Sort call-sign DB w.r.t. the current DMR ID in _config\n  // this is part of the \"auto-selection\" of calls-signs for upload\n  Settings settings;\n  if (settings.selectUsingUserDMRID()) {\n    if (nullptr == _config->settings()->defaultId()) {\n      QMessageBox::critical(nullptr, tr(\"Cannot write call-sign DB.\"),\n                            tr(\"QDMR selects the call-signs to be written based on the default DMR \"\n                               \"ID of the radio. No default ID set.\"));\n      radio->deleteLater();\n      return;\n    }\n    // Sort w.r.t users DMR ID\n    unsigned id = _config->settings()->defaultId()->number();\n    logDebug() << \"Sort call-signs closest to ID=\" << id << \".\";\n    _users->sortUsers(id);\n  } else {\n    // sort w.r.t. chosen prefixes\n    QSet<unsigned> ids=settings.callSignDBPrefixes(); QStringList prefs;\n    foreach (unsigned pref, ids)\n      prefs.append(QString::number(pref));\n    logDebug() << \"Sort call-signs closest to IDs={\" << prefs.join(\", \") << \"}.\";\n    _users->sortUsers(ids);\n  }\n\n  // Assemble flags for callsign DB encoding\n  CallsignDB::Flags css;\n  css.setUpdateDeviceClock(settings.updateDeviceClock());\n  if (settings.limitCallSignDBEntries()) {\n    logDebug() << \"Limit callsign DB entries to \" << settings.maxCallSignDBEntries() << \".\";\n    css.setCountLimit(settings.maxCallSignDBEntries());\n  }\n\n  QProgressBar *progress = _mainWindow->findChild<QProgressBar *>(\"progress\");\n  progress->setRange(0, 0); progress->setValue(0); progress->setVisible(true);\n\n  connect(radio, &Radio::uploadProgress, this, &Application::onProgress);\n  connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *)));\n  connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *)));\n\n  ErrorStack err;\n  css.setBlocking(false);\n  if (radio->startUploadCallsignDB(_users, css, err)) {\n    logDebug() << \"Start call-sign DB write...\";\n    _mainWindow->statusBar()->showMessage(tr(\"Write call-sign DB ...\"));\n    _mainWindow->setEnabled(false);\n  } else {\n    ErrorMessageView(err).exec();\n    progress->setVisible(false);\n  }\n}\n\n\nvoid\nApplication::uploadSatellites() {\n  // Start upload satellites\n  Radio *radio = autoDetect();\n  if (nullptr == radio) {\n    QMessageBox::warning(nullptr, tr(\"No radio found\"),\n                         tr(\"No matching device was found.\"));\n    return;\n  }\n\n  if (! radio->limits().hasSatelliteConfig()) {\n    logDebug() << \"Radio \" << radio->name() << \" does not support satellite tracking.\";\n    QMessageBox::information(nullptr, tr(\"Cannot write satellite config.\"),\n                             tr(\"The detected radio '%1' does not support satellite tracking.\")\n                             .arg(radio->name()));\n    radio->deleteLater();\n    return;\n  }\n\n  if (! radio->limits().satelliteConfigImplemented()) {\n    logDebug() << \"Radio \" << radio->name()\n               << \" does support satellite tracking but it is not implemented yet.\";\n    QMessageBox::critical(nullptr, tr(\"Cannot write satellite config.\"),\n                          tr(\"The detected radio '%1' does support satellite tracking. \"\n                             \"This feature, however, is not implemented yet.\").arg(radio->name()));\n    radio->deleteLater();\n    return;\n  }\n\n  Settings settings;\n  TransferFlags flags;\n  flags.setUpdateDeviceClock(settings.updateDeviceClock());\n\n  QProgressBar *progress = _mainWindow->findChild<QProgressBar *>(\"progress\");\n  progress->setRange(0, 100); progress->setValue(0);\n  progress->setVisible(true);\n\n  connect(radio, SIGNAL(uploadProgress(int)), progress, SLOT(setValue(int)));\n  connect(radio, SIGNAL(uploadError(Radio *)), this, SLOT(onCodeplugUploadError(Radio *)));\n  connect(radio, SIGNAL(uploadComplete(Radio *)), this, SLOT(onCodeplugUploaded(Radio *)));\n\n  ErrorStack err;  \n  flags.setBlocking(false);\n  if (radio->startUploadSatelliteConfig(_satellites, flags, err)) {\n    logDebug() << \"Start satellite config write...\";\n    _mainWindow->statusBar()->showMessage(tr(\"Write satellite config ...\"));\n    _mainWindow->setEnabled(false);\n  } else {\n    ErrorMessageView(err).exec();\n    progress->setVisible(false);\n  }\n}\n\n\nvoid\nApplication::onProgress(int value) {\n  QProgressBar *progress = _mainWindow->findChild<QProgressBar *>(\"progress\");\n  if (value >= 0)\n    progress->setMaximum(100);\n  progress->setValue(value);\n}\n\nvoid\nApplication::onCodeplugUploadError(Radio *radio) {\n  _mainWindow->statusBar()->showMessage(tr(\"Write error\"));\n  ErrorMessageView(radio->errorStack()).exec();\n  _mainWindow->findChild<QProgressBar *>(\"progress\")->setVisible(false);\n  _mainWindow->setEnabled(true);\n\n  if (radio->wait(250))\n    radio->deleteLater();\n}\n\n\nvoid\nApplication::onCodeplugUploaded(Radio *radio) {\n  _mainWindow->statusBar()->showMessage(tr(\"Write complete\"));\n  _mainWindow->findChild<QProgressBar *>(\"progress\")->setVisible(false);\n  _mainWindow->setEnabled(true);\n\n  logDebug() << \"Write complete.\";\n\n  if (radio->wait(250))\n    radio->deleteLater();\n}\n\n\nvoid\nApplication::showSettings() {\n  SettingsDialog dialog;\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n\n  Settings settings;\n  // Handle positioning\n  if (! settings.queryPosition()) {\n    if (_source)\n      _source->stopUpdates();\n    setSearchRadius(settings.position(), settings.repeaterSearchRadius());\n  } else {\n    if (_source)\n      _source->startUpdates();\n    _searchRadius = settings.repeaterSearchRadius();\n  }\n}\n\n\nvoid\nApplication::showAbout() {\n\n  QUiLoader loader;\n  QFile uiFile(\"://ui/aboutdialog.ui\");\n  uiFile.open(QIODevice::ReadOnly);\n  auto obj = loader.load(&uiFile);\n  if (nullptr == obj) return;\n  QDialog *dialog = qobject_cast<QDialog *>(obj);\n  if (nullptr == dialog) return;\n\n  QTextEdit *text = dialog->findChild<QTextEdit *>(\"textEdit\");\n  text->setHtml(text->toHtml().arg(VERSION_STRING));\n\n  QTreeWidget *radioTab = dialog->findChild<QTreeWidget *>(\"radioTable\");\n  radioTab->setColumnCount(1);\n\n  QHash<QString, QTreeWidgetItem*> items;\n  foreach (RadioInfo radio, RadioInfo::allRadios(false)) {\n    if (! items.contains(radio.manufacturer()))\n      items.insert(radio.manufacturer(),\n                   new QTreeWidgetItem(QStringList(radio.manufacturer())));\n    items[radio.manufacturer()]->addChild(\n          new QTreeWidgetItem(QStringList(radio.name())));\n    foreach (RadioInfo alias, radio.alias()) {\n      if (! items.contains(alias.manufacturer()))\n        items.insert(alias.manufacturer(),\n                     new QTreeWidgetItem(QStringList(alias.manufacturer())));\n      items[alias.manufacturer()]->addChild(\n            new QTreeWidgetItem(QStringList(tr(\"%1 (alias for %2 %3)\").arg(alias.name())\n                                            .arg(radio.manufacturer()).arg(radio.name()))));\n    }\n  }\n  radioTab->insertTopLevelItems(0, items.values());\n  radioTab->sortByColumn(0, Qt::AscendingOrder);\n\n  connect(dialog, &QDialog::finished, dialog, &QObject::deleteLater);\n  dialog->open();\n}\n\nvoid\nApplication::showHelp() {\n  QDesktopServices::openUrl(QUrl(\"https://static.dm3mat.de/qdmr/manual\"));\n}\n\n\nvoid\nApplication::editSatellites() {\n  SatelliteDatabaseDialog dialog(_satellites);\n  if (QDialog::Accepted == dialog.exec()) {\n    _satellites->save();\n  } else {\n    _satellites->load();\n  }\n}\n\n\nvoid\nApplication::onConfigModifed() {\n  if (! _mainWindow)\n    return;\n\n  _mainWindow->setWindowModified(true);\n}\n\nvoid\nApplication::positionUpdated(const QGeoPositionInfo &info) {\n  if (info.isValid())\n    setSearchRadius(info.coordinate(), _searchRadius);\n}\n\nbool\nApplication::hasPosition() const {\n  return _currentPosition.isValid();\n}\n\nQGeoCoordinate\nApplication::position() const {\n  return _currentPosition;\n}\n\nvoid\nApplication::setSearchRadius(const QGeoCoordinate &position, unsigned int radius) {\n  QGeoCoordinate lastPosition = _currentPosition;\n  _currentPosition = position;\n  _searchRadius = radius;\n  if (_searchRadius && _currentPosition.isValid()\n    && (!lastPosition.isValid() || _currentPosition.distanceTo(lastPosition)>100*_searchRadius)) {\n    _repeater->setSearchRadius(_currentPosition, _searchRadius);\n  }\n}\n\nvoid\nApplication::onPaletteChanged(const QPalette &palette) {\n  // Set theme based on UI mode (light vs. dark).\n  if (isDarkMode(palette)) {\n    QIcon::setThemeName(\"dark\");\n    logDebug() << \"Set icon theme to 'dark'.\";\n  } else {\n    QIcon::setThemeName(\"light\");\n    logDebug() << \"Set icon theme to 'light'.\";\n  }\n}\n\n\n"
  },
  {
    "path": "src/application.hh",
    "content": "#ifndef APPLICATION_HH\n#define APPLICATION_HH\n\n#include <QApplication>\n#include <QIcon>\n#include \"config.hh\"\n#include <QGeoPositionInfoSource>\n#include \"releasenotes.hh\"\n#include \"radio.hh\"\n#include <QPointer>\n\nclass MainWindow;\nclass QTranslator;\nclass RepeaterDatabase;\nclass UserDatabase;\nclass TalkGroupDatabase;\nclass SatelliteDatabase;\nclass RadioIDListView;\nclass GeneralSettingsView;\nclass ContactListView;\nclass GroupListsView;\nclass ChannelListView;\nclass ZoneListView;\nclass ScanListsView;\nclass PositioningSystemListView;\nclass RoamingChannelListView;\nclass RoamingZoneListView;\nclass ExtensionView;\n\n\nclass Application : public QApplication\n{\n  Q_OBJECT\n\npublic:\n  Application(int &argc, char *argv[]);\n  virtual ~Application();\n\n  MainWindow *mainWindow();\n\n  bool isModified() const;\n\n  UserDatabase *user() const;\n  RepeaterDatabase *repeater() const;\n  TalkGroupDatabase *talkgroup() const;\n  SatelliteDatabase *satellite() const;\n\n  bool hasPosition() const;\n  QGeoCoordinate position() const;\n  unsigned int radius() const;\n  void setSearchRadius(const QGeoCoordinate &position, unsigned int radius);\n\n  Radio *autoDetect(const ErrorStack &err=ErrorStack());\n\n  bool isDarkMode() const;\n  bool isDarkMode(const QPalette &palette) const;\n\npublic slots:\n  void newCodeplug();\n  void loadCodeplug();\n  bool loadCodeplug(const QString &filename, const ErrorStack &err);\n  void saveCodeplug();\n  void exportCodeplugToChirp();\n  void importCodeplug();\n\n  void detectRadio();\n  bool verifyCodeplug(Radio *radio=nullptr, bool showSuccess=true);\n\n  void downloadCodeplug();\n  void uploadCodeplug();\n  void uploadCallsignDB();\n  void uploadSatellites();\n\n  void showSettings();\n  void showAbout();\n  void showHelp();\n\n  void editSatellites();\n\nprivate slots:\n  void onCodeplugDownloadError(Radio *radio);\n  void onCodeplugDownloaded(Radio *radio, Codeplug *codeplug);\n\n  void onProgress(int val);\n  void onCodeplugUploadError(Radio *radio);\n  void onCodeplugUploaded(Radio *radio);\n\n  void onConfigModifed();\n\n  void positionUpdated(const QGeoPositionInfo &info);\n\n  void onPaletteChanged(const QPalette &palette);\n\nprotected:\n  Config *_config;\n  MainWindow *_mainWindow;\n  QTranslator *_translator;\n\n  RepeaterDatabase *_repeater;\n  UserDatabase *_users;\n  TalkGroupDatabase *_talkgroups;\n  SatelliteDatabase *_satellites;\n\n  QGeoPositionInfoSource *_source;\n  QGeoCoordinate _currentPosition;\n  unsigned int _searchRadius;\n\n  ReleaseNotes _releaseNotes;\n\n  // Last detected device:\n  USBDeviceDescriptor _lastDevice;\n};\n\n#endif // APPLICATION_HH\n"
  },
  {
    "path": "src/aprsselect.cc",
    "content": "#include \"aprsselect.hh\"\n#include \"gpssystem.hh\"\n#include \"config.hh\"\n\n/* ******************************************************************************************* *\n * FM APRS selection\n * ******************************************************************************************* */\nFMAPRSSelect::FMAPRSSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[None]\"), QVariant::fromValue<FMAPRSSystem *>(nullptr));\n  for (int i=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<FMAPRSSystem>()) {\n      auto sys = config->posSystems()->system(i)->as<FMAPRSSystem>();\n      addItem(sys->name(), QVariant::fromValue(sys));\n    }\n  }\n}\n\n\nvoid\nFMAPRSSelect::setAPRSSystem(FMAPRSSystem *sys) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<FMAPRSSystem*>() == sys) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nFMAPRSSystem *\nFMAPRSSelect::aprsSystem() const {\n  return currentData().value<FMAPRSSystem*>();\n}\n\n\n\n/* ******************************************************************************************* *\n * Any (FM & DMR) APRS selection\n * ******************************************************************************************* */\nAPRSSelect::APRSSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[None]\"), QVariant::fromValue<PositionReportingSystem *>(nullptr));\n  for (int i=0; i<config->posSystems()->count(); i++) {\n    if (config->posSystems()->system(i)->is<PositionReportingSystem>()) {\n      auto sys = config->posSystems()->system(i)->as<PositionReportingSystem>();\n      addItem(sys->name(), QVariant::fromValue(sys));\n    }\n  }\n}\n\n\nvoid\nAPRSSelect::setAPRSSystem(PositionReportingSystem *sys) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<PositionReportingSystem*>() == sys) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nPositionReportingSystem *\nAPRSSelect::aprsSystem() const {\n  return currentData().value<PositionReportingSystem*>();\n}\n"
  },
  {
    "path": "src/aprsselect.hh",
    "content": "#ifndef APRSSELECT_HH\n#define APRSSELECT_HH\n\n#include <QComboBox>\nclass PositionReportingSystem;\nclass FMAPRSSystem;\nclass Config;\n\n\nclass APRSSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  APRSSelect(Config *config, QWidget *parent=nullptr);\n\n  void setAPRSSystem(PositionReportingSystem *sys);\n  PositionReportingSystem *aprsSystem() const;\n};\n\n\n\nclass FMAPRSSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  FMAPRSSelect(Config *config, QWidget *parent=nullptr);\n\n  void setAPRSSystem(FMAPRSSystem *sys);\n  FMAPRSSystem *aprsSystem() const;\n};\n\n#endif // APRSSELECT_HH\n"
  },
  {
    "path": "src/aprssystemdialog.cc",
    "content": "#include \"aprssystemdialog.hh\"\n#include \"ui_aprssystemdialog.h\"\n#include \"settings.hh\"\n\n\nstatic QVector<QPair<FMAPRSSystem::Icon, QString>> aprsIconTable{\n  {FMAPRSSystem::Icon::None, FMAPRSSystem::tr(\"[None]\")},\n  {FMAPRSSystem::Icon::PoliceStation, FMAPRSSystem::tr(\"Police station\")},\n  {FMAPRSSystem::Icon::Digipeater, FMAPRSSystem::tr(\"Digipeater\")},\n  {FMAPRSSystem::Icon::Phone, FMAPRSSystem::tr(\"Phone\")},\n  {FMAPRSSystem::Icon::DXCluster, FMAPRSSystem::tr(\"DX cluster\")},\n  {FMAPRSSystem::Icon::HFGateway, FMAPRSSystem::tr(\"HF gateway\")},\n  {FMAPRSSystem::Icon::SmallPlane, FMAPRSSystem::tr(\"Plane small\")},\n  {FMAPRSSystem::Icon::MobileSatelliteStation, FMAPRSSystem::tr(\"Mobile Satellite station\")},\n  {FMAPRSSystem::Icon::WheelChair, FMAPRSSystem::tr(\"Wheel Chair\")},\n  {FMAPRSSystem::Icon::Snowmobile, FMAPRSSystem::tr(\"Snowmobile\")},\n  {FMAPRSSystem::Icon::RedCross, FMAPRSSystem::tr(\"Red cross\")},\n  {FMAPRSSystem::Icon::BoyScout, FMAPRSSystem::tr(\"Boy scout\")},\n  {FMAPRSSystem::Icon::Home,  FMAPRSSystem::tr(\"Home\")},\n  {FMAPRSSystem::Icon::X, FMAPRSSystem::tr(\"X\")},\n  {FMAPRSSystem::Icon::RedDot, FMAPRSSystem::tr(\"Red dot\")},\n  {FMAPRSSystem::Icon::Circle0, FMAPRSSystem::tr(\"Circle 0\")},\n  {FMAPRSSystem::Icon::Circle1, FMAPRSSystem::tr(\"Circle 1\")},\n  {FMAPRSSystem::Icon::Circle2, FMAPRSSystem::tr(\"Circle 2\")},\n  {FMAPRSSystem::Icon::Circle3, FMAPRSSystem::tr(\"Circle 3\")},\n  {FMAPRSSystem::Icon::Circle4, FMAPRSSystem::tr(\"Circle 4\")},\n  {FMAPRSSystem::Icon::Circle5, FMAPRSSystem::tr(\"Circle 5\")},\n  {FMAPRSSystem::Icon::Circle6, FMAPRSSystem::tr(\"Circle 6\")},\n  {FMAPRSSystem::Icon::Circle7, FMAPRSSystem::tr(\"Circle 7\")},\n  {FMAPRSSystem::Icon::Circle8, FMAPRSSystem::tr(\"Circle 8\")},\n  {FMAPRSSystem::Icon::Circle9, FMAPRSSystem::tr(\"Circle 9\")},\n  {FMAPRSSystem::Icon::Fire, FMAPRSSystem::tr(\"Fire\")},\n  {FMAPRSSystem::Icon::Campground, FMAPRSSystem::tr(\"Campground\")},\n  {FMAPRSSystem::Icon::Motorcycle, FMAPRSSystem::tr(\"Motorcycle\")},\n  {FMAPRSSystem::Icon::RailEngine, FMAPRSSystem::tr(\"Rail engine\")},\n  {FMAPRSSystem::Icon::Car, FMAPRSSystem::tr(\"Car\")},\n  {FMAPRSSystem::Icon::FileServer, FMAPRSSystem::tr(\"File server\")},\n  {FMAPRSSystem::Icon::HCFuture, FMAPRSSystem::tr(\"HC Future\")},\n  {FMAPRSSystem::Icon::AidStation, FMAPRSSystem::tr(\"Aid station\")},\n  {FMAPRSSystem::Icon::BBS, FMAPRSSystem::tr(\"BBS\")},\n  {FMAPRSSystem::Icon::Canoe, FMAPRSSystem::tr(\"Canoe\")},\n  {FMAPRSSystem::Icon::Eyeball, FMAPRSSystem::tr(\"Eyeball\")},\n  {FMAPRSSystem::Icon::Tractor, FMAPRSSystem::tr(\"Tractor\")},\n  {FMAPRSSystem::Icon::GridSquare, FMAPRSSystem::tr(\"Grid Square\")},\n  {FMAPRSSystem::Icon::Hotel, FMAPRSSystem::tr(\"Hotel\")},\n  {FMAPRSSystem::Icon::TCPIP, FMAPRSSystem::tr(\"TCP/IP\")},\n  {FMAPRSSystem::Icon::School, FMAPRSSystem::tr(\"School\")},\n  {FMAPRSSystem::Icon::Logon, FMAPRSSystem::tr(\"Logon\")},\n  {FMAPRSSystem::Icon::MacOS, FMAPRSSystem::tr(\"MacOS\")},\n  {FMAPRSSystem::Icon::NTSStation, FMAPRSSystem::tr(\"NTS station\")},\n  {FMAPRSSystem::Icon::Balloon, FMAPRSSystem::tr(\"Balloon\")},\n  {FMAPRSSystem::Icon::PoliceCar, FMAPRSSystem::tr(\"Police car\")},\n  {FMAPRSSystem::Icon::TBD, FMAPRSSystem::tr(\"TBD\")},\n  {FMAPRSSystem::Icon::RV, FMAPRSSystem::tr(\"RV\")},\n  {FMAPRSSystem::Icon::Shuttle, FMAPRSSystem::tr(\"Shuttle\")},\n  {FMAPRSSystem::Icon::SSTV, FMAPRSSystem::tr(\"SSTV\")},\n  {FMAPRSSystem::Icon::Bus, FMAPRSSystem::tr(\"Bus\")},\n  {FMAPRSSystem::Icon::ATV, FMAPRSSystem::tr(\"ATV\")},\n  {FMAPRSSystem::Icon::WXService, FMAPRSSystem::tr(\"Weather service\")},\n  {FMAPRSSystem::Icon::Helo, FMAPRSSystem::tr(\"Helo\")},\n  {FMAPRSSystem::Icon::Yacht, FMAPRSSystem::tr(\"Yacht\")},\n  {FMAPRSSystem::Icon::Windows, FMAPRSSystem::tr(\"MS Windows\")},\n  {FMAPRSSystem::Icon::Jogger, FMAPRSSystem::tr(\"Jogger\")},\n  {FMAPRSSystem::Icon::Triangle, FMAPRSSystem::tr(\"Triangle\")},\n  {FMAPRSSystem::Icon::PBBS, FMAPRSSystem::tr(\"PBBS\")},\n  {FMAPRSSystem::Icon::LargePlane, FMAPRSSystem::tr(\"Plane large\")},\n  {FMAPRSSystem::Icon::WXStation, FMAPRSSystem::tr(\"Weather station\")},\n  {FMAPRSSystem::Icon::DishAntenna, FMAPRSSystem::tr(\"Dish antenna\")},\n  {FMAPRSSystem::Icon::Ambulance, FMAPRSSystem::tr(\"Ambulance\")},\n  {FMAPRSSystem::Icon::Bike, FMAPRSSystem::tr(\"Bike\")},\n  {FMAPRSSystem::Icon::ICP, FMAPRSSystem::tr(\"ICP\")},\n  {FMAPRSSystem::Icon::FireStation, FMAPRSSystem::tr(\"Fire station\")},\n  {FMAPRSSystem::Icon::Horse, FMAPRSSystem::tr(\"Horse\")},\n  {FMAPRSSystem::Icon::FireTruck, FMAPRSSystem::tr(\"Fire truck\")},\n  {FMAPRSSystem::Icon::Glider, FMAPRSSystem::tr(\"Glider\")},\n  {FMAPRSSystem::Icon::Hospital, FMAPRSSystem::tr(\"Hospital\")},\n  {FMAPRSSystem::Icon::IOTA, FMAPRSSystem::tr(\"IOTA\")},\n  {FMAPRSSystem::Icon::Jeep, FMAPRSSystem::tr(\"Jeep\")},\n  {FMAPRSSystem::Icon::SmallTruck, FMAPRSSystem::tr(\"Truck small\")},\n  {FMAPRSSystem::Icon::Laptop, FMAPRSSystem::tr(\"Laptop\")},\n  {FMAPRSSystem::Icon::MicE, FMAPRSSystem::tr(\"Mic-E\")},\n  {FMAPRSSystem::Icon::Node, FMAPRSSystem::tr(\"Node\")},\n  {FMAPRSSystem::Icon::EOC, FMAPRSSystem::tr(\"EOC\")},\n  {FMAPRSSystem::Icon::Rover, FMAPRSSystem::tr(\"Rover\")},\n  {FMAPRSSystem::Icon::Grid, FMAPRSSystem::tr(\"Grid\")},\n  {FMAPRSSystem::Icon::Antenna, FMAPRSSystem::tr(\"Antenna\")},\n  {FMAPRSSystem::Icon::PowerBoat, FMAPRSSystem::tr(\"Power boat\")},\n  {FMAPRSSystem::Icon::TruckStop, FMAPRSSystem::tr(\"Truck stop\")},\n  {FMAPRSSystem::Icon::TruckLarge, FMAPRSSystem::tr(\"Truck large\")},\n  {FMAPRSSystem::Icon::Van, FMAPRSSystem::tr(\"Van\")},\n  {FMAPRSSystem::Icon::Water, FMAPRSSystem::tr(\"Water\")},\n  {FMAPRSSystem::Icon::XAPRS, FMAPRSSystem::tr(\"XAPRS\")},\n  {FMAPRSSystem::Icon::Yagi, FMAPRSSystem::tr(\"Yagi\")},\n  {FMAPRSSystem::Icon::Shelter, FMAPRSSystem::tr(\"Shelter\")}};\n\n\n\nAPRSSystemDialog::APRSSystemDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myAPRS(new FMAPRSSystem()), _aprs(nullptr),\n    ui(new Ui::aprssystemdialog), _icons0(\":/icons/aprs/table0.png\")\n{\n  setWindowTitle(tr(\"Create APRS system\"));\n  construct();\n}\n\nAPRSSystemDialog::APRSSystemDialog(Config *config, FMAPRSSystem *aprs, QWidget *parent)\n  : QDialog(parent), _config(config), _myAPRS(new FMAPRSSystem()), _aprs(aprs),\n    ui(new Ui::aprssystemdialog), _icons0(\":/icons/aprs/table0.png\")\n{\n  setWindowTitle(tr(\"Edit APRS system\"));\n\n  if (_aprs)\n    _myAPRS->copy(*_aprs);\n\n  construct();\n}\n\nAPRSSystemDialog::~APRSSystemDialog() {\n  delete ui;\n}\n\n\nvoid\nAPRSSystemDialog::construct() {\n  // Construct UI\n  ui->setupUi(this);\n  Settings settings;\n\n  // Setup name\n  ui->name->setText(_myAPRS->name());\n\n  // Setup analog revert channels\n  ui->channel->addItem(tr(\"[Selected]\"), QVariant::fromValue<FMChannel*>(nullptr));\n  if (! _myAPRS->hasRevertChannel())\n    ui->channel->setCurrentIndex(0);\n  for (int i=0, j=1; i<_config->channelList()->count(); i++) {\n    if (! _config->channelList()->channel(i)->is<FMChannel>())\n      continue;\n    FMChannel *ch = _config->channelList()->channel(i)->as<FMChannel>();\n    ui->channel->addItem(ch->name(), QVariant::fromValue(ch));\n    if (_myAPRS->hasRevertChannel() && (_myAPRS->revertChannel() == ch))\n      ui->channel->setCurrentIndex(j);\n    j++;\n  }\n\n  ui->source->setText(_myAPRS->source());\n  ui->srcSSID->setValue(_myAPRS->srcSSID());\n  ui->destination->setText(_myAPRS->destination());\n  ui->destSSID->setValue(_myAPRS->destSSID());\n  ui->path->setText(_myAPRS->path());\n\n  // Setup icons\n  for (int i=0; i<aprsIconTable.size(); i++) {\n    const QPair<FMAPRSSystem::Icon, QString> &item = aprsIconTable[i];\n    ui->icon->addItem(aprsIcon(item.first), item.second, unsigned(item.first));\n    if (_myAPRS->icon() == item.first)\n      ui->icon->setCurrentIndex(i);\n  }\n\n  if (_myAPRS->periodDisabled())\n    ui->updatePeriod->setValue(0);\n  else\n    ui->updatePeriod->setValue(_myAPRS->period().seconds());\n  ui->message->setText(_myAPRS->message());\n\n  ui->extensionView->setObjectName(\"aprsSystemExtension\");\n  ui->extensionView->setObject(_myAPRS, _config);\n}\n\nFMAPRSSystem *\nAPRSSystemDialog::aprsSystem() {\n  _myAPRS->setName(ui->name->text().simplified());\n  if (ui->channel->currentData().isNull())\n    _myAPRS->resetRevertChannel();\n  else\n    _myAPRS->setRevertChannel(ui->channel->currentData().value<FMChannel*>());\n  _myAPRS->setSource(ui->source->text().simplified(), ui->srcSSID->value());\n  _myAPRS->setDestination(ui->destination->text().simplified(), ui->destSSID->value());\n  _myAPRS->setPath(ui->path->text().simplified());\n  _myAPRS->setIcon(FMAPRSSystem::Icon(ui->icon->currentData().toUInt()));\n  if (0 == ui->updatePeriod->value())\n    _myAPRS->disablePeriod();\n  else\n    _myAPRS->setPeriod(Interval::fromSeconds(ui->updatePeriod->value()));\n  _myAPRS->setMessage(ui->message->text().simplified());\n\n  FMAPRSSystem *system = _myAPRS;\n  if (_aprs) {\n    _aprs->copy(*_myAPRS);\n    system = _aprs;\n  } else {\n    _myAPRS->setParent(nullptr);\n  }\n\n  return system;\n}\n\nQIcon\nAPRSSystemDialog::aprsIcon(FMAPRSSystem::Icon icon) {\n  unsigned table = (FMAPRSSystem::TABLE_MASK & (unsigned)icon);\n  unsigned idx   = (FMAPRSSystem::ICON_MASK & (unsigned)icon);\n  unsigned row   = idx/16, col = idx % 16;\n\n  if (FMAPRSSystem::PRIMARY_TABLE == table) {\n    return QPixmap::fromImage(_icons0.copy(24*col, 24*row, 24,24));\n  }\n\n  return QPixmap::fromImage(_icons0.copy(24,0,24,24));\n}\n"
  },
  {
    "path": "src/aprssystemdialog.hh",
    "content": "#ifndef APRSSYSTEMDIALOG_HH\n#define APRSSYSTEMDIALOG_HH\n\n#include <QDialog>\n#include <QImage>\n#include \"config.hh\"\n\nnamespace Ui {\n  class aprssystemdialog;\n}\n\nclass APRSSystemDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit APRSSystemDialog(Config *config, QWidget *parent = nullptr);\n  explicit APRSSystemDialog(Config *config, FMAPRSSystem *aprs, QWidget *parent = nullptr);\n\n  virtual ~APRSSystemDialog();\n\n  FMAPRSSystem *aprsSystem();\n\nprotected:\n  void construct();\n  QIcon aprsIcon(FMAPRSSystem::Icon icon);\n\nprotected:\n  Config *_config;\n  FMAPRSSystem *_myAPRS;\n  FMAPRSSystem *_aprs;\n\nprivate:\n  Ui::aprssystemdialog *ui;\n  QImage _icons0;\n};\n\n#endif // APRSSYSTEMDIALOG_HH\n"
  },
  {
    "path": "src/aprssystemdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>aprssystemdialog</class>\n <widget class=\"QDialog\" name=\"aprssystemdialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>569</width>\n    <height>431</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit APRS System</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <property name=\"horizontalSpacing\">\n        <number>6</number>\n       </property>\n       <property name=\"verticalSpacing\">\n        <number>6</number>\n       </property>\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nameLabel\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"name\"/>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"channelLabel\">\n         <property name=\"text\">\n          <string>Channel</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"channel\"/>\n       </item>\n       <item row=\"4\" column=\"0\">\n        <widget class=\"QLabel\" name=\"sourceLabel\">\n         <property name=\"text\">\n          <string>Source</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"4\" column=\"1\">\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QLineEdit\" name=\"source\">\n           <property name=\"inputMask\">\n            <string/>\n           </property>\n           <property name=\"maxLength\">\n            <number>6</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"label\">\n           <property name=\"text\">\n            <string notr=\"true\">-</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"srcSSID\">\n           <property name=\"maximum\">\n            <number>15</number>\n           </property>\n           <property name=\"value\">\n            <number>7</number>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item row=\"6\" column=\"0\">\n        <widget class=\"QLabel\" name=\"destinationLabel\">\n         <property name=\"text\">\n          <string>Destination</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"6\" column=\"1\">\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <item>\n          <widget class=\"QLineEdit\" name=\"destination\">\n           <property name=\"text\">\n            <string notr=\"true\">WIDE3</string>\n           </property>\n           <property name=\"maxLength\">\n            <number>6</number>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"text\">\n            <string notr=\"true\">-</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QSpinBox\" name=\"destSSID\">\n           <property name=\"maximum\">\n            <number>15</number>\n           </property>\n           <property name=\"value\">\n            <number>3</number>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item row=\"8\" column=\"0\">\n        <widget class=\"QLabel\" name=\"pathLabel\">\n         <property name=\"text\">\n          <string>Path</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"12\" column=\"0\">\n        <widget class=\"QLabel\" name=\"iconLabel\">\n         <property name=\"text\">\n          <string>Icon</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"15\" column=\"0\">\n        <widget class=\"QLabel\" name=\"updatePeriodSLabel\">\n         <property name=\"text\">\n          <string>Update period [s]</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"19\" column=\"0\">\n        <widget class=\"QLabel\" name=\"messageLabel\">\n         <property name=\"text\">\n          <string>Message</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"19\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"message\"/>\n       </item>\n       <item row=\"8\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"path\"/>\n       </item>\n       <item row=\"12\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"icon\"/>\n       </item>\n       <item row=\"15\" column=\"1\">\n        <widget class=\"QSpinBox\" name=\"updatePeriod\">\n         <property name=\"suffix\">\n          <string notr=\"true\">s</string>\n         </property>\n         <property name=\"maximum\">\n          <number>6000</number>\n         </property>\n         <property name=\"singleStep\">\n          <number>10</number>\n         </property>\n         <property name=\"value\">\n          <number>300</number>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>aprssystemdialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>aprssystemdialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/bandwidthselect.cc",
    "content": "#include \"bandwidthselect.hh\"\n#include \"channel.hh\"\n\n\nBandwidthSelect::BandwidthSelect(QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"Narrow (12.5 kHz)\"), QVariant::fromValue(FMChannel::Bandwidth::Narrow));\n  addItem(tr(\"Wide (25 kHz)\"), QVariant::fromValue(FMChannel::Bandwidth::Wide));\n}\n\nvoid\nBandwidthSelect::setBandwidth(FMChannel::Bandwidth bw) {\n  setCurrentIndex((FMChannel::Bandwidth::Narrow == bw) ? 0 : 1);\n}\n\nFMChannel::Bandwidth\nBandwidthSelect::bandwidth() const {\n  return currentData().value<FMChannel::Bandwidth>();\n}\n"
  },
  {
    "path": "src/bandwidthselect.hh",
    "content": "#ifndef BANDWIDTHSELECT_HH\n#define BANDWIDTHSELECT_HH\n\n#include <QComboBox>\n#include \"channel.hh\"\n\n\nclass BandwidthSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  explicit BandwidthSelect(QWidget *parent=nullptr);\n\n  void setBandwidth(FMChannel::Bandwidth bw);\n  FMChannel::Bandwidth bandwidth() const;\n};\n\n#endif // BANDWIDTHSELECT_HH\n"
  },
  {
    "path": "src/channel_type_edit.cc",
    "content": "#include \"channel_type_edit.hh\"\n\n#include <QDialogButtonBox>\n#include <QHBoxLayout>\n#include <QIcon>\n#include <QLabel>\n#include <QListWidget>\n#include <QMetaEnum>\n#include <QPushButton>\n#include <QVBoxLayout>\n#include <QDialogButtonBox>\n\n#include \"channel.hh\"\n\nChannelTypeEdit::ChannelTypeEdit(QWidget *parent)\n  : QWidget(parent), _types(Channel::Type::None)\n{\n  _label = new QLabel();\n  _label->setText(QMetaEnum::fromType<Channel::Type>().valueToKeys(_types));\n  _label->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);\n  auto edit = new QPushButton();\n  edit->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);\n  edit->setIcon(QIcon::fromTheme(\"edit-edit\"));\n  auto box = new QHBoxLayout();\n  box->addWidget(edit);\n  box->addWidget(_label);\n  setLayout(box);\n\n  connect(edit, &QPushButton::clicked, this, &ChannelTypeEdit::onEditClicked);\n}\n\nChannel::Types\nChannelTypeEdit::types() const {\n  return _types;\n}\n\nvoid ChannelTypeEdit::setTypes(Channel::Types types) {\n  _types = types;\n  _label->setText(QMetaEnum::fromType<Channel::Type>().valueToKeys(_types));\n}\n\nvoid ChannelTypeEdit::onEditClicked() {\n  ChannelTypeSelectionDialog dialog(_types);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  setTypes(dialog.types());\n  emit typesChanged(types());\n}\n\nChannelTypeSelectionDialog::ChannelTypeSelectionDialog(Channel::Types types, QWidget *parent)\n  : QDialog(parent), _list(new QListWidget(this))\n{\n  setWindowTitle(tr(\"Select channel types\"));\n\n  auto e = QMetaEnum::fromType<Channel::Type>();\n  for (int i=1; i<e.keyCount(); i++) {\n    auto item = new QListWidgetItem(e.key(i));\n    auto value = (Channel::Type)e.value(i);\n    item->setData(Qt::UserRole, QVariant::fromValue(value));\n    item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable);\n    item->setCheckState(types.testFlag(value) ? Qt::Checked : Qt::Unchecked);\n    _list->addItem(item);\n  }\n  auto bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);\n  auto box = new QVBoxLayout();\n  box->addWidget(_list);\n  box->addWidget(bbox);\n  setLayout(box);\n\n  connect(bbox, &QDialogButtonBox::accepted, this, &QDialog::accept);\n  connect(bbox, &QDialogButtonBox::rejected, this, &QDialog::reject);\n}\n\nChannel::Types\nChannelTypeSelectionDialog::types() const {\n  Channel::Types types = Channel::Type::None;\n  for (int i=0; i<_list->count(); i++) {\n    auto item = _list->item(i);\n    if (item->checkState() == Qt::Checked)\n      types |= item->data(Qt::UserRole).value<Channel::Type>();\n  }\n  return types;\n}"
  },
  {
    "path": "src/channel_type_edit.hh",
    "content": "#ifndef CHANNEL_TYPE_EDIT_HH\n#define CHANNEL_TYPE_EDIT_HH\n\n#include <QDialog>\n#include \"channel.hh\"\n\nclass QLabel;\nclass QListWidget;\n\n\nclass ChannelTypeEdit: public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ChannelTypeEdit(QWidget *parent = nullptr);\n\n  Channel::Types types() const;\n\npublic slots:\n  void setTypes(Channel::Types types);\n\nsignals:\n  void typesChanged(Channel::Types types);\n\nprotected slots:\n  void onEditClicked();\n\nprotected:\n  QLabel *_label;\n  Channel::Types _types;\n};\n\n\nclass ChannelTypeSelectionDialog: public QDialog\n{\n  Q_OBJECT\n\npublic:\n  ChannelTypeSelectionDialog(Channel::Types types, QWidget *parent = nullptr);\n\n  Channel::Types types() const;\n\nprotected:\n  QListWidget *_list;\n};\n\n\n#endif //CHANNEL_TYPE_EDIT_HH\n"
  },
  {
    "path": "src/channelcombobox.cc",
    "content": "#include \"channelcombobox.hh\"\n#include \"channel.hh\"\n#include <QCompleter>\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelComboBox\n * ********************************************************************************************* */\nChannelComboBox::ChannelComboBox(ChannelList *list, bool includeSelectedChannel, QWidget *parent)\n  : QComboBox(parent)\n{\n  setInsertPolicy(QComboBox::NoInsert);\n  setEditable(true);\n  completer()->setCompletionMode(QCompleter::PopupCompletion);\n  if (includeSelectedChannel)\n    addItem(SelectedChannel::get()->name(), QVariant::fromValue(SelectedChannel::get()));\n  for (int i=0; i<list->count(); i++)\n    addItem(list->channel(i)->name(), QVariant::fromValue(list->channel(i)));\n}\n\nChannel *\nChannelComboBox::channel() const {\n  return currentData().value<Channel*>();\n}\n\n\n"
  },
  {
    "path": "src/channelcombobox.hh",
    "content": "#ifndef CHANNELCOMBOBOX_HH\n#define CHANNELCOMBOBOX_HH\n\n#include <QComboBox>\n\nclass Channel;\nclass ChannelList;\n\nclass ChannelComboBox: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  ChannelComboBox(ChannelList *lst, bool includeSelectedChannel=false, QWidget *parent=nullptr);\n\n  Channel *channel() const;\n};\n\n\n\n#endif // CHANNELCOMBOBOX_HH\n"
  },
  {
    "path": "src/channeldialog.cc",
    "content": "#include \"channeldialog.hh\"\n#include \"channel.hh\"\n#include \"settings.hh\"\n#include \"application.hh\"\n#include \"ui_channeldialog.h\"\n\n\n\nChannelDialog::ChannelDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _channel()\n{\n  ui = new Ui::ChannelDialog();\n  ui->setupUi(this);\n  Settings settings;\n\n  if (settings.hideChannelNote()) {\n    ui->hintLabel->setVisible(false);\n    layout()->invalidate();\n    adjustSize();\n  }\n  connect(ui->hintLabel, &QLabel::linkActivated,\n    this, &ChannelDialog::onHideChannelHint);\n\n  ui->offsetComboBox->addItem(QIcon::fromTheme(\"symbol-none\"), \"\");\n  ui->offsetComboBox->setItemData(0, tr(\"No offset\"), Qt::ToolTipRole);\n  ui->offsetComboBox->addItem(QIcon::fromTheme(\"symbol-plus\"), \"\");\n  ui->offsetComboBox->setItemData(1, tr(\"Positive offset\"), Qt::ToolTipRole);\n  ui->offsetComboBox->addItem(QIcon::fromTheme(\"symbol-minus\"), \"\");\n  ui->offsetComboBox->setItemData(2, tr(\"Negative offset\"), Qt::ToolTipRole);\n  connect(ui->txFrequency, &QLineEdit::editingFinished,\n    this, &ChannelDialog::onTxFrequencyEdited);\n  connect(ui->rxFrequency, &QLineEdit::editingFinished,\n    this, &ChannelDialog::onRxFrequencyEdited);\n  connect(ui->offsetLineEdit, &QLineEdit::editingFinished,\n    this, &ChannelDialog::onOffsetFrequencyEdited);\n  connect(ui->offsetComboBox, &QComboBox::currentIndexChanged,\n    this, &ChannelDialog::onOffsetDirectionChanged);\n\n  ui->powerValue->setItemData(0, unsigned(Channel::Power::Max));\n  ui->powerValue->setItemData(1, unsigned(Channel::Power::High));\n  ui->powerValue->setItemData(2, unsigned(Channel::Power::Mid));\n  ui->powerValue->setItemData(3, unsigned(Channel::Power::Low));\n  ui->powerValue->setItemData(4, unsigned(Channel::Power::Min));\n  ui->powerDefault->setChecked(true); ui->powerValue->setEnabled(false); ui->powerValue->setCurrentIndex(1);\n  connect(ui->powerDefault, &QCheckBox::toggled, [this](bool checked) { this->ui->powerValue->setEnabled(! checked); });\n\n  ui->totDefault->setChecked(true); ui->totValue->setValue(0); ui->totValue->setEnabled(false);\n  connect(ui->totDefault, &QCheckBox::toggled, [this](bool checked) { this->ui->totValue->setEnabled(! checked); });\n\n  ui->scanList->addItem(tr(\"[None]\"), QVariant::fromValue<ScanList *>(nullptr));\n  for (int i=0; i<_config->scanlists()->count(); i++) {\n    auto lst = _config->scanlists()->scanlist(i);\n    ui->scanList->addItem(lst->name(), QVariant::fromValue(lst));\n  }\n\n  ui->voxDefault->setChecked(true); ui->voxValue->setValue(0); ui->voxValue->setEnabled(false);\n  connect(ui->voxDefault, &QCheckBox::toggled, [this](bool checked) { this->ui->voxValue->setEnabled(! checked); });\n\n  updateOffsetFrequency();\n\n  connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &ChannelDialog::accept);\n  connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &ChannelDialog::reject);\n}\n\n\nChannelDialog::~ChannelDialog() {\n  delete ui;\n}\n\n\nvoid\nChannelDialog::setChannel(Channel *channel) {\n  _channel = channel;\n  if (_channel.isNull())\n    return;\n\n  ui->channelName->setText(_channel->name());\n  ui->rxFrequency->setText(_channel->rxFrequency().format(Frequency::Unit::MHz));\n  ui->txFrequency->setText(_channel->txFrequency().format(Frequency::Unit::MHz));\n  updateOffsetFrequency();\n\n  if (! _channel->defaultPower()) {\n    ui->powerDefault->setChecked(false); ui->powerValue->setEnabled(true);\n    switch (_channel->power()) {\n    case Channel::Power::Max: ui->powerValue->setCurrentIndex(0); break;\n    case Channel::Power::High: ui->powerValue->setCurrentIndex(1); break;\n    case Channel::Power::Mid: ui->powerValue->setCurrentIndex(2); break;\n    case Channel::Power::Low: ui->powerValue->setCurrentIndex(3); break;\n    case Channel::Power::Min: ui->powerValue->setCurrentIndex(4); break;\n    }\n  }\n\n  if (! _channel->defaultTimeout()) {\n    ui->totDefault->setChecked(false); ui->totValue->setEnabled(true);\n    ui->totValue->setValue(_channel->timeout().seconds());\n  }\n  ui->rxOnly->setChecked(_channel->rxOnly());\n  if (! _channel->defaultVOX()) {\n    ui->voxDefault->setChecked(false); ui->voxValue->setEnabled(true);\n    ui->voxValue->setValue(_channel->vox().value());\n  }\n\n  ui->extensionView->setObjectName(\"ChannelExtension\");\n  ui->extensionView->setObject(_channel, _config);\n\n  for (int i=0; i<ui->scanList->count(); i++) {\n    if (ui->scanList->itemData(i).value<ScanList*>() == _channel->scanList())\n      ui->scanList->setCurrentIndex(i);\n  }\n}\n\n\nvoid\nChannelDialog::accept() {\n  if (_channel.isNull())\n    return;\n\n  _channel->setName(ui->channelName->text().simplified());\n  _channel->setRXFrequency(Frequency::fromString(ui->rxFrequency->text()));\n  _channel->setTXFrequency(Frequency::fromString(ui->txFrequency->text()));\n  if (ui->powerDefault->isChecked()) {\n    _channel->setDefaultPower();\n  } else {\n    _channel->setPower(Channel::Power(ui->powerValue->currentData().toUInt()));\n  }\n  if (ui->totDefault->isChecked())\n    _channel->setDefaultTimeout();\n  else\n    _channel->setTimeout(Interval::fromSeconds(ui->totValue->value()));\n  _channel->setRXOnly(ui->rxOnly->isChecked());\n  _channel->setScanList(ui->scanList->currentData().value<ScanList *>());\n  if (ui->voxDefault->isChecked())\n    _channel->setVOXDefault();\n  else\n    _channel->setVOX(Level::fromValue(ui->voxValue->value()));\n\n  QDialog::accept();\n}\n\n\nvoid\nChannelDialog::onTxFrequencyEdited() {\n  // Normalize frequency format\n  ui->txFrequency->setText(Frequency::fromString(ui->txFrequency->text()).format());\n  updateOffsetFrequency();\n}\n\nvoid\nChannelDialog::onRxFrequencyEdited() {\n  auto rx = Frequency::fromString(ui->rxFrequency->text());\n  // normalize RX frequency\n  ui->rxFrequency->setText(rx.format());\n  // if no previous txFrequency set, match rx frequency.\n  if (ui->txFrequency->text().isEmpty())\n    ui->txFrequency->setText(rx.format());\n  updateOffsetFrequency();\n}\n\nvoid\nChannelDialog::onOffsetFrequencyEdited() {\n  auto offsetFrequency = FrequencyOffset::fromString(ui->offsetLineEdit->text()).abs();\n  // By default use same as RX frequency\n  auto rx = Frequency::fromString(ui->rxFrequency->text()), tx = rx;\n\n  switch (ui->offsetComboBox->currentIndex()) {\n  case 0: break;\n  case 1: tx = rx + offsetFrequency; break;\n  case 2: tx = rx + offsetFrequency.invert(); break;\n  }\n\n  ui->offsetLineEdit->setText(offsetFrequency.format());\n  ui->txFrequency->setText(tx.format());\n}\n\nvoid\nChannelDialog::onOffsetDirectionChanged(int index) {\n  auto rx = Frequency::fromString(ui->rxFrequency->text()), tx = rx;\n  FrequencyOffset offsetFrequency = FrequencyOffset::fromString(ui->offsetLineEdit->text()).abs();\n\n  switch (index) {\n  case 0:\n    ui->offsetLineEdit->setEnabled(false);\n    break;\n  case 1:\n    ui->offsetLineEdit->setEnabled(true);\n    tx = rx + offsetFrequency;\n    break;\n  case 2:\n    ui->offsetLineEdit->setEnabled(true);\n    tx = rx + offsetFrequency.invert();\n    break;\n  }\n\n  ui->txFrequency->setText(tx.format());\n}\n\nvoid\nChannelDialog::updateOffsetFrequency() {\n  Frequency rx,tx;\n  if (!rx.parse(ui->rxFrequency->text().simplified()) ||\n      !tx.parse(ui->txFrequency->text().simplified()))\n    return;\n  // Show absolute value\n  ui->offsetLineEdit->setText((tx-rx).abs().format());\n  // Use combo box to indicate direction\n  updateComboBox();\n}\n\nvoid\nChannelDialog::updateComboBox() {\n  auto rx = Frequency::fromString(ui->rxFrequency->text()),\n    tx = Frequency::fromString(ui->txFrequency->text());\n  if (tx == rx) {\n    ui->offsetComboBox->setCurrentIndex(0);\n    ui->offsetLineEdit->setEnabled(false);\n  } else if (tx > rx) {\n    ui->offsetComboBox->setCurrentIndex(1);\n    ui->offsetLineEdit->setEnabled(true);\n  } else {\n    ui->offsetComboBox->setCurrentIndex(2);\n    ui->offsetLineEdit->setEnabled(true);\n  }\n}\n\n\nvoid\nChannelDialog::onHideChannelHint() {\n  Settings settings;\n  settings.setHideChannelNote(true);\n  ui->hintLabel->setVisible(false);\n  layout()->invalidate();\n  adjustSize();\n}\n"
  },
  {
    "path": "src/channeldialog.hh",
    "content": "#ifndef CHANNELDIALOG_HH\n#define CHANNELDIALOG_HH\n\n#include <QDialog>\n#include <QPointer>\n\nnamespace Ui {\nclass ChannelDialog;\n}\nclass Config;\nclass Channel;\n\n\nclass ChannelDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  ChannelDialog(Config *config, QWidget *parent=nullptr);\n  ~ChannelDialog() override;\n\n  void setChannel(Channel *mychannel);\n\npublic slots:\n  void accept() override;\n\nprotected slots:\n  void updateOffsetFrequency();\n\nprivate slots:\n  void onRxFrequencyEdited();\n  void onTxFrequencyEdited();\n  void onOffsetFrequencyEdited();\n  void onOffsetDirectionChanged(int index);\n  void onHideChannelHint();\n\nprivate:\n  void updateComboBox();\n\nprotected:\n  Ui::ChannelDialog *ui;\n  QPointer<Config> _config;\n  QPointer<Channel> _channel;\n};\n\n#endif // CHANNELDIALOG_HH\n"
  },
  {
    "path": "src/channeldialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ChannelDialog</class>\n <widget class=\"QDialog\" name=\"ChannelDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>680</width>\n    <height>545</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Channel</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"hintLabel\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">padding:10px;border: 2px solid black; border-radius: 10px;</string>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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.&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::TextFormat::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_11\">\n       <item>\n        <layout class=\"QFormLayout\" name=\"leftForm\">\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"nameLabel\">\n           <property name=\"text\">\n            <string>Name</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"channelName\">\n           <property name=\"minimumSize\">\n            <size>\n             <width>200</width>\n             <height>0</height>\n            </size>\n           </property>\n           <property name=\"toolTip\">\n            <string>Enter a repater call-sign.</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"rxFrequencyLabel\">\n           <property name=\"text\">\n            <string>Rx Frequency</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"rxFrequency\"/>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"txFrequencyLabel\">\n           <property name=\"text\">\n            <string>Tx Frequency</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"txFrequency\"/>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QLabel\" name=\"offsetLabel\">\n           <property name=\"text\">\n            <string>Tx Offset</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"1\">\n          <layout class=\"QHBoxLayout\" name=\"offsetLayout\">\n           <item>\n            <widget class=\"QComboBox\" name=\"offsetComboBox\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"minimumSize\">\n              <size>\n               <width>62</width>\n               <height>0</height>\n              </size>\n             </property>\n             <property name=\"maximumSize\">\n              <size>\n               <width>62</width>\n               <height>16777215</height>\n              </size>\n             </property>\n             <property name=\"currentText\">\n              <string notr=\"true\"/>\n             </property>\n             <property name=\"currentIndex\">\n              <number>-1</number>\n             </property>\n             <property name=\"sizeAdjustPolicy\">\n              <enum>QComboBox::SizeAdjustPolicy::AdjustToMinimumContentsLengthWithIcon</enum>\n             </property>\n             <property name=\"placeholderText\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QLineEdit\" name=\"offsetLineEdit\">\n             <property name=\"enabled\">\n              <bool>true</bool>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item row=\"4\" column=\"0\">\n          <widget class=\"QLabel\" name=\"powerLabel\">\n           <property name=\"text\">\n            <string>Power</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"4\" column=\"1\">\n          <layout class=\"QHBoxLayout\" name=\"powerLayout\">\n           <item>\n            <widget class=\"QComboBox\" name=\"powerValue\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <item>\n              <property name=\"text\">\n               <string>Max</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>High</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Mid</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Low</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Min</string>\n              </property>\n             </item>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"powerDefault\">\n             <property name=\"text\">\n              <string>Default</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item row=\"5\" column=\"0\">\n          <widget class=\"QLabel\" name=\"txTimeoutLabel\">\n           <property name=\"text\">\n            <string>Tx Timeout</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"5\" column=\"1\">\n          <layout class=\"QHBoxLayout\" name=\"totLayout\">\n           <item>\n            <widget class=\"QSpinBox\" name=\"totValue\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"specialValueText\">\n              <string>Off</string>\n             </property>\n             <property name=\"suffix\">\n              <string notr=\"true\" extracomment=\"Transmit time out (TOT) in seconds. A value of 0 means disabled.\"> s</string>\n             </property>\n             <property name=\"maximum\">\n              <number>9999</number>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"totDefault\">\n             <property name=\"text\">\n              <string>Default</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item row=\"6\" column=\"0\">\n          <widget class=\"QLabel\" name=\"vOXLevelLabel\">\n           <property name=\"text\">\n            <string>VOX Level</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"6\" column=\"1\">\n          <layout class=\"QHBoxLayout\" name=\"voxLayout\">\n           <item>\n            <widget class=\"QSpinBox\" name=\"voxValue\">\n             <property name=\"sizePolicy\">\n              <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n               <horstretch>0</horstretch>\n               <verstretch>0</verstretch>\n              </sizepolicy>\n             </property>\n             <property name=\"specialValueText\">\n              <string>Off</string>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item>\n            <widget class=\"QCheckBox\" name=\"voxDefault\">\n             <property name=\"text\">\n              <string>Default</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item row=\"7\" column=\"0\">\n          <widget class=\"QLabel\" name=\"rxOnlyLabel\">\n           <property name=\"text\">\n            <string>Rx Only</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"7\" column=\"1\">\n          <widget class=\"QCheckBox\" name=\"rxOnly\"/>\n         </item>\n         <item row=\"8\" column=\"0\">\n          <widget class=\"QLabel\" name=\"scanListLabel\">\n           <property name=\"text\">\n            <string>Scan List</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"8\" column=\"1\">\n          <widget class=\"QComboBox\" name=\"scanList\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QFormLayout\" name=\"rightForm\">\n         <property name=\"sizeConstraint\">\n          <enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>\n         </property>\n         <property name=\"fieldGrowthPolicy\">\n          <enum>QFormLayout::FieldGrowthPolicy::FieldsStayAtSizeHint</enum>\n         </property>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"extensionTab\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"ExtensionView\" name=\"extensionView\" native=\"true\"/>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/channellistview.cc",
    "content": "#include \"channellistview.hh\"\n#include \"ui_channellistview.h\"\n\n#include \"fmchanneldialog.hh\"\n#include \"amchanneldialog.hh\"\n#include \"dmrchanneldialog.hh\"\n#include \"config.hh\"\n#include \"settings.hh\"\n\n#include <QMenu>\n#include <QHeaderView>\n#include <QMessageBox>\n#include <QMenu>\n\n#include \"m17channeldialog.hh\"\n\nChannelListView::ChannelListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::ChannelListView), _config(config)\n{\n  ui->setupUi(this);\n\n  connect(ui->channelTableView->header(), SIGNAL(sectionCountChanged(int,int)),\n          this, SLOT(loadChannelListSectionState()));\n  connect(ui->channelTableView->header(), SIGNAL(sectionResized(int,int,int)),\n          this, SLOT(storeChannelListSectionState()));\n\n  ui->channelTableView->setModel(new ChannelListWrapper(_config->channelList(), ui->channelTableView));\n\n  auto menu = new QMenu();\n  menu->addAction(ui->actionAddDMRChannel);\n  menu->addAction(ui->actionAddFMChannel);\n  menu->addAction(ui->actionAddAMChannel);\n  ui->addChannel->setMenu(menu);\n\n  connect(ui->actionAddFMChannel, &QAction::triggered, this, &ChannelListView::onAddFMChannel);\n  connect(ui->actionAddAMChannel, &QAction::triggered, this, &ChannelListView::onAddAMChannel);\n  connect(ui->actionAddDMRChannel, &QAction::triggered, this, &ChannelListView::onAddDMRChannel);\n  connect(ui->cloneChannel, SIGNAL(clicked()), this, SLOT(onCloneChannel()));\n  connect(ui->remChannel, SIGNAL(clicked()), this, SLOT(onRemChannel()));\n  connect(ui->channelTableView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditChannel(unsigned)));\n}\n\nChannelListView::~ChannelListView() {\n  delete ui;\n}\n\n\nvoid\nChannelListView::onAddFMChannel() {\n  FMChannelDialog dialog(_config, parentWidget());\n  auto newCh = new FMChannel();\n  dialog.setChannel(newCh);\n  if (QDialog::Accepted != dialog.exec()) {\n    delete newCh;\n    return;\n  }\n\n  int row=-1;\n  if (ui->channelTableView->hasSelection())\n    row = ui->channelTableView->selection().second+1;\n  _config->channelList()->add(newCh, row);\n}\n\nvoid\nChannelListView::onAddAMChannel() {\n  AMChannelDialog dialog(_config, parentWidget());\n  auto newCh = new AMChannel();\n  dialog.setChannel(newCh);\n\n  if (QDialog::Accepted != dialog.exec()) {\n    delete newCh;\n    return;\n  }\n\n  int row=-1;\n  if (ui->channelTableView->hasSelection())\n    row = ui->channelTableView->selection().second+1;\n  _config->channelList()->add(newCh, row);\n}\n\nvoid\nChannelListView::onAddDMRChannel() {\n  DMRChannelDialog dialog(_config, parentWidget());\n  auto newCh = new DMRChannel();\n  dialog.setChannel(newCh);\n  if (QDialog::Accepted != dialog.exec()) {\n    delete newCh;\n    return;\n  }\n\n  int row=-1;\n  if (ui->channelTableView->hasSelection())\n    row = ui->channelTableView->selection().second+1;\n  _config->channelList()->add(newCh, row);\n}\n\nvoid\nChannelListView::onCloneChannel() {\n  // get selection\n  int row = ui->channelTableView->selection().first;\n  if ((! ui->channelTableView->hasSelection()) || (row != ui->channelTableView->selection().second)) {\n    QMessageBox::information(nullptr, tr(\"Select a single channel first\"),\n                             tr(\"To clone a channel, please select a single channel to clone.\"),\n                             QMessageBox::Close);\n    return;\n  }\n\n  // Get selected channel\n  Channel *channel = _config->channelList()->channel(row);\n  if (! channel)\n    return;\n\n  // Dispatch by type\n  if (channel->is<FMChannel>()) {\n    // clone channel\n    FMChannel *clone = channel->clone()->as<FMChannel>();\n    // open editor\n    FMChannelDialog dialog(_config);\n    dialog.setChannel(clone);\n    if (QDialog::Accepted != dialog.exec()) {\n      // if rejected -> destroy clone\n      clone->deleteLater();\n      return;\n    }\n    // add to list (below selected one)\n    _config->channelList()->add(clone, row+1);\n  } else if (channel->is<AMChannel>()) {\n    // clone channel\n    auto clone = channel->clone()->as<AMChannel>();\n    // open editor\n    AMChannelDialog dialog(_config);\n    dialog.setChannel(clone);\n    if (QDialog::Accepted != dialog.exec()) {\n      // if rejected -> destroy clone\n      clone->deleteLater();\n      return;\n    }\n    // add to list (below selected one)\n    _config->channelList()->add(clone, row+1);\n  } else if (channel->is<DMRChannel>()) {\n    // clone channel\n    DMRChannel *clone = channel->clone()->as<DMRChannel>();\n    // open editor\n    DMRChannelDialog dialog(_config);\n    dialog.setChannel(clone);\n    if (QDialog::Accepted != dialog.exec()) {\n      clone->deleteLater();\n      return;\n    }\n    // add to list (below selected one)\n    _config->channelList()->add(clone, row+1);\n  }\n}\n\nvoid\nChannelListView::onRemChannel() {\n  if (! ui->channelTableView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete channel\"),\n          tr(\"Cannot delete channel: You have to select a channel first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->channelTableView->selection();\n  int rowcount = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->channelList()->channel(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete channel?\"), tr(\"Delete channel %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete channel?\"), tr(\"Delete %1 channels?\").arg(rowcount)))\n      return;\n  }\n\n  // collect all selected channels\n  // need to collect them first as rows change when deleting channels\n  QList<Channel *> channels; channels.reserve(rowcount);\n  for(int row=rows.first; row<=rows.second; row++)\n    channels.push_back(_config->channelList()->channel(row));\n  // remove channels\n  foreach (Channel *channel, channels)\n    _config->channelList()->del(channel);\n}\n\nvoid\nChannelListView::onEditChannel(unsigned row) {\n  Channel *channel = _config->channelList()->channel(row);\n  if (! channel)\n    return;\n  if (channel->is<FMChannel>()) {\n    FMChannelDialog dialog(_config);\n    dialog.setChannel(channel->as<FMChannel>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n  } else if (channel->is<DMRChannel>()) {\n    DMRChannelDialog dialog(_config);\n    dialog.setChannel(channel->as<DMRChannel>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n  } else if (channel->is<AMChannel>()) {\n    AMChannelDialog dialog(_config);\n    dialog.setChannel(channel->as<AMChannel>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n  } else if (channel->is<M17Channel>()) {\n    M17ChannelDialog dialog(_config);\n    dialog.setChannel(channel->as<M17Channel>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n  }\n}\n\nvoid\nChannelListView::loadChannelListSectionState() {\n  Settings settings;\n  ui->channelTableView->header()->restoreState(settings.headerState(\"channelList\"));\n}\nvoid\nChannelListView::storeChannelListSectionState() {\n  Settings settings;\n  settings.setHeaderState(\"channelList\", ui->channelTableView->header()->saveState());\n}\n\n"
  },
  {
    "path": "src/channellistview.hh",
    "content": "#ifndef CHANNELLISTVIEW_HH\n#define CHANNELLISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nclass QSortFilterProxyModel;\n\nnamespace Ui {\n  class ChannelListView;\n}\n\nclass ChannelListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ChannelListView(Config *config, QWidget *parent = nullptr);\n  ~ChannelListView();\n\nprotected slots:\n  void onAddFMChannel();\n  void onAddAMChannel();\n  //void onAddM17Channel();\n  void onAddDMRChannel();\n  void onCloneChannel();\n  void onRemChannel();\n  void onEditChannel(unsigned row);\n  void loadChannelListSectionState();\n  void storeChannelListSectionState();\n\nprivate:\n  Ui::ChannelListView *ui;\n  Config *_config;\n};\n\n#endif // CHANNELLISTVIEW_HH\n"
  },
  {
    "path": "src/channellistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ChannelListView</class>\n <widget class=\"QWidget\" name=\"ChannelListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>547</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"ConfigObjectTableView\" name=\"channelTableView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addChannel\">\n       <property name=\"text\">\n        <string>Add Channel ...</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+A</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"cloneChannel\">\n       <property name=\"text\">\n        <string>Clone Channel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+C</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remChannel\">\n       <property name=\"text\">\n        <string>Delete Channel</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionAddFMChannel\">\n   <property name=\"text\">\n    <string>Add FM Channel</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds a new FM channel</string>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::MenuRole::NoRole</enum>\n   </property>\n  </action>\n  <action name=\"actionAddDMRChannel\">\n   <property name=\"text\">\n    <string>Add DMR Channel</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds a new DMR channel.</string>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::MenuRole::NoRole</enum>\n   </property>\n  </action>\n  <action name=\"actionAddAMChannel\">\n   <property name=\"text\">\n    <string>Add AM Channel</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds a new AM channel.</string>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::MenuRole::NoRole</enum>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectTableView</class>\n   <extends>QWidget</extends>\n   <header>configobjecttableview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/channelselectiondialog.cc",
    "content": "#include \"channelselectiondialog.hh\"\n#include \"channel.hh\"\n#include \"channelcombobox.hh\"\n#include \"configreference.hh\"\n\n#include <QDialogButtonBox>\n#include <QLabel>\n#include <QVBoxLayout>\n#include <QListWidget>\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelSelectionDialog\n * ********************************************************************************************* */\nChannelSelectionDialog::ChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel, QWidget *parent)\n  : QDialog(parent)\n{\n  _channel = new ChannelComboBox(lst, includeSelectedChannel);\n  QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);\n  connect(bbox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(bbox, SIGNAL(rejected()), this, SLOT(reject()));\n\n  QVBoxLayout *layout = new QVBoxLayout();\n  layout->addWidget(new QLabel(tr(\"Select a channel:\")));\n  layout->addWidget(_channel);\n  layout->addWidget(bbox);\n  setLayout(layout);\n}\n\nChannel *\nChannelSelectionDialog::channel() const {\n  return _channel->channel();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of MultiChannelSelectionDialog\n * ********************************************************************************************* */\nMultiChannelSelectionDialog::MultiChannelSelectionDialog(\n  ChannelList *lst, bool includeSelectedChannel, bool digitalOnly, ChannelRefList *exclude,\n  QWidget *parent)\n  : QDialog(parent)\n{\n  _channel = new QListWidget();\n  if (includeSelectedChannel) {\n    QListWidgetItem *item = new QListWidgetItem(tr(\"[Selected]\"));\n    item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);\n    item->setData(Qt::UserRole, QVariant::fromValue(SelectedChannel::get()));\n    item->setCheckState(Qt::Unchecked);\n    _channel->addItem(item);\n  }\n  for (int i=0; i<lst->count(); i++) {\n    Channel *channel = lst->channel(i);\n    if (digitalOnly && channel->is<AnalogChannel>())\n      continue;\n    if (exclude && exclude->has(channel))\n      continue;\n    QListWidgetItem *item = new QListWidgetItem(channel->name());\n    item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);\n    item->setData(Qt::UserRole, QVariant::fromValue(channel));\n    item->setCheckState(Qt::Unchecked);\n    _channel->addItem(item);\n  }\n  QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);\n  connect(bbox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(bbox, SIGNAL(rejected()), this, SLOT(reject()));\n\n  QVBoxLayout *layout = new QVBoxLayout();\n  layout->addWidget(new QLabel(tr(\"Select a channel:\")));\n  layout->addWidget(_channel);\n  layout->addWidget(bbox);\n  setLayout(layout);\n}\n\nQList<Channel *>\nMultiChannelSelectionDialog::channel() const {\n  QList<Channel *> channels;\n  for (int i=0; i<_channel->count(); i++) {\n    if (Qt::Checked == _channel->item(i)->checkState())\n      channels.push_back(_channel->item(i)->data(Qt::UserRole).value<Channel *>());\n  }\n  return channels;\n}\n\n"
  },
  {
    "path": "src/channelselectiondialog.hh",
    "content": "#ifndef CHANNELSELECTIONDIALOG_HH\n#define CHANNELSELECTIONDIALOG_HH\n\n#include <QDialog>\n\nclass Channel;\nclass ChannelList;\nclass ChannelComboBox;\nclass QListWidget;\nclass ChannelRefList;\n\nclass ChannelSelectionDialog: public QDialog\n{\n  Q_OBJECT\n\npublic:\n  ChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel=false, QWidget *parent=nullptr);\n\n  Channel *channel() const;\n\nprotected:\n  ChannelComboBox *_channel;\n};\n\n\nclass MultiChannelSelectionDialog: public QDialog\n{\n  Q_OBJECT\n\npublic:\n  MultiChannelSelectionDialog(ChannelList *lst, bool includeSelectedChannel=false,\n                              bool digitalOnly=false, ChannelRefList *exclude=nullptr,\n                              QWidget *parent=nullptr);\n\n  QList<Channel *> channel() const;\n\nprotected:\n  QListWidget *_channel;\n};\n\n\n#endif // CHANNELSELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/channelvalidator.cc",
    "content": "#include \"channelvalidator.hh\"\n#include \"channel.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelValidator\n * ********************************************************************************************* */\nChannelValidator::ChannelValidator(ChannelList *lst, QObject *parent)\n  : QValidator(parent), _channels(lst)\n{\n  // pass...\n}\n\nQValidator::State\nChannelValidator::validate(QString &text, int &pos) const {\n  Q_UNUSED(pos);\n  if (text.size()<1)\n    return QValidator::Intermediate;\n\n  QValidator::State res = QValidator::Invalid;\n  for (int i=0; i<_channels->count(); i++) {\n    if (0 == text.compare(_channels->channel(i)->name(), Qt::CaseInsensitive))\n      return QValidator::Acceptable;\n    if (_channels->channel(i)->name().startsWith(text, Qt::CaseInsensitive))\n      res = QValidator::Intermediate;\n  }\n  return res;\n}\n\n\n"
  },
  {
    "path": "src/channelvalidator.hh",
    "content": "#ifndef CHANNELVALIDATOR_HH\n#define CHANNELVALIDATOR_HH\n\n#include <QValidator>\n\nclass ChannelList;\n\nclass ChannelValidator: public QValidator\n{\n  Q_OBJECT\n\npublic:\n  ChannelValidator(ChannelList *lst, QObject *parent=nullptr);\n\n  QValidator::State validate(QString &input, int &pos) const;\n\nprotected:\n  ChannelList *_channels;\n};\n\n\n\n#endif // CHANNELVALIDATOR_HH\n"
  },
  {
    "path": "src/collapsablewidget.cc",
    "content": "#include \"collapsablewidget.hh\"\n\n#include <QPropertyAnimation>\n#include <QApplication>\n\n\nCollapsableWidget::CollapsableWidget(QWidget *parent)\n  : QToolButton(parent), _content(nullptr)\n{\n  setCheckable(true);\n  setStyleSheet(\"background:none\");\n  setToolButtonStyle(Qt::ToolButtonTextBesideIcon);\n  setIconSize(QSize(8, 8));\n  setFont(QApplication::font());\n  hideContent();\n  setArrowType(Qt::ArrowType::RightArrow);\n  connect(this, &QToolButton::toggled, [=](bool checked) {\n    setArrowType(checked ? Qt::ArrowType::DownArrow : Qt::ArrowType::RightArrow);\n    _content != nullptr && checked ? showContent() : hideContent();\n  });\n}\n\n\nvoid\nCollapsableWidget::setContent(QWidget *content) {\n  assert(content != nullptr);\n  _content = content;\n  auto animation = new QPropertyAnimation(_content, \"maximumHeight\"); // QObject with auto delete\n  animation->setStartValue(0);\n  animation->setEasingCurve(QEasingCurve::InOutQuad);\n  animation->setDuration(300);\n  animation->setEndValue(content->geometry().height() + 10);\n  _animator.addAnimation(animation);\n  if (!isChecked())\n    content->setMaximumHeight(0);\n}\n\nvoid\nCollapsableWidget::hideContent() {\n  _animator.setDirection(QAbstractAnimation::Backward);\n  _animator.start();\n}\n\nvoid\nCollapsableWidget::showContent() {\n  _animator.setDirection(QAbstractAnimation::Forward);\n  _animator.start();\n}\n"
  },
  {
    "path": "src/collapsablewidget.hh",
    "content": "#ifndef COLLAPSABLEWIDGET_HH\n#define COLLAPSABLEWIDGET_HH\n\n#include <QToolButton>\n#include <QParallelAnimationGroup>\n\nclass CollapsableWidget : public QToolButton\n{\n  Q_OBJECT\n\npublic:\n  explicit CollapsableWidget(QWidget *parent=nullptr);\n\n  void setContent(QWidget *content);\n\npublic slots:\n  void hideContent();\n  void showContent();\n\nprivate:\n  QWidget *_content;\n  QParallelAnimationGroup _animator;\n};\n\n#endif // COLLAPSABLEWIDGET_HH\n"
  },
  {
    "path": "src/configitemwrapper.cc",
    "content": "#include \"configitemwrapper.hh\"\n#include <cmath>\n#include \"logger.hh\"\n#include <QColor>\n#include <QPalette>\n#include <QWidget>\n\n\n/* ********************************************************************************************* *\n * Implementation of GenericListWrapper\n * ********************************************************************************************* */\nGenericListWrapper::GenericListWrapper(AbstractConfigObjectList *list, QObject *parent)\n  : QAbstractListModel(parent), _list(list)\n{\n  if (nullptr == _list)\n    return;\n\n  connect(_list, SIGNAL(destroyed(QObject*)), this, SLOT(onListDeleted()));\n  connect(_list, SIGNAL(elementAdded(int)), this, SLOT(onItemAdded(int)));\n  connect(_list, SIGNAL(elementModified(int)), this, SLOT(onItemModified(int)));\n  connect(_list, SIGNAL(elementRemoved(int)), this, SLOT(onItemRemoved(int)));\n}\n\nint\nGenericListWrapper::rowCount(const QModelIndex &index) const {\n  Q_UNUSED(index)\n  if (nullptr == _list)\n    return 0;\n  return _list->count();\n}\n\nint\nGenericListWrapper::columnCount(const QModelIndex &index) const {\n  Q_UNUSED(index)\n  if (nullptr == _list)\n    return 0;\n  return 1;\n}\n\nQt::ItemFlags\nGenericListWrapper::flags(const QModelIndex &index) const {\n  if (index.isValid())\n    return QAbstractListModel::flags(index) | Qt::ItemIsDragEnabled;\n  return QAbstractListModel::flags(index) | Qt::ItemIsDropEnabled;\n}\n\nQt::DropActions\nGenericListWrapper::supportedDropActions() const {\n  return Qt::MoveAction;\n}\n\nbool\nGenericListWrapper::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,\n                             const QModelIndex &destinationParent, int destinationChild)\n{\n  logDebug() << \"Move \" << count << \" rows from \" << sourceRow << \" to \" << destinationChild;\n\n  beginMoveRows(sourceParent, sourceRow, sourceRow+count-1,\n                destinationParent, destinationChild);\n  bool success = _list->move(sourceRow, count, destinationChild);\n  endMoveRows();\n\n  return success;\n}\n\n\nvoid\nGenericListWrapper::onListDeleted() {\n  beginResetModel();\n  _list = nullptr;\n  endResetModel();\n}\n\nvoid\nGenericListWrapper::onItemAdded(int idx) {\n  beginInsertRows(QModelIndex(), idx, idx);\n  endInsertRows();\n}\n\nvoid\nGenericListWrapper::onItemRemoved(int idx) {\n  beginRemoveRows(QModelIndex(), idx, idx);\n  //logDebug() << \"Signal removal of item at idx=\" << idx;\n  endRemoveRows();\n}\n\nvoid\nGenericListWrapper::onItemModified(int idx) {\n  emit dataChanged(index(idx),index(idx));\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GenericTableWrapper\n * ********************************************************************************************* */\nGenericTableWrapper::GenericTableWrapper(AbstractConfigObjectList *list, QObject *parent)\n  : QAbstractTableModel(parent), _list(list), _insertRow(-1)\n{\n  if (nullptr == _list)\n    return;\n\n  connect(_list, SIGNAL(destroyed(QObject*)), this, SLOT(onListDeleted()));\n  connect(_list, SIGNAL(elementAdded(int)), this, SLOT(onItemAdded(int)));\n  connect(_list, SIGNAL(elementModified(int)), this, SLOT(onItemModified(int)));\n  connect(_list, SIGNAL(elementRemoved(int)), this, SLOT(onItemRemoved(int)));\n}\n\nint\nGenericTableWrapper::rowCount(const QModelIndex &index) const {\n  Q_UNUSED(index)\n  if (nullptr == _list)\n    return 0;\n  return _list->count();\n}\n\nQt::ItemFlags\nGenericTableWrapper::flags(const QModelIndex &index) const {\n  if (index.isValid())\n    return QAbstractTableModel::flags(index) | Qt::ItemIsDragEnabled;\n  return QAbstractTableModel::flags(index) | Qt::ItemIsDropEnabled;\n}\n\nQt::DropActions\nGenericTableWrapper::supportedDropActions() const {\n  return Qt::MoveAction;\n}\n\nbool\nGenericTableWrapper::insertRows(int row, int count, const QModelIndex &parent) {\n  Q_UNUSED(parent); Q_UNUSED(count);\n\n  if (-1 == _insertRow) {\n    _insertRow = row;\n    return true;\n  }\n  return false;\n}\n\nbool\nGenericTableWrapper::removeRows(int row, int count, const QModelIndex &parent) {\n  Q_UNUSED(parent);\n\n  if (-1 == _insertRow)\n    return false;\n\n  bool success = moveRows(QModelIndex(), row, count,\n                          QModelIndex(), _insertRow);\n  _insertRow = -1;\n  return success;\n}\n\nbool\nGenericTableWrapper::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,\n                             const QModelIndex &destinationParent, int destinationChild)\n{\n  logDebug() << \"Move \" << count << \" rows from \" << sourceRow << \" to \" << destinationChild;\n\n  beginMoveRows(sourceParent, sourceRow, sourceRow+count-1,\n                destinationParent, destinationChild);\n  bool success = _list->move(sourceRow, count, destinationChild);\n  endMoveRows();\n\n  return success;\n}\n\nbool\nGenericTableWrapper::canDropMimeData(const QMimeData *data, Qt::DropAction action,\n                                 int row, int column, const QModelIndex &parent) const\n{\n  if (0 != column)\n    return false;\n  return QAbstractTableModel::canDropMimeData(data, action, row, column, parent);\n}\n\nvoid\nGenericTableWrapper::onListDeleted() {\n  beginResetModel();\n  _list = nullptr;\n  endResetModel();\n}\n\nvoid\nGenericTableWrapper::onItemAdded(int idx) {\n  beginInsertRows(QModelIndex(), idx, idx);\n  endInsertRows();\n}\n\nvoid\nGenericTableWrapper::onItemRemoved(int idx) {\n  beginRemoveRows(QModelIndex(), idx, idx);\n  //logDebug() << \"Signal removal of item at idx=\" << idx;\n  endRemoveRows();\n}\n\nvoid\nGenericTableWrapper::onItemModified(int idx) {\n  emit dataChanged(index(idx,0),index(idx,columnCount()-1));\n}\n\nQString\nGenericTableWrapper::formatExtensions(int idx) const {\n  if (idx >= _list->count())\n    return QString();\n\n  ConfigObject *item = _list->get(idx);\n  QStringList extensions;\n  auto metaObj = item->metaObject();\n  for (int i=QObject::staticMetaObject.propertyCount(); i<metaObj->propertyCount(); i++) {\n    auto prop = metaObj->property(i);\n    if (QMetaType::UnknownType == prop.userType())\n      continue;\n    QMetaType type(prop.userType());\n    if (! (QMetaType::PointerToQObject & type.flags()))\n      continue;\n    const QMetaObject *propType = type.metaObject();\n    if (! propType->inherits(&ConfigExtension::staticMetaObject))\n      continue;\n    if (prop.read(item).isNull())\n      continue;\n    extensions.append(prop.name());\n  }\n  return extensions.join(\", \");\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelListWrapper\n * ********************************************************************************************* */\nChannelListWrapper::ChannelListWrapper(ChannelList *list, QObject *parent)\n  : GenericTableWrapper(list, parent)\n{\n  // pass...\n}\n\nint\nChannelListWrapper::columnCount(const QModelIndex &index) const {\n  Q_UNUSED(index);\n  return 22;\n}\n\nQVariant\nChannelListWrapper::data(const QModelIndex &index, int role) const {\n  if (nullptr == _list)\n    return QVariant();\n\n  if ((! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n\n  if (Qt::ForegroundRole == role) {\n    const QPalette &palette = qobject_cast<QWidget *>(QObject::parent())->palette();\n    QColor active   = palette.color(QPalette::Active, QPalette::Text);\n    QColor inactive = palette.color(QPalette::Inactive, QPalette::Text);\n    bool isDigital = dynamic_cast<ChannelList *>(_list)->channel(index.row())->is<DMRChannel>();\n    switch(index.column()) {\n    case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9:\n      return active;\n    case 11: case 12: case 13: case 14:\n      return (isDigital ? active : inactive);\n    case 15:\n      return active;\n    case 16:\n      return (isDigital ? active : inactive);\n    case 17: case 18: case 19: case 20:\n      return (isDigital ? inactive : active);\n    }\n  }\n\n  if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role))\n    return QVariant();\n\n  Channel *channel = dynamic_cast<ChannelList *>(_list)->channel(index.row());\n\n  switch (index.column()) {\n  case 0:\n    if (channel->is<DMRChannel>())\n      return tr(\"DMR\");\n    else if (channel->is<FMChannel>())\n      return tr(\"FM\");\n    else if (channel->is<AMChannel>())\n      return tr(\"AM\");\n    else\n      return channel->metaObject()->className();\n  case 1:\n    return channel->name();\n  case 2:\n    return channel->rxFrequency().format(Frequency::Unit::MHz);\n  case 3:\n    return channel->txFrequency().format(Frequency::Unit::MHz);\n  case 4:\n    if (channel->defaultPower())\n      return tr(\"[Default]\");\n    switch (channel->power()) {\n    case Channel::Power::Max: return tr(\"Max\"); break;\n    case Channel::Power::High: return tr(\"High\"); break;\n    case Channel::Power::Mid: return tr(\"Mid\"); break;\n    case Channel::Power::Low: return tr(\"Low\"); break;\n    case Channel::Power::Min: return tr(\"Min\"); break;\n    }\n    break;\n  case 5:\n    if (channel->defaultTimeout())\n      return tr(\"[Default]\");\n    if (channel->timeoutDisabled())\n      return tr(\"Off\");\n    return channel->timeout().format();\n  case 6:\n    return channel->rxOnly() ? tr(\"On\") : tr(\"Off\");\n  case 7:\n    if (DMRChannel *dmr = channel->as<DMRChannel>()) {\n      switch (dmr->admit()) {\n      case DMRChannel::Admit::Always: return tr(\"Always\"); break;\n      case DMRChannel::Admit::Free: return tr(\"Free\"); break;\n      case DMRChannel::Admit::ColorCode: return tr(\"Color\"); break;\n      }\n    } else if (FMChannel *fm = channel->as<FMChannel>()) {\n      switch (fm->admit()) {\n      case FMChannel::Admit::Always: return tr(\"Always\"); break;\n      case FMChannel::Admit::Free: return tr(\"Free\"); break;\n      case FMChannel::Admit::Tone: return tr(\"Tone\"); break;\n      }\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 8:\n    if (channel->scanList()) {\n      return channel->scanList()->name();\n    } else {\n      return QString(\"-\");\n    }\n  case 9: { // Collect zones, the channel is a member of\n      QStringList zones;\n      for(int i=0;i<channel->config()->zones()->count(); i++) {\n        Zone *zone = channel->config()->zones()->zone(i);\n        if (zone->contains(channel))\n          zones.append(zone->name());\n      }\n      return zones.join(\", \");\n    } break;\n  case 10:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      return digi->colorCode();\n    } else if (M17Channel *m17 = channel->as<M17Channel>()) {\n            return m17->accessNumber();\n    } else if (channel->is<FMChannel>()) {\n      return tr(\"[None]\");\n    }\n    break;\n  case 11:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      return (DMRChannel::TimeSlot::TS1 == digi->timeSlot()) ? 1 : 2;\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 12:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      if (digi->groupList()) {\n        return digi->groupList()->name();\n      } else {\n        return QString(\"-\");\n      }\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 13:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      if (digi->contact())\n        return digi->contact()->name();\n      else\n        return QString(\"-\");\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 14:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      if ((nullptr == digi->radioId()) || (DefaultRadioID::get() == digi->radioId()))\n        return tr(\"[Default]\");\n      return digi->radioId()->name();\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 15:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      if (digi->aprs())\n        return digi->aprs()->name();\n      else\n        return QString(\"-\");\n    } else if (FMChannel *analog = channel->as<FMChannel>()) {\n      if (analog->aprs())\n        return analog->aprs()->name();\n      else\n        return QString(\"-\");\n    } else {\n      return QString(\"-\");\n    }\n    break;\n  case 16:\n    if (DMRChannel *digi = channel->as<DMRChannel>()) {\n      if (nullptr == digi->roaming())\n        return QString(\"-\");\n      else if (DefaultRoamingZone::get() == digi->roaming())\n        return tr(\"[Default]\");\n      return digi->roaming()->name();\n    } else {\n      return tr(\"[None]\");\n    }\n    break;\n  case 17:\n    if (channel->is<DMRChannel>() || channel->is<M17Channel>()) {\n      return tr(\"[None]\");\n    } else if (FMChannel *fm = channel->as<FMChannel>()) {\n      if (fm->defaultSquelch())\n        return tr(\"[Default]\");\n      if (fm->squelchDisabled())\n        return tr(\"Open\");\n      else\n        return fm->squelch().value();\n    } else if (AMChannel *am = channel->as<AMChannel>()) {\n      if (am->defaultSquelch())\n        return tr(\"[Default]\");\n      if (am->squelchDisabled())\n        return tr(\"Open\");\n      else\n        return am->squelch().value();\n    }\n    break;\n  case 18:\n    if (channel->is<DMRChannel>() || channel->is<AMChannel>()) {\n      return tr(\"[None]\");\n    } else if (FMChannel *analog = channel->as<FMChannel>()) {\n      return analog->rxTone().format();\n    }\n    break;\n  case 19:\n    if (channel->is<DMRChannel>() || channel->is<AMChannel>()) {\n      return tr(\"[None]\");\n    } else if (FMChannel *analog = channel->as<FMChannel>()) {\n      return analog->txTone().format();\n    }\n    break;\n  case 20:\n    if (channel->is<DMRChannel>() || channel->is<AMChannel>()) {\n      return tr(\"[None]\");\n    } else if (FMChannel *analog = channel->as<FMChannel>()) {\n      if (FMChannel::Bandwidth::Wide == analog->bandwidth()) {\n        return tr(\"Wide\");\n      } else\n        return tr(\"Narrow\");\n    }\n    break;\n  case 21: {\n      auto exts = formatExtensions(index.row());\n      if (exts.isEmpty())\n        return tr(\"[None]\");\n      return exts;\n    }\n  default:\n    break;\n  }\n\n  return QVariant();\n}\n\nQVariant\nChannelListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation))\n    return QVariant();\n  switch (section) {\n  case 0: return tr(\"Type\");\n  case 1: return tr(\"Name\");\n  case 2: return tr(\"Rx Frequency\");\n  case 3: return tr(\"Tx Frequency\");\n  case 4: return tr(\"Power\");\n  case 5: return tr(\"Timeout\");\n  case 6: return tr(\"Rx Only\");\n  case 7: return tr(\"Admit\");\n  case 8: return tr(\"Scanlist\");\n  case 9: return tr(\"Zones\");\n  case 10: return tr(\"CC\");\n  case 11: return tr(\"TS\");\n  case 12: return tr(\"RX Group List\");\n  case 13: return tr(\"TX Contact\");\n  case 14: return tr(\"DMR ID\");\n  case 15: return tr(\"GPS/APRS\");\n  case 16: return tr(\"Roaming\");\n  case 17: return tr(\"Squelch\");\n  case 18: return tr(\"Rx Tone\");\n  case 19: return tr(\"Tx Tone\");\n  case 20: return tr(\"Bandwidth\");\n  case 21: return tr(\"Extensions\");\n    default:\n      break;\n  }\n  return QVariant();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ChannelRefListWrapper\n * ********************************************************************************************* */\nChannelRefListWrapper::ChannelRefListWrapper(ChannelRefList *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nChannelRefListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n  return _list->get(index.row())->as<Channel>()->name();\n}\n\nQVariant\nChannelRefListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role))\n    return QVariant();\n  return tr(\"Channel\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingChannelListWrapper\n * ********************************************************************************************* */\nRoamingChannelListWrapper::RoamingChannelListWrapper(RoamingChannelList *list, QObject *parent)\n  : GenericTableWrapper(list, parent)\n{\n  // pass...\n}\n\nint\nRoamingChannelListWrapper::columnCount(const QModelIndex &index) const {\n  Q_UNUSED(index);\n  return 7;\n}\n\nQVariant\nRoamingChannelListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row() >= _list->count()))\n    return QVariant();\n\n  RoamingChannel *ch = _list->get(index.row())->as<RoamingChannel>();\n\n  // Dispatch by column\n  switch (index.column()) {\n  case 0: return ch->name();\n  case 1: return ch->rxFrequency().format(Frequency::Unit::MHz);\n  case 2: return ch->txFrequency().format(Frequency::Unit::MHz);\n  case 3:\n    if (ch->colorCodeOverridden())\n      return ch->colorCode();\n    return tr(\"[Selected]\");\n  case 4:\n    if (ch->timeSlotOverridden()) {\n      switch(ch->timeSlot()) {\n      case DMRChannel::TimeSlot::TS1: return 1;\n      case DMRChannel::TimeSlot::TS2: return 2;\n      }\n    }\n    return tr(\"[Selected]\");\n  case 5: {\n      QStringList zones;\n      for(int i=0;i<ch->config()->roamingZones()->count(); i++) {\n        RoamingZone *zone = ch->config()->roamingZones()->zone(i);\n        if (zone->contains(ch))\n          zones.append(zone->name());\n      }\n      return zones.join(\", \");\n    } break;\n\n  case 6: {\n    auto exts = formatExtensions(index.row());\n    if (exts.isEmpty())\n      return tr(\"[None]\");\n    return exts;\n  }\n\n  default: break;\n  }\n\n  return QVariant();\n}\n\nQVariant\nRoamingChannelListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role))\n    return QVariant();\n  switch (section) {\n  case 0: return tr(\"Name\");\n  case 1: return tr(\"RX Frequency\");\n  case 2: return tr(\"TX Frequency\");\n  case 3: return tr(\"CC\");\n  case 4: return tr(\"TS\");\n  case 5: return tr(\"Zones\");\n  case 6: return tr(\"Extensions\");\n  default: break;\n  }\n  return QVariant();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingChannelRefListWrapper\n * ********************************************************************************************* */\nRoamingChannelRefListWrapper::RoamingChannelRefListWrapper(RoamingChannelRefList *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nRoamingChannelRefListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row() >= _list->count()))\n    return QVariant();\n  return _list->get(index.row())->as<RoamingChannel>()->name();\n}\n\nQVariant\nRoamingChannelRefListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role))\n    return QVariant();\n  return tr(\"Roaming Channel\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ContactListWrapper\n * ********************************************************************************************* */\nContactListWrapper::ContactListWrapper(ContactList *list, QObject *parent)\n  : GenericTableWrapper(list, parent)\n{\n  // pass...\n}\n\nint\nContactListWrapper::columnCount(const QModelIndex &index) const {\n  Q_UNUSED(index);\n  return 5;\n}\n\nQVariant\nContactListWrapper::data(const QModelIndex &index, int role) const {\n  if ((!index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n\n  if (Qt::DisplayRole == role) {\n    Contact *contact = _list->get(index.row())->as<Contact>();\n    if (contact->is<DTMFContact>()) {\n      DTMFContact *dtmf = contact->as<DTMFContact>();\n      switch (index.column()) {\n        case 0:\n          return tr(\"DTMF\");\n        case 1:\n          return dtmf->name();\n        case 2:\n          return dtmf->number();\n        case 3:\n          return (dtmf->ring() ? tr(\"On\") : tr(\"Off\"));\n        case 4:\n          return formatExtensions(index.row());\n        default:\n          return QVariant();\n      }\n    } else if (contact->is<DMRContact>()) {\n      DMRContact *dmr = contact->as<DMRContact>();\n      switch (index.column()) {\n      case 0:\n        switch (dmr->type()) {\n        case DMRContact::PrivateCall: return tr(\"Private Call\");\n        case DMRContact::GroupCall: return tr(\"Group Call\");\n        case DMRContact::AllCall: return tr(\"All Call\");\n        }\n      break;\n      case 1:\n      return dmr->name();\n      case 2:\n      return dmr->number();\n      case 3:\n      return (dmr->ring() ? tr(\"On\") : tr(\"Off\"));\n      case 4: {\n        auto exts = formatExtensions(index.row());\n        if (exts.isEmpty())\n          return tr(\"[None]\");\n        return exts;\n      }\n      default:\n      return QVariant();\n      }\n    } else if (contact->is<M17Contact>()) {\n      M17Contact *m17 = contact->as<M17Contact>();\n      switch (index.column()) {\n      case 0:\n        return tr(\"M17\");\n      case 1:\n        return m17->name();\n      case 2:\n        if (m17->isBroadcast())\n          return tr(\"[Broadcast]\");\n        return m17->call();\n      case 3:\n        return (m17->ring() ? tr(\"On\") : tr(\"Off\"));\n      default:\n        return QVariant();\n      }\n    }\n  }\n\n  return QVariant();\n}\n\n\nQVariant\nContactListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole != role) || (Qt::Horizontal != orientation)) {\n    return QVariant();\n  }\n  if (0 == section) {\n    return tr(\"Type\");\n  } else if (1 == section) {\n    return tr(\"Name\");\n  } else if (2 == section) {\n    return tr(\"Number\");\n  } else if (3 == section) {\n    return tr(\"RX Tone\");\n  } else if (4 == section) {\n    return tr(\"Extensions\");\n  }\n  return QVariant();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneListWrapper\n * ********************************************************************************************* */\nZoneListWrapper::ZoneListWrapper(ZoneList *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nZoneListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column()))\n    return QVariant();\n  Zone *zone = _list->get(index.row())->as<Zone>();\n  return zone->name();\n}\n\nQVariant\nZoneListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section))\n    return QVariant();\n  return tr(\"Zone\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of PositioningSystemListWrapper\n * ********************************************************************************************* */\nPositioningSystemListWrapper::PositioningSystemListWrapper(PositionReportingSystems *list, QObject *parent)\n  : GenericTableWrapper(list, parent)\n{\n  // pass...\n}\n\nint\nPositioningSystemListWrapper::columnCount(const QModelIndex &idx) const {\n  Q_UNUSED(idx);\n  return 7;\n}\n\nQVariant\nPositioningSystemListWrapper::data(const QModelIndex &index, int role) const {\n  if ((! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n  if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role))\n    return QVariant();\n\n  PositionReportingSystem *sys = _list->get(index.row())->as<PositionReportingSystem>();\n\n  switch (index.column()) {\n  case 0:\n    if (sys->is<DMRAPRSSystem>())\n      return tr(\"DMR\");\n    else if (sys->is<FMAPRSSystem>())\n      return tr(\"APRS\");\n    else\n      return QString(\"Oops!\");\n\n  case 1:\n    return sys->name();\n\n  case 2:\n    if (sys->is<DMRAPRSSystem>()) {\n      if (! sys->as<DMRAPRSSystem>()->hasContact())\n        return tr(\"[None]\");\n      return sys->as<DMRAPRSSystem>()->contact()->name();\n    } else if (sys->is<FMAPRSSystem>())\n      return QString(\"%1-%2\").arg(sys->as<FMAPRSSystem>()->destination())\n          .arg(sys->as<FMAPRSSystem>()->destSSID());\n    break;\n\n  case 3:\n    if (Qt::DisplayRole == role)\n      return sys->period().format();\n    else if (Qt::EditRole == role)\n      return QVariant::fromValue(sys->period());\n    break;\n\n  case 4:\n    if (sys->is<DMRAPRSSystem>()) {\n      if (! sys->as<DMRAPRSSystem>()->hasRevertChannel())\n        return tr(\"[Selected]\");\n      return sys->as<DMRAPRSSystem>()->revertChannel()->name();\n    } else if (sys->is<FMAPRSSystem>()) {\n      if (! sys->as<FMAPRSSystem>()->hasRevertChannel())\n        return tr(\"[Selected]\");\n      return sys->as<FMAPRSSystem>()->revertChannel()->name();\n    }\n  break;\n\n  case 5:\n    if (sys->is<DMRAPRSSystem>())\n      return tr(\"[None]\");\n    else if (sys->is<FMAPRSSystem>())\n      return sys->as<FMAPRSSystem>()->message();\n  break;\n\n  case 6: {\n    auto exts = formatExtensions(index.row());\n    if (exts.isEmpty())\n      return tr(\"[None]\");\n    return exts;\n  }\n\n  default:\n    break;\n  }\n\n  return QVariant();\n}\n\nQVariant\nPositioningSystemListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation))\n    return QVariant();\n  switch (section) {\n  case 0: return tr(\"Type\");\n  case 1: return tr(\"Name\");\n  case 2: return tr(\"Destination\");\n  case 3: return tr(\"Period\");\n  case 4: return tr(\"Channel\");\n  case 5: return tr(\"Message\");\n  case 6: return tr(\"Extensions\");\n  default:\n    break;\n  }\n  return QVariant();\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of ScanListsWrapper\n * ********************************************************************************************* */\nScanListsWrapper::ScanListsWrapper(ScanLists *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nScanListsWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column()))\n    return QVariant();\n  return _list->get(index.row())->as<ScanList>()->name();\n}\n\nQVariant\nScanListsWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section))\n    return QVariant();\n  return tr(\"Scan-List\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GroupListsWrapper\n * ********************************************************************************************* */\nGroupListsWrapper::GroupListsWrapper(RXGroupLists *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nGroupListsWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n  return _list->get(index.row())->as<RXGroupList>()->name();\n}\n\nQVariant\nGroupListsWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role))\n    return QVariant();\n  return tr(\"RX Group Lists\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of GroupListWrapper\n * ********************************************************************************************* */\nGroupListWrapper::GroupListWrapper(RXGroupList *list, QObject *parent)\n  : GenericListWrapper(list->contacts(), parent)\n{\n  // pass...\n}\n\nQVariant\nGroupListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n  return _list->get(index.row())->as<DMRContact>()->name();\n}\n\nQVariant\nGroupListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((0!=section) || (Qt::Horizontal!=orientation) || (Qt::DisplayRole!=role))\n    return QVariant();\n  return tr(\"Contact\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RoamingListWrapper\n * ********************************************************************************************* */\nRoamingListWrapper::RoamingListWrapper(RoamingZoneList *list, QObject *parent)\n  : GenericListWrapper(list, parent)\n{\n  // pass...\n}\n\nQVariant\nRoamingListWrapper::data(const QModelIndex &index, int role) const {\n  if ((Qt::DisplayRole!=role) || (index.row()>=_list->count()) || (0 != index.column()))\n    return QVariant();\n  RoamingZone *zone = _list->get(index.row())->as<RoamingZone>();\n  return tr(\"%1 (containing %2 channels)\").arg(zone->name()).arg(zone->count());\n}\n\nQVariant\nRoamingListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation) || (0 != section))\n    return QVariant();\n  return tr(\"Roaming zone\");\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RadioIdListWrapper\n * ********************************************************************************************* */\nRadioIdListWrapper::RadioIdListWrapper(RadioIDList *list, QObject *parent)\n  : GenericTableWrapper(list, parent)\n{\n  // pass...\n}\n\nint\nRadioIdListWrapper::columnCount(const QModelIndex &idx) const {\n  Q_UNUSED(idx);\n  return 4;\n}\n\nQVariant\nRadioIdListWrapper::data(const QModelIndex &index, int role) const {\n  if ((! index.isValid()) || (index.row()>=_list->count()))\n    return QVariant();\n  if ((Qt::DisplayRole!=role) && (Qt::EditRole!=role))\n    return QVariant();\n\n  DMRRadioID *id = _list->get(index.row())->as<DMRRadioID>();\n\n  switch (index.column()) {\n  case 0:\n    return (\"DMR\");\n  case 1:\n    return id->name();\n  case 2:\n    return id->number();\n  case 3:{\n    auto exts = formatExtensions(index.row());\n    if (exts.isEmpty())\n      return tr(\"[None]\");\n    return exts;\n  }\n  default:\n    break;\n  }\n\n  return QVariant();\n}\n\nQVariant\nRadioIdListWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  if ((Qt::DisplayRole!=role) || (Qt::Horizontal!=orientation))\n    return QVariant();\n  switch (section) {\n  case 0: return tr(\"Type\");\n  case 1: return tr(\"Name\");\n  case 2: return tr(\"Number\");\n  case 3: return tr(\"Extensions\");\n  default:\n    break;\n  }\n  return QVariant();\n}\n\n\n"
  },
  {
    "path": "src/configitemwrapper.hh",
    "content": "#ifndef CONFIG_ITEM_WRAPPER_HH\n#define CONFIG_ITEM_WRAPPER_HH\n\n#include \"config.hh\"\n#include <QAbstractTableModel>\n\nclass GenericListWrapper: public QAbstractListModel\n{\n  Q_OBJECT\n\nprotected:\n  GenericListWrapper(AbstractConfigObjectList *list, QObject *parent=nullptr);\n\npublic:\n  // QAbstractListModel interface\n  /** Implements QAbstractTableModel, returns number of rows. */\n  int rowCount(const QModelIndex &index) const;\n  /** Implements QAbstractTableModel, returns number of columns. */\n  int columnCount(const QModelIndex &index) const;\n\n  Qt::ItemFlags flags(const QModelIndex &index) const;\n\n  Qt::DropActions supportedDropActions() const;\n\n  bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,\n                const QModelIndex &destinationParent, int destinationChild);\n\nsignals:\n  /** Gets emitted once the table has been changed. */\n  void modified();\n\nprotected slots:\n  /** Internal used callback on deleted config. */\n  void onListDeleted();\n  /** Internal callback on added items. */\n  void onItemAdded(int idx);\n  /** Internal callback on deleted channels. */\n  void onItemRemoved(int idx);\n  /** Internal callback on modified channels. */\n  void onItemModified(int idx);\n\nprotected:\n  /** Holds a weak reference to the list object. */\n  AbstractConfigObjectList *_list;\n};\n\n\nclass GenericTableWrapper: public QAbstractTableModel\n{\n  Q_OBJECT\n\nprotected:\n  GenericTableWrapper(AbstractConfigObjectList *list, QObject *parent=nullptr);\n\npublic:\n  // QAbstractTableModel interface\n  /** Implements QAbstractTableModel, returns number of rows. */\n  int rowCount(const QModelIndex &index) const;\n\n  Qt::ItemFlags flags(const QModelIndex &index) const;\n\n  Qt::DropActions supportedDropActions() const;\n\n  bool insertRows(int row, int count, const QModelIndex &parent);\n  bool removeRows(int row, int count, const QModelIndex &parent);\n  bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count,\n                const QModelIndex &destinationParent, int destinationChild);\n\n  bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const;\n\nsignals:\n  /** Gets emitted once the table has been changed. */\n  void modified();\n\nprotected slots:\n  /** Internal used callback on deleted config. */\n  void onListDeleted();\n  /** Internal used callback on adding an item. */\n  void onItemAdded(int idx);\n  /** Internal callback on deleted channels. */\n  void onItemRemoved(int idx);\n  /** Internal callback on modified channels. */\n  void onItemModified(int idx);\n\nprotected:\n  /** Returns a string containing all extension properties set. */\n  QString formatExtensions(int idx) const;\n\nprotected:\n  /** Holds a weak reference to the list object. */\n  AbstractConfigObjectList *_list;\n  /** Insert index for drag & drop move. */\n  int _insertRow;\n};\n\n\nclass ChannelListWrapper: public GenericTableWrapper\n{\n  Q_OBJECT\n\npublic:\n  ChannelListWrapper(ChannelList *list, QObject *parent=nullptr);\n\npublic:\n  // QAbstractTableModel interface\n  /** Implements QAbstractTableModel, returns number of columns. */\n  int columnCount(const QModelIndex &index) const;\n  /** Implements QAbstractTableModel, returns data at cell. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implements QAbstractTableModel, returns header at section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;\n};\n\n\nclass ChannelRefListWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  ChannelRefListWrapper(ChannelRefList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass RoamingChannelListWrapper: public GenericTableWrapper\n{\n  Q_OBJECT\n\npublic:\n  RoamingChannelListWrapper(RoamingChannelList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractTableModel\n  /** Returns the number of columns, implements the QAbstractTableModel. */\n  int columnCount(const QModelIndex &index) const;\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass RoamingChannelRefListWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  RoamingChannelRefListWrapper(RoamingChannelRefList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass ContactListWrapper: public GenericTableWrapper\n{\n  Q_OBJECT\n\npublic:\n  ContactListWrapper(ContactList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractTableModel\n  /** Returns the number of columns, implements the QAbstractTableModel. */\n  int columnCount(const QModelIndex &index) const;\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Returns the header at given section, implements the QAbstractTableModel. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;\n};\n\n\nclass ZoneListWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  ZoneListWrapper(ZoneList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass PositioningSystemListWrapper: public GenericTableWrapper\n{\n  Q_OBJECT\n\npublic:\n  PositioningSystemListWrapper(PositionReportingSystems *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractTableModel\n  /** Returns the number of columns, implements the QAbstractTableModel. */\n  int columnCount(const QModelIndex &index) const;\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Returns the header at given section, implements the QAbstractTableModel. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;\n};\n\n\nclass ScanListsWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  ScanListsWrapper(ScanLists *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass GroupListsWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  GroupListsWrapper(RXGroupLists *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass GroupListWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  GroupListWrapper(RXGroupList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Implementation of QAbstractListModel, returns the header data at the given section. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;\n};\n\n\nclass RoamingListWrapper: public GenericListWrapper\n{\n  Q_OBJECT\n\npublic:\n  RoamingListWrapper(RoamingZoneList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractListModel\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Returns the header at given section, implements the QAbstractTableModel. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;\n};\n\n\nclass RadioIdListWrapper: public GenericTableWrapper\n{\n  Q_OBJECT\n\npublic:\n  RadioIdListWrapper(RadioIDList *list, QObject *parent=nullptr);\n\npublic:\n  // Implementation of QAbstractTableModel\n  /** Returns the number of columns, implements the QAbstractTableModel. */\n  int columnCount(const QModelIndex &index) const;\n  /** Returns the cell data at given index, implements the QAbstractTableModel. */\n  QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;\n  /** Returns the header at given section, implements the QAbstractTableModel. */\n  QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const;\n};\n\n\n\n#endif // CONFIG_ITEM_WRAPPER_HH\n"
  },
  {
    "path": "src/configmergedialog.cc",
    "content": "#include \"configmergedialog.hh\"\n#include \"ui_configmergedialog.h\"\n#include \"configmergevisitor.hh\"\n#include \"settings.hh\"\n\n\nConfigMergeDialog::ConfigMergeDialog(QWidget *parent) :\n  QDialog(parent),\n  ui(new Ui::ConfigMergeDialog)\n{\n  ui->setupUi(this);\n\n  ui->itemStrategy->setItemData(0, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Ignore));\n  ui->itemStrategy->setItemData(1, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Override));\n  ui->itemStrategy->setItemData(2, QVariant((uint) ConfigMergeVisitor::ItemStrategy::Duplicate));\n\n  ui->setStrategy->setItemData(0, QVariant((uint) ConfigMergeVisitor::SetStrategy::Ignore));\n  ui->setStrategy->setItemData(1, QVariant((uint) ConfigMergeVisitor::SetStrategy::Override));\n  ui->setStrategy->setItemData(2, QVariant((uint) ConfigMergeVisitor::SetStrategy::Duplicate));\n  ui->setStrategy->setItemData(3, QVariant((uint) ConfigMergeVisitor::SetStrategy::Merge));\n\n  Settings settings;\n  switch (settings.configMergeItemStrategy()) {\n  case ConfigMergeVisitor::ItemStrategy::Ignore: ui->itemStrategy->setCurrentIndex(0); break;\n  case ConfigMergeVisitor::ItemStrategy::Override: ui->itemStrategy->setCurrentIndex(1); break;\n  case ConfigMergeVisitor::ItemStrategy::Duplicate: ui->itemStrategy->setCurrentIndex(2); break;\n  }\n\n  switch (settings.configMergeSetStrategy()) {\n  case ConfigMergeVisitor::SetStrategy::Ignore: ui->setStrategy->setCurrentIndex(0); break;\n  case ConfigMergeVisitor::SetStrategy::Override: ui->setStrategy->setCurrentIndex(1); break;\n  case ConfigMergeVisitor::SetStrategy::Duplicate: ui->setStrategy->setCurrentIndex(2); break;\n  case ConfigMergeVisitor::SetStrategy::Merge: ui->setStrategy->setCurrentIndex(3); break;\n  }\n\n  onItemStrategySelected(ui->itemStrategy->currentIndex());\n  onSetStrategySelected(ui->setStrategy->currentIndex());\n\n  connect(ui->itemStrategy, SIGNAL(currentIndexChanged(int)),\n          this, SLOT(onItemStrategySelected(int)));\n  connect(ui->setStrategy, SIGNAL(currentIndexChanged(int)),\n          this, SLOT(onSetStrategySelected(int)));\n  connect(ui->buttonBox, SIGNAL(accepted()), SLOT(accept()));\n  connect(ui->buttonBox, SIGNAL(rejected()), SLOT(reject()));\n}\n\nConfigMergeDialog::~ConfigMergeDialog() {\n  delete ui;\n}\n\n\nConfigMergeVisitor::ItemStrategy\nConfigMergeDialog::itemStrategy() const {\n  return (ConfigMergeVisitor::ItemStrategy)ui->itemStrategy->currentData().toUInt();\n}\n\nConfigMergeVisitor::SetStrategy\nConfigMergeDialog::setStrategy() const {\n  return (ConfigMergeVisitor::SetStrategy)ui->setStrategy->currentData().toUInt();\n}\n\n\nvoid\nConfigMergeDialog::onItemStrategySelected(int index) {\n  ConfigMergeVisitor::ItemStrategy strategy =\n      (ConfigMergeVisitor::ItemStrategy)ui->itemStrategy->itemData(index).toUInt();\n  Settings().setConfigMergeItemStrategy(strategy);\n\n  switch (strategy) {\n  case ConfigMergeVisitor::ItemStrategy::Ignore:\n    ui->itemStrategyLabel->setText(tr(\"Ignores any duplicate item.\"));\n    break;\n  case ConfigMergeVisitor::ItemStrategy::Override:\n    ui->itemStrategyLabel->setText(tr(\"Replaces any duplicate item with the imported one.\"));\n    break;\n  case ConfigMergeVisitor::ItemStrategy::Duplicate:\n    ui->itemStrategyLabel->setText(tr(\"Imports any duplicate item with a modified name.\"));\n    break;\n  }\n}\n\nvoid\nConfigMergeDialog::onSetStrategySelected(int index) {\n  ConfigMergeVisitor::SetStrategy strategy =\n      (ConfigMergeVisitor::SetStrategy)ui->setStrategy->itemData(index).toUInt();\n  Settings().setConfigMergeSetStrategy(strategy);\n  switch (strategy) {\n  case ConfigMergeVisitor::SetStrategy::Ignore:\n    ui->setStrategyLabel->setText(tr(\"Ignores any duplicate set.\"));\n    break;\n  case ConfigMergeVisitor::SetStrategy::Override:\n    ui->setStrategyLabel->setText(tr(\"Replaces any duplicate set with the imported one.\"));\n    break;\n  case ConfigMergeVisitor::SetStrategy::Duplicate:\n    ui->setStrategyLabel->setText(tr(\"Imports any duplicate set with a modified name.\"));\n    break;\n  case ConfigMergeVisitor::SetStrategy::Merge:\n    ui->setStrategyLabel->setText(tr(\"Merges duplicate sets.\"));\n    break;\n  }\n}\n"
  },
  {
    "path": "src/configmergedialog.hh",
    "content": "#ifndef CONFIGMERGEDIALOG_HH\n#define CONFIGMERGEDIALOG_HH\n\n#include <QDialog>\n#include \"configmergevisitor.hh\"\n\nnamespace Ui {\n  class ConfigMergeDialog;\n}\n\nclass ConfigMergeDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit ConfigMergeDialog(QWidget *parent = nullptr);\n  ~ConfigMergeDialog();\n\n  ConfigMergeVisitor::ItemStrategy itemStrategy() const;\n  ConfigMergeVisitor::SetStrategy setStrategy() const;\n\nprivate slots:\n  void onItemStrategySelected(int idx);\n  void onSetStrategySelected(int idx);\n\nprivate:\n  Ui::ConfigMergeDialog *ui;\n};\n\n#endif // CONFIGMERGEDIALOG_HH\n"
  },
  {
    "path": "src/configmergedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConfigMergeDialog</class>\n <widget class=\"QDialog\" name=\"ConfigMergeDialog\">\n  <property name=\"windowModality\">\n   <enum>Qt::NonModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>487</width>\n    <height>342</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Merging codeplugs ...</string>\n  </property>\n  <property name=\"modal\">\n   <bool>true</bool>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Conflict resolution strategies:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;If some of the imported objects (channels, contacts, ...) already exist, select how these conflicts are resolved for items and sets.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"groupBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"toolTip\">\n      <string>Items are all atomic objects like radio IDs, channels, contacts and roaming channels.</string>\n     </property>\n     <property name=\"title\">\n      <string>Items</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n      <item>\n       <widget class=\"QComboBox\" name=\"itemStrategy\">\n        <item>\n         <property name=\"text\">\n          <string>Ignore</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Override</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Duplicate</string>\n         </property>\n        </item>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"itemStrategyLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"wordWrap\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"groupBox_2\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"toolTip\">\n      <string>Sets are all objects, containing other elements like group lists, zones, scan lists and roaming zones.</string>\n     </property>\n     <property name=\"title\">\n      <string>Sets</string>\n     </property>\n     <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n      <item>\n       <widget class=\"QComboBox\" name=\"setStrategy\">\n        <item>\n         <property name=\"text\">\n          <string>Ignore</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Override</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Duplicate</string>\n         </property>\n        </item>\n        <item>\n         <property name=\"text\">\n          <string>Merge</string>\n         </property>\n        </item>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLabel\" name=\"setStrategyLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string/>\n        </property>\n        <property name=\"wordWrap\">\n         <bool>true</bool>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ConfigMergeDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ConfigMergeDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/configobjectlistview.cc",
    "content": "#include \"configobjectlistview.hh\"\n#include \"ui_configobjectlistview.h\"\n#include \"searchpopup.hh\"\n#include <QMessageBox>\n\ninline QPair<int, int>\ngetSelectionRowRange(const QModelIndexList &indices) {\n  int rmin=-1, rmax=-1;\n  foreach (QModelIndex idx, indices) {\n    if ((-1==rmin) || (rmin>idx.row()))\n      rmin = idx.row();\n    if ((-1==rmax) || (rmax<idx.row()))\n      rmax = idx.row();\n  }\n  return QPair<int,int>(rmin, rmax);\n}\n\n\nConfigObjectListView::ConfigObjectListView(QWidget *parent)\n  : QWidget(parent), ui(new Ui::ConfigObjectListView)\n{\n  ui->setupUi(this);\n\n  connect(ui->itemTop, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTop()));\n  connect(ui->itemTenUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenUp()));\n  connect(ui->itemUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemUp()));\n  connect(ui->itemDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemDown()));\n  connect(ui->itemTenDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenDown()));\n  connect(ui->itemBottom, SIGNAL(clicked(bool)), this, SLOT(onMoveItemBottom()));\n  connect(ui->listView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onDoubleClicked(QModelIndex)));\n\n  ui->listView->setSelectionMode(QAbstractItemView::ContiguousSelection);\n  ui->listView->setSelectionBehavior(QAbstractItemView::SelectRows);\n\n  ui->listView->setDragEnabled(true);\n  ui->listView->viewport()->setAcceptDrops(true);\n  ui->listView->setDropIndicatorShown(true);\n  ui->listView->setDragDropMode(QAbstractItemView::InternalMove);\n\n  SearchPopup::attach(ui->listView);\n}\n\nConfigObjectListView::~ConfigObjectListView() {\n  delete ui;\n}\n\nGenericListWrapper *\nConfigObjectListView::model() const {\n  return qobject_cast<GenericListWrapper *>(ui->listView->model());\n}\n\nvoid\nConfigObjectListView::setModel(GenericListWrapper *model) {\n  ui->listView->setModel(model);\n}\n\nbool\nConfigObjectListView::hasSelection() const {\n  return ui->listView->selectionModel()->hasSelection();\n}\n\nQPair<int,int>\nConfigObjectListView::selection() const {\n  return getSelectionRowRange(ui->listView->selectionModel()->selection().indexes());\n}\n\nvoid\nConfigObjectListView::onMoveItemUp() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::max(0, rows.first-1));\n}\n\nvoid\nConfigObjectListView::onMoveItemTenUp() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  int dest = std::max(0, rows.first-9);\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), dest);\n\n}\n\nvoid\nConfigObjectListView::onMoveItemTop() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), 0);\n}\n\n\nvoid\nConfigObjectListView::onMoveItemDown() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::min(model()->rowCount(QModelIndex()), rows.second+2));\n}\n\n\nvoid\nConfigObjectListView::onMoveItemTenDown() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::min(rows.second+10, model()->rowCount(QModelIndex())));\n}\n\nvoid\nConfigObjectListView::onMoveItemBottom() {\n  // Check if there is a selection\n  if (! ui->listView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return;\n  }\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->listView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), model()->rowCount(QModelIndex()));\n}\n\n\nvoid\nConfigObjectListView::onDoubleClicked(QModelIndex idx) {\n  if ((0 > idx.row()) || (idx.row() >= model()->rowCount(QModelIndex())))\n    return;\n  emit doubleClicked(idx.row());\n}\n"
  },
  {
    "path": "src/configobjectlistview.hh",
    "content": "#ifndef CONFIGOBJECTLISTVIEW_H\n#define CONFIGOBJECTLISTVIEW_H\n\n#include <QWidget>\n#include \"configitemwrapper.hh\"\n\nnamespace Ui {\n  class ConfigObjectListView;\n}\n\nclass ConfigObjectListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ConfigObjectListView(QWidget *parent = nullptr);\n  ~ConfigObjectListView();\n\n  GenericListWrapper *model() const;\n  void setModel(GenericListWrapper *model);\n\n  bool hasSelection() const;\n  QPair<int,int> selection() const;\n\nsignals:\n  void doubleClicked(unsigned row);\n\nprotected slots:\n  void onMoveItemUp();\n  void onMoveItemTenUp();\n  void onMoveItemTop();\n\n  void onMoveItemDown();\n  void onMoveItemTenDown();\n  void onMoveItemBottom();\n\n  void onDoubleClicked(QModelIndex idx);\n\nprivate:\n  Ui::ConfigObjectListView *ui;\n};\n\n#endif // CONFIGOBJECTLISTVIEW_H\n"
  },
  {
    "path": "src/configobjectlistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConfigObjectListView</class>\n <widget class=\"QWidget\" name=\"ConfigObjectListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <item>\n    <widget class=\"QListView\" name=\"listView\"/>\n   </item>\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"itemTop\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) to the top.</string>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-top\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"itemTenUp\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) ten positions up.</string>\n       </property>\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-10-up\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"itemUp\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) one position up.</string>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-up\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"itemDown\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) one position down.</string>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-down\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"itemTenDown\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) ten positions down.</string>\n       </property>\n       <property name=\"text\">\n        <string notr=\"true\"/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-10-down\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"itemBottom\">\n       <property name=\"toolTip\">\n        <string>Move selected item(s) to the bottom.</string>\n       </property>\n       <property name=\"text\">\n        <string/>\n       </property>\n       <property name=\"icon\">\n        <iconset theme=\"edit-move-bottom\">\n         <normaloff>.</normaloff>.</iconset>\n       </property>\n       <property name=\"iconSize\">\n        <size>\n         <width>16</width>\n         <height>16</height>\n        </size>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/configobjecttableview.cc",
    "content": "#include \"configobjecttableview.hh\"\n#include \"ui_configobjecttableview.h\"\n#include \"searchpopup.hh\"\n#include <QMessageBox>\n#include <QSortFilterProxyModel>\n#include \"settings.hh\"\n\n\ninline QPair<int, int>\ngetSelectionRowRange(const QModelIndexList &indices) {\n  int rmin=-1, rmax=-1;\n  foreach (QModelIndex idx, indices) {\n    if ((-1==rmin) || (rmin>idx.row()))\n      rmin = idx.row();\n    if ((-1==rmax) || (rmax<idx.row()))\n      rmax = idx.row();\n  }\n  return QPair<int,int>(rmin, rmax);\n}\n\n\nConfigObjectTableView::ConfigObjectTableView(QWidget *parent)\n  : QWidget(parent), _model(nullptr), ui(new Ui::ConfigObjectTableView)\n{\n  ui->setupUi(this);\n\n  ui->filterToggleButton->setDefaultAction(ui->actionToggleFilterSort);\n\n  connect(ui->itemTop, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTop()));\n  connect(ui->itemTenUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenUp()));\n  connect(ui->itemUp, SIGNAL(clicked(bool)), this, SLOT(onMoveItemUp()));\n  connect(ui->itemDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemDown()));\n  connect(ui->itemTenDown, SIGNAL(clicked(bool)), this, SLOT(onMoveItemTenDown()));\n  connect(ui->itemBottom, SIGNAL(clicked(bool)), this, SLOT(onMoveItemBottom()));\n  connect(ui->tableView, SIGNAL(doubleClicked(QModelIndex)),\n          this, SLOT(onDoubleClicked(QModelIndex)));\n\n  ui->tableView->setSelectionMode(QAbstractItemView::ContiguousSelection);\n  ui->tableView->setSelectionBehavior(QAbstractItemView::SelectRows);\n\n  toggleSortFilter(ui->actionToggleFilterSort->isChecked());\n  connect(this, &QObject::objectNameChanged, [this](const QString &objName) {\n    this->ui->actionToggleFilterSort->setChecked(Settings().sortFilterEnabled(objName));\n  });\n\n  ui->tableView->setDropIndicatorShown(true);\n  ui->tableView->setDefaultDropAction(Qt::MoveAction);\n  ui->tableView->setDragDropMode(QAbstractItemView::InternalMove);\n  ui->tableView->setDragDropOverwriteMode(false);\n\n  ui->tableView->addAction(ui->actionToggleFilterSort);\n  connect(ui->actionToggleFilterSort, &QAction::toggled,\n          this, &ConfigObjectTableView::toggleSortFilter);\n\n  connect(ui->clearButton, &QPushButton::clicked, ui->filterEdit, &QLineEdit::clear);\n\n  ui->filterEdit->addAction(ui->actionCloseSortFilter);\n  connect(ui->actionCloseSortFilter, &QAction::triggered, [this]() {\n    this->ui->actionToggleFilterSort->setChecked(false);\n  });\n\n  SearchPopup::attach(ui->tableView);\n}\n\nConfigObjectTableView::~ConfigObjectTableView() {\n  delete ui;\n}\n\nGenericTableWrapper *\nConfigObjectTableView::model() const {\n  return _model;\n}\n\nvoid\nConfigObjectTableView::setModel(GenericTableWrapper *model) {\n  _model = model;\n  _model->setParent(this);\n  // If sorting is enabled -> set source model of proxy\n  if (ui->actionToggleFilterSort->isChecked()) {\n    proxy()->setSourceModel(_model);\n  } else {\n    // if not, set model directly\n    auto selectionModel = ui->tableView->selectionModel();\n    ui->tableView->setModel(_model);\n    if (selectionModel)\n      selectionModel->deleteLater();\n  }\n}\n\n\nQSortFilterProxyModel *\nConfigObjectTableView::proxy() const {\n  if (nullptr == ui->tableView->model())\n    return nullptr;\n  return qobject_cast<QSortFilterProxyModel*>(ui->tableView->model());\n}\n\nbool\nConfigObjectTableView::isFilteredOrSorted() const {\n  if (nullptr == ui->tableView->model())\n    return false;\n  return nullptr != qobject_cast<QSortFilterProxyModel*>(ui->tableView->model());\n}\n\nbool\nConfigObjectTableView::hasSelection() const {\n  return ui->tableView->selectionModel()->hasSelection();\n}\n\nQPair<int,int>\nConfigObjectTableView::selection() const {\n  if (isFilteredOrSorted())\n    return getSelectionRowRange(\n          proxy()->mapSelectionToSource(\n            ui->tableView->selectionModel()->selection()).indexes());\n  return getSelectionRowRange(ui->tableView->selectionModel()->selection().indexes());\n}\n\nQHeaderView *\nConfigObjectTableView::header() const {\n  return ui->tableView->horizontalHeader();\n}\n\nvoid\nConfigObjectTableView::onMoveItemUp() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::max(0, rows.first-1));\n}\n\nvoid\nConfigObjectTableView::onMoveItemTenUp() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  int dest = std::max(0, rows.first-9);\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), dest);\n\n}\n\nvoid\nConfigObjectTableView::onMoveItemTop() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>=rows.first) || (0>rows.second))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), 0);\n}\n\n\nvoid\nConfigObjectTableView::onMoveItemDown() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::min(model()->rowCount(QModelIndex()), rows.second+2));\n}\n\n\nvoid\nConfigObjectTableView::onMoveItemTenDown() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), std::min(rows.second+10, model()->rowCount(QModelIndex())));\n}\n\nvoid\nConfigObjectTableView::onMoveItemBottom() {\n  // check if we can move items around safely\n  if (! canMove()) return;\n\n  // Get selection range assuming only continuous selection mode\n  QPair<int, int> rows = getSelectionRowRange(\n        ui->tableView->selectionModel()->selection().indexes());\n\n  // If selection range is invalid or I cannot move at all: done.\n  if ((0>rows.first) || (0>rows.second) ||\n      ((rows.second+1)>=model()->rowCount(QModelIndex())))\n    return;\n\n  // Then move rows\n  model()->moveRows(QModelIndex(), rows.first, (rows.second-rows.first+1),\n                    QModelIndex(), model()->rowCount(QModelIndex()));\n}\n\nvoid\nConfigObjectTableView::onDoubleClicked(QModelIndex idx) {\n  // Map index if sort/filter is enabled\n  if (isFilteredOrSorted())\n    idx = proxy()->mapToSource(idx);\n\n  if ((0 > idx.row()) || (idx.row() >= model()->rowCount(QModelIndex())))\n    return;\n\n  emit doubleClicked(idx.row());\n}\n\nbool\nConfigObjectTableView::canMove() const {\n  // Check if there is a selection\n  if (! ui->tableView->selectionModel()->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items: You have to select at least one item first.\"));\n    return false;\n  }\n\n  if (isFilteredOrSorted()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot move items.\"),\n          tr(\"Cannot move items as long as there is some filter or sorting applied.\"));\n    return false;\n  }\n\n  return true;\n}\n\nvoid\nConfigObjectTableView::toggleSortFilter(bool enableSortFilter) {\n  Settings().enableSortFilter(objectName(), enableSortFilter);\n\n  ui->filterWidget->setVisible(enableSortFilter);\n  ui->tableView->setSortingEnabled(enableSortFilter);\n  ui->moveWidget->setVisible(! enableSortFilter);\n\n  auto selectionModel = ui->tableView->selectionModel();\n\n  if (enableSortFilter) {\n    // Setup proxy model\n    ui->tableView->setModel(new QSortFilterProxyModel());\n    proxy()->setFilterKeyColumn(-1);\n    proxy()->setFilterCaseSensitivity(Qt::CaseInsensitive);\n    if (_model) proxy()->setSourceModel(_model);\n    // Connect filter edit\n    connect(ui->filterEdit, &QLineEdit::textChanged,\n            proxy(), &QSortFilterProxyModel::setFilterFixedString);\n    ui->filterEdit->setFocus();\n  } else {\n    if (_model)\n      ui->tableView->setModel(_model);\n    ui->filterEdit->clear();\n  }\n\n  if (selectionModel)\n    selectionModel->deleteLater();\n\n  ui->tableView->setDragEnabled(! enableSortFilter);\n  ui->tableView->viewport()->setAcceptDrops(! enableSortFilter);\n}\n"
  },
  {
    "path": "src/configobjecttableview.hh",
    "content": "#ifndef CONFIGOBJECTTABLEVIEW_HH\n#define CONFIGOBJECTTABLEVIEW_HH\n\n#include <QWidget>\n#include \"configitemwrapper.hh\"\n\nclass QHeaderView;\nclass QSortFilterProxyModel;\n\nnamespace Ui {\n  class ConfigObjectTableView;\n}\n\n\nclass ConfigObjectTableView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ConfigObjectTableView(QWidget *parent = nullptr);\n  ~ConfigObjectTableView();\n\n  GenericTableWrapper *model() const;\n  void setModel(GenericTableWrapper *model);\n\n  bool hasSelection() const;\n  bool isFilteredOrSorted() const;\n  QPair<int,int> selection() const;\n\n  QHeaderView *header() const;\n\nsignals:\n  void doubleClicked(unsigned row);\n\nprotected slots:\n  void onMoveItemUp();\n  void onMoveItemTenUp();\n  void onMoveItemTop();\n  void onMoveItemDown();\n  void onMoveItemTenDown();\n  void onMoveItemBottom();\n  void onDoubleClicked(QModelIndex idx);\n  void toggleSortFilter(bool sortFilter);\n\nprotected:\n  QSortFilterProxyModel *proxy() const;\n  bool canMove() const;\n\nprivate:\n  GenericTableWrapper *_model;\n  Ui::ConfigObjectTableView *ui;\n};\n\n#endif // CONFIGOBJECTTABLEVIEW_HH\n"
  },
  {
    "path": "src/configobjecttableview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConfigObjectTableView</class>\n <widget class=\"QWidget\" name=\"ConfigObjectTableView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QTableView\" name=\"tableView\">\n       <property name=\"sortingEnabled\">\n        <bool>true</bool>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QWidget\" name=\"moveWidget\" native=\"true\">\n       <property name=\"sizePolicy\">\n        <sizepolicy hsizetype=\"Minimum\" vsizetype=\"MinimumExpanding\">\n         <horstretch>0</horstretch>\n         <verstretch>0</verstretch>\n        </sizepolicy>\n       </property>\n       <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n        <property name=\"leftMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"topMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"rightMargin\">\n         <number>0</number>\n        </property>\n        <property name=\"bottomMargin\">\n         <number>0</number>\n        </property>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemTop\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) to the top.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-top\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemTenUp\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) ten positions up.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-10-up\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemUp\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) one position up.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-up\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemDown\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) one position down.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-down\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemTenDown\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) ten positions down.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-10-down\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n        <item>\n         <widget class=\"QPushButton\" name=\"itemBottom\">\n          <property name=\"toolTip\">\n           <string>Move selected item(s) to the bottom.</string>\n          </property>\n          <property name=\"text\">\n           <string/>\n          </property>\n          <property name=\"icon\">\n           <iconset theme=\"edit-move-bottom\">\n            <normaloff>.</normaloff>.</iconset>\n          </property>\n         </widget>\n        </item>\n       </layout>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QWidget\" name=\"filterWidget\" native=\"true\">\n     <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n      <property name=\"spacing\">\n       <number>6</number>\n      </property>\n      <property name=\"leftMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"topMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"rightMargin\">\n       <number>0</number>\n      </property>\n      <property name=\"bottomMargin\">\n       <number>0</number>\n      </property>\n      <item>\n       <widget class=\"QToolButton\" name=\"filterToggleButton\">\n        <property name=\"text\">\n         <string notr=\"true\"/>\n        </property>\n       </widget>\n      </item>\n      <item>\n       <widget class=\"QLineEdit\" name=\"filterEdit\"/>\n      </item>\n      <item>\n       <widget class=\"QPushButton\" name=\"clearButton\">\n        <property name=\"text\">\n         <string notr=\"true\"/>\n        </property>\n        <property name=\"icon\">\n         <iconset theme=\"QIcon::ThemeIcon::EditClear\">\n          <normaloff>.</normaloff>.</iconset>\n        </property>\n       </widget>\n      </item>\n     </layout>\n    </widget>\n   </item>\n  </layout>\n  <action name=\"actionToggleFilterSort\">\n   <property name=\"checkable\">\n    <bool>true</bool>\n   </property>\n   <property name=\"icon\">\n    <iconset>\n     <normaloff>.</normaloff>.</iconset>\n   </property>\n   <property name=\"text\">\n    <string>Toggle Filter and Sorting</string>\n   </property>\n   <property name=\"shortcut\">\n    <string notr=\"true\">Ctrl+Shift+L</string>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::NoRole</enum>\n   </property>\n  </action>\n  <action name=\"actionCloseSortFilter\">\n   <property name=\"text\">\n    <string>Close Sort and Filter</string>\n   </property>\n   <property name=\"shortcut\">\n    <string notr=\"true\">Esc</string>\n   </property>\n   <property name=\"menuRole\">\n    <enum>QAction::NoRole</enum>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/configobjecttypeselectiondialog.cc",
    "content": "#include \"configobjecttypeselectiondialog.hh\"\n#include \"ui_configobjecttypeselectiondialog.h\"\n#include \"logger.hh\"\n#include <QMetaClassInfo>\n\nConfigObjectTypeSelectionDialog::ConfigObjectTypeSelectionDialog(\n    const QList<QMetaObject> &cls, QWidget *parent)\n  : QDialog(parent), ui(new Ui::ConfigObjectTypeSelectionDialog), _types(cls)\n{\n  ui->setupUi(this);\n\n  // Iterate over all classes\n  foreach (const QMetaObject type, _types) {\n    logDebug() << \"Inspect class '\" << type.className() << \"'.\";\n    ui->typeSelection->addItem(type.className());\n  }\n\n  if (0 <= ui->typeSelection->currentIndex())\n    onSelectionChanged(ui->typeSelection->currentIndex());\n\n  ui->description->setWordWrap(true);\n  ui->description->setTextFormat(Qt::RichText);\n\n  connect(ui->typeSelection, SIGNAL(currentIndexChanged(int)), this, SLOT(onSelectionChanged(int)));\n}\n\nConfigObjectTypeSelectionDialog::~ConfigObjectTypeSelectionDialog()\n{\n  delete ui;\n}\n\nconst QMetaObject &\nConfigObjectTypeSelectionDialog::selectedType() const {\n  return _types.at(ui->typeSelection->currentIndex());\n}\n\nvoid\nConfigObjectTypeSelectionDialog::onSelectionChanged(int currentIndex) {\n  ui->description->clear();\n  if (_types.count() <= currentIndex)\n    return;\n\n  QMetaObject meta = _types.at(currentIndex);\n  QString description = tr(\"An instance of %1.\").arg(meta.className());\n  QString longDescription;\n\n  for (int i=meta.classInfoOffset(); i<meta.classInfoCount(); i++) {\n    QMetaClassInfo info = meta.classInfo(i);\n    if (0 == strcmp(\"description\", info.name()))\n      description = info.value();\n    if (0 == strcmp(\"longDescription\", info.name()))\n      longDescription = info.value();\n  }\n\n  ui->description->setText(tr(\"<p>%1<p><p style=\\\"margin-left:10px;\\\">%2</p>\").arg(description).arg(longDescription));\n}\n"
  },
  {
    "path": "src/configobjecttypeselectiondialog.hh",
    "content": "#ifndef CONFIGOBJECTTYPESELECTIONDIALOG_HH\n#define CONFIGOBJECTTYPESELECTIONDIALOG_HH\n\n#include <QDialog>\n\nnamespace Ui {\n  class ConfigObjectTypeSelectionDialog;\n}\n\nclass ConfigObjectTypeSelectionDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit ConfigObjectTypeSelectionDialog(const QList<QMetaObject> &cls, QWidget *parent = nullptr);\n  ~ConfigObjectTypeSelectionDialog();\n\n  const QMetaObject &selectedType() const;\n\nprotected slots:\n  void onSelectionChanged(int currentIndex);\n\nprivate:\n  Ui::ConfigObjectTypeSelectionDialog *ui;\n  QList<QMetaObject> _types;\n};\n\n#endif // CONFIGOBJECTTYPESELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/configobjecttypeselectiondialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ConfigObjectTypeSelectionDialog</class>\n <widget class=\"QDialog\" name=\"ConfigObjectTypeSelectionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>184</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Preferred\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Create extension object</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"label\">\n     <property name=\"text\">\n      <string>Select the class of object to create</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"typeSelection\"/>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"description\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ConfigObjectTypeSelectionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ConfigObjectTypeSelectionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/contactlistview.cc",
    "content": "#include \"contactlistview.hh\"\n#include \"ui_contactlistview.h\"\n#include \"dmrcontactdialog.hh\"\n#include \"m17contactdialog.hh\"\n#include \"dtmfcontactdialog.hh\"\n#include \"application.hh\"\n#include \"settings.hh\"\n#include <QMessageBox>\n#include <QHeaderView>\n#include <QMenu>\n\nContactListView::ContactListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::ContactListView), _config(config)\n{\n  ui->setupUi(this);\n\n  connect(ui->contactTableView->header(), SIGNAL(sectionCountChanged(int,int)),\n          this, SLOT(loadHeaderState()));\n  connect(ui->contactTableView->header(), SIGNAL(sectionResized(int,int,int)),\n          this, SLOT(storeHeaderState()));\n\n  ui->contactTableView->setModel(new ContactListWrapper(_config->contacts(), ui->contactTableView));\n\n  QMenu *menu = new QMenu();\n  menu->addAction(ui->actionAdd_DMR_Contact);\n  menu->addAction(ui->actionAdd_M17_Contact);\n  menu->addAction(ui->actionAdd_DTMF_Contact);\n  ui->addContact->setMenu(menu);\n\n  connect(ui->actionAdd_DMR_Contact, SIGNAL(triggered(bool)), this, SLOT(onAddDMRContact()));\n  connect(ui->actionAdd_M17_Contact, SIGNAL(triggered(bool)), this, SLOT(onAddM17Contact()));\n  connect(ui->actionAdd_DTMF_Contact, SIGNAL(triggered(bool)), this, SLOT(onAddDTMFContact()));\n  connect(ui->remContact, SIGNAL(clicked()), this, SLOT(onRemContact()));\n  connect(ui->contactTableView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditContact(unsigned)));\n}\n\nContactListView::~ContactListView() {\n  delete ui;\n}\n\nvoid\nContactListView::onAddDMRContact() {\n  Application *app = qobject_cast<Application *>(QApplication::instance());\n  DMRContactDialog dialog(app->user(), app->talkgroup(), _config);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->contactTableView->hasSelection())\n    row = ui->contactTableView->selection().second+1;\n  _config->contacts()->add(dialog.contact(), row);\n}\n\nvoid\nContactListView::onAddM17Contact() {\n  M17ContactDialog dialog(_config);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->contactTableView->hasSelection())\n    row = ui->contactTableView->selection().second+1;\n  _config->contacts()->add(dialog.contact(), row);\n}\n\nvoid\nContactListView::onAddDTMFContact() {\n  DTMFContactDialog dialog(_config);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->contactTableView->hasSelection())\n    row = ui->contactTableView->selection().second+1;\n  _config->contacts()->add(dialog.contact(), row);\n}\n\nvoid\nContactListView::onRemContact() {\n  // Check if there is any contacts selected\n  if (! ui->contactTableView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete contact\"),\n          tr(\"Cannot delete contact: You have to select a contact first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->contactTableView->selection();\n  int numrows = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->contacts()->contact(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete contact?\"), tr(\"Delete contact %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete contacts?\"), tr(\"Delete %1 contacts?\").arg(numrows)))\n      return;\n  }\n\n  // collect all selected contacts\n  // need to collect them first as rows change when deleting contacts\n  QList<Contact *> contacts; contacts.reserve(numrows);\n  for (int i=rows.first; i<=rows.second; i++)\n    contacts.push_back(_config->contacts()->contact(i));\n  // remove contacts\n  foreach (Contact *contact, contacts)\n    _config->contacts()->del(contact);\n}\n\nvoid\nContactListView::onEditContact(unsigned row) {\n  Application *app = qobject_cast<Application *>(QApplication::instance());\n  Contact *contact = _config->contacts()->contact(row);\n\n  if (DMRContact *dmr = contact->as<DMRContact>()) {\n    DMRContactDialog dialog(dmr, app->user(), app->talkgroup(), _config);\n    if (QDialog::Accepted != dialog.exec())\n      return;\n    dialog.contact();\n  } else if (M17Contact *m17 = contact->as<M17Contact>()) {\n    M17ContactDialog dialog(_config, m17);\n    if (QDialog::Accepted != dialog.exec())\n      return;\n    dialog.contact();\n  } else if (DTMFContact *dtmf = contact->as<DTMFContact>()) {\n    DTMFContactDialog dialog(dtmf, _config);\n    if (QDialog::Accepted != dialog.exec())\n      return;\n    dialog.contact();\n  }\n}\n\nvoid\nContactListView::loadHeaderState() {\n  Settings settings;\n  ui->contactTableView->header()->restoreState(settings.headerState(\"contactList\"));\n}\nvoid\nContactListView::storeHeaderState() {\n  Settings settings;\n  settings.setHeaderState(\"contactList\", ui->contactTableView->header()->saveState());\n}\n"
  },
  {
    "path": "src/contactlistview.hh",
    "content": "#ifndef CONTACTLISTVIEW_HH\n#define CONTACTLISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nnamespace Ui {\n  class ContactListView;\n}\n\nclass ContactListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ContactListView(Config *config, QWidget *parent = nullptr);\n  ~ContactListView();\n\nprotected slots:\n  void onAddDMRContact();\n  void onAddM17Contact();\n  void onAddDTMFContact();\n  void onRemContact();\n  void onEditContact(unsigned row);\n\n  void loadHeaderState();\n  void storeHeaderState();\n\nprivate:\n  Ui::ContactListView *ui;\n  Config *_config;\n};\n\n#endif // CONTACTLISTVIEW_HH\n"
  },
  {
    "path": "src/contactlistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ContactListView</class>\n <widget class=\"QWidget\" name=\"ContactListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>492</width>\n    <height>306</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"ConfigObjectTableView\" name=\"contactTableView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addContact\">\n       <property name=\"toolTip\">\n        <string>Adds a contact to the list.</string>\n       </property>\n       <property name=\"text\">\n        <string>Add Contact</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt++</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remContact\">\n       <property name=\"accessibleName\">\n        <string>Delete contact button</string>\n       </property>\n       <property name=\"text\">\n        <string>Delete Contact</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n  <action name=\"actionAdd_M17_Contact\">\n   <property name=\"text\">\n    <string>Add M17 Contact</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds an M17 contact to the list.</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_DTMF_Contact\">\n   <property name=\"text\">\n    <string>Add DTMF Contact</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds an DTMF (analog) contact to the list.</string>\n   </property>\n  </action>\n  <action name=\"actionAdd_DMR_Contact\">\n   <property name=\"text\">\n    <string>Add DMR Contact</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Adds an DMR contact to the list.</string>\n   </property>\n  </action>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectTableView</class>\n   <extends>QWidget</extends>\n   <header>configobjecttableview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/contactselectiondialog.cc",
    "content": "#include \"contactselectiondialog.hh\"\n#include \"contact.hh\"\n\n#include <QListWidget>\n#include <QDialogButtonBox>\n#include <QVBoxLayout>\n#include <QLabel>\n#include <QCheckBox>\n\n/* ********************************************************************************************* *\n * Implementation of MultiGroupCallSelectionDialog\n * ********************************************************************************************* */\nMultiGroupCallSelectionDialog::MultiGroupCallSelectionDialog(\n  ContactList *contacts, bool showPrivateCalls, DMRContactRefList *exclude, QWidget *parent)\n  : QDialog(parent)\n{\n  _contacts = new QListWidget();\n  QCheckBox *showPrivCall = new QCheckBox(tr(\"Show private calls\"));\n  showPrivCall->setChecked(showPrivateCalls);\n\n  for (int i=0; i<contacts->count(); i++) {\n    Contact *contact = contacts->contact(i);\n    if (! contact->is<DMRContact>())\n      continue;\n    if (exclude && exclude->has(contact))\n      continue;\n    auto digi = contact->as<DMRContact>();\n    auto item = new QListWidgetItem(digi->name());\n    item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);\n    item->setData(Qt::UserRole, QVariant::fromValue(digi));\n    item->setCheckState(Qt::Unchecked);\n    _contacts->addItem(item);\n    // Hide private calls if showPrivateCall is false (default)\n    item->setHidden((DMRContact::PrivateCall == digi->type()) && (! showPrivateCalls));\n  }\n\n  QDialogButtonBox *bbox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);\n  connect(bbox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(bbox, SIGNAL(rejected()), this, SLOT(reject()));\n  connect(showPrivCall, SIGNAL(toggled(bool)), this, SLOT(showPrivateCallsToggled(bool)));\n\n  _label = new QLabel(tr(\"Select a group call:\"));\n  QVBoxLayout *layout = new QVBoxLayout();\n  layout->addWidget(_label);\n  layout->addWidget(_contacts);\n  layout->addWidget(showPrivCall);\n  layout->addWidget(bbox);\n  setLayout(layout);\n}\n\n\nvoid\nMultiGroupCallSelectionDialog::setLabel(const QString &text) {\n  _label->setText(text);\n}\n\nQList<DMRContact *>\nMultiGroupCallSelectionDialog::contacts() {\n  QList<DMRContact *> contacts;\n  for (int i=0; i<_contacts->count(); i++) {\n    if (Qt::Checked == _contacts->item(i)->checkState())\n      contacts.push_back(_contacts->item(i)->data(Qt::UserRole).value<DMRContact *>());\n  }\n  return contacts;\n}\n\nvoid\nMultiGroupCallSelectionDialog::showPrivateCallsToggled(bool show) {\n  for (int i=0; i<_contacts->count(); i++) {\n    if (DMRContact::PrivateCall == _contacts->item(i)->data(Qt::UserRole).value<DMRContact *>()->type())\n      _contacts->item(i)->setHidden(! show);\n  }\n}\n"
  },
  {
    "path": "src/contactselectiondialog.hh",
    "content": "#ifndef CONTACTSELECTIONDIALOG_HH\n#define CONTACTSELECTIONDIALOG_HH\n\n#include <QList>\n#include <QDialog>\n\nclass DMRContact;\nclass ContactList;\nclass QListWidget;\nclass QLabel;\nclass DMRContactRefList;\n\n\nclass MultiGroupCallSelectionDialog: public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit MultiGroupCallSelectionDialog(ContactList *contacts, bool showPrivateCalls=false,\n    DMRContactRefList *exclude=nullptr, QWidget *parent=nullptr);\n\n  QList<DMRContact *> contacts();\n\n  void setLabel(const QString &text);\n\nprotected slots:\n  void showPrivateCallsToggled(bool show);\n\nprotected:\n  QLabel *_label;\n  QListWidget *_contacts;\n};\n\n#endif // CONTACTSELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/deviceselectiondialog.cc",
    "content": "#include \"deviceselectiondialog.hh\"\n#include \"ui_deviceselectiondialog.h\"\n\nDeviceSelectionDialog::DeviceSelectionDialog(const QList<USBDeviceDescriptor> &interfaces, QWidget *parent) :\n  QDialog(parent), ui(new Ui::DeviceSelectionDialog), _interfaces(interfaces)\n{\n  ui->setupUi(this);\n\n  // Populate combo box\n  foreach (USBDeviceDescriptor dev, _interfaces) {\n    ui->comboBox->addItem(dev.description());\n  }\n  connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onDeviceSelected(int)));\n\n  // Select first device\n  if (_interfaces.count())\n    ui->comboBox->setCurrentIndex(0);\n\n  connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nDeviceSelectionDialog::~DeviceSelectionDialog() {\n  delete ui;\n}\n\nUSBDeviceDescriptor\nDeviceSelectionDialog::device() const {\n  return _interfaces.at(ui->comboBox->currentIndex());\n}\n\nvoid\nDeviceSelectionDialog::onDeviceSelected(int idx) {\n  // Update description.\n  USBDeviceDescriptor device = _interfaces.at(idx);\n  ui->description->setText(device.description() + \". \" + device.longDescription());\n}\n\n"
  },
  {
    "path": "src/deviceselectiondialog.hh",
    "content": "#ifndef DEVICESELECTIONDIALOG_HH\n#define DEVICESELECTIONDIALOG_HH\n\n#include <QDialog>\n#include \"usbdevice.hh\"\n\nnamespace Ui {\n  class DeviceSelectionDialog;\n}\n\nclass DeviceSelectionDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit DeviceSelectionDialog(const QList<USBDeviceDescriptor> &interfaces, QWidget *parent = nullptr);\n  ~DeviceSelectionDialog();\n\n  USBDeviceDescriptor device() const;\n\nprotected slots:\n  void onDeviceSelected(int idx);\n\nprivate:\n  Ui::DeviceSelectionDialog *ui;\n  const QList<USBDeviceDescriptor> &_interfaces;\n};\n\n#endif // DEVICESELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/deviceselectiondialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DeviceSelectionDialog</class>\n <widget class=\"QDialog\" name=\"DeviceSelectionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Select a device</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"message\">\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"comboBox\"/>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"description\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DeviceSelectionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DeviceSelectionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dmrchanneldialog.cc",
    "content": "#include \"dmrchanneldialog.hh\"\n#include \"application.hh\"\n#include <QCompleter>\n#include <QSpinBox>\n#include \"rxgrouplistdialog.hh\"\n#include \"repeatercompleter.hh\"\n#include \"repeaterdatabase.hh\"\n#include \"settings.hh\"\n#include \"idselect.hh\"\n#include \"admitselect.hh\"\n#include \"timeslotselect.hh\"\n#include \"dmrcontactdialog.hh\"\n#include \"aprsselect.hh\"\n#include \"roamingzonedialog.hh\"\n#include \"ui_channeldialog.h\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DMRChannelDialog\n * ********************************************************************************************* */\nDMRChannelDialog::DMRChannelDialog(Config *config, QWidget *parent)\n  : ChannelDialog(config, parent), _clone(nullptr), _orig(), _dmrId(nullptr), _admit(nullptr),\n  _colorcode(nullptr), _timeSlot(nullptr), _groupList(nullptr), _contact(nullptr),\n  _aprs(nullptr), _roam(nullptr)\n{\n  auto app = qobject_cast<Application *>(qApp);\n  auto completer = new RepeaterCompleter(2, app->repeater(), this);\n  auto filter = new DMRRepeaterFilter(app->repeater(), app->position(), this);\n  filter->setSourceModel(app->repeater());\n  completer->setModel(filter);\n  ui->channelName->setCompleter(completer);\n  connect(completer, SIGNAL(activated(const QModelIndex &)),\n          this, SLOT(onRepeaterSelected(const QModelIndex &)));\n\n  ui->rightForm->addRow(tr(\"Radio Id\"),\n                        _dmrId = new DMRIdSelect(config));\n  ui->rightForm->addRow(tr(\"Tx Admit\"),\n                        _admit = new DMRAdmitSelect());\n  ui->rightForm->addRow(tr(\"Color-code\"),\n                        _colorcode = new QSpinBox());\n  _colorcode->setRange(0,15);\n  ui->rightForm->addRow(tr(\"Time-slot\"),\n                        _timeSlot = new TimeslotSelect());\n  ui->rightForm->addRow(tr(\"Group list\"),\n                        _groupList = new RXGroupListBox(config->rxGroupLists()));\n  ui->rightForm->addRow(tr(\"Tx Contact\"),\n                        _contact = new DMRContactSelect(config));\n  ui->rightForm->addRow(tr(\"APRS\"),\n                        _aprs = new APRSSelect(config));\n  ui->rightForm->addRow(tr(\"Roaming zone\"),\n                        _roam = new RoamingZoneSelect(config));\n}\n\n\nvoid\nDMRChannelDialog::setChannel(DMRChannel *ch) {\n  if (_clone) {\n    delete _clone;\n    _clone = nullptr;\n  }\n\n  _orig = ch;\n  if (_orig.isNull())\n    return;\n\n  _clone = _orig->clone()->as<DMRChannel>();\n  _clone->setParent(this);\n\n  ChannelDialog::setChannel(_clone);\n\n\n  _dmrId->setRadioId(_clone->radioId());\n  _admit->setAdmit(_clone->admit());\n  _colorcode->setValue(_clone->colorCode());\n  _timeSlot->setSlot(_clone->timeSlot());\n  _groupList->setGroupList(_clone->groupList());\n  _contact->setContact(_clone->contact());\n  _aprs->setAPRSSystem(_clone->aprs());\n  _roam->setRoamingZone(_clone->roaming());\n}\n\n\nvoid\nDMRChannelDialog::accept()\n{\n  _clone->setRadioId(_dmrId->radioId());\n  _clone->setAdmit(_admit->admit());\n  _clone->setColorCode(_colorcode->value());\n  _clone->setTimeSlot(_timeSlot->slot());\n  _clone->setGroupList(_groupList->groupList());\n  _clone->setContact(_contact->contact());\n  _clone->setAPRS(_aprs->aprsSystem());\n  _clone->setRoaming(_roam->roamingZone());\n\n  ChannelDialog::accept();\n\n  _orig->copy(*_clone);\n}\n\n\nvoid\nDMRChannelDialog::onRepeaterSelected(const QModelIndex &index) {\n  Application *app = qobject_cast<Application *>(qApp);\n\n  QModelIndex src = qobject_cast<QAbstractProxyModel*>(\n        ui->channelName->completer()->completionModel())->mapToSource(index);\n  src = qobject_cast<QAbstractProxyModel*>(\n        ui->channelName->completer()->model())->mapToSource(src);\n  Frequency rx = app->repeater()->get(src.row()).rxFrequency();\n  Frequency tx = app->repeater()->get(src.row()).txFrequency();\n  _colorcode->setValue(app->repeater()->get(src.row()).colorCode());\n  ui->rxFrequency->setText(rx.format());\n  ui->txFrequency->setText(tx.format());\n  updateOffsetFrequency();\n}\n\n"
  },
  {
    "path": "src/dmrchanneldialog.hh",
    "content": "#ifndef DMRCHANNELDIALOG_H\n#define DMRCHANNELDIALOG_H\n\n#include \"channeldialog.hh\"\n\nclass DMRChannel;\nclass DMRIdSelect;\nclass DMRAdmitSelect;\nclass QSpinBox;\nclass TimeslotSelect;\nclass RXGroupListBox;\nclass DMRContactSelect;\nclass APRSSelect;\nclass RoamingZoneSelect;\n\n\nclass DMRChannelDialog: public ChannelDialog\n{\n  Q_OBJECT\n\npublic:\n  DMRChannelDialog(Config *config, QWidget *parent=nullptr);\n\n  void setChannel(DMRChannel *ch);\n\npublic slots:\n  void accept() override;\n\nprotected slots:\n  void onRepeaterSelected(const QModelIndex &index);\n\nprotected:\n  DMRChannel *_clone;\n  QPointer<DMRChannel> _orig;\n  DMRIdSelect *_dmrId;\n  DMRAdmitSelect *_admit;\n  QSpinBox *_colorcode;\n  TimeslotSelect *_timeSlot;\n  RXGroupListBox *_groupList;\n  DMRContactSelect *_contact;\n  APRSSelect *_aprs;\n  RoamingZoneSelect *_roam;\n};\n\n#endif // DMRCHANNELDIALOG_H\n"
  },
  {
    "path": "src/dmrcontactdialog.cc",
    "content": "#include \"dmrcontactdialog.hh\"\n#include \"ui_dmrcontactdialog.h\"\n\n#include <QPushButton>\n#include <QVBoxLayout>\n#include <QHBoxLayout>\n#include <QMessageBox>\n#include <QDialogButtonBox>\n#include <QRegularExpressionValidator>\n#include <QFormLayout>\n#include <QCompleter>\n#include \"contact.hh\"\n#include \"userdatabase.hh\"\n#include \"talkgroupdatabase.hh\"\n#include \"settings.hh\"\n#include \"config.hh\"\n\n\n/* ****************************************************************************************** *\n * Implementation of DMRContactDialog\n * ****************************************************************************************** */\nDMRContactDialog::DMRContactDialog(UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent)\n  : QDialog(parent), _myContact(new DMRContact(this)), _contact(nullptr),\n    _user_completer(nullptr), _tg_completer(nullptr), _config(context),\n    ui(new Ui::DMRContactDialog)\n{\n  setWindowTitle(tr(\"Create DMR Contact\"));\n\n  _user_completer = new QCompleter(users, this);\n  _user_completer->setCompletionColumn(0);\n  _user_completer->setCaseSensitivity(Qt::CaseInsensitive);\n\n  _tg_completer = new QCompleter(tgs, this);\n  _tg_completer->setCompletionColumn(0);\n  _tg_completer->setCaseSensitivity(Qt::CaseInsensitive);\n\n  connect(_user_completer, SIGNAL(activated(QModelIndex)),\n          this, SLOT(onCompleterActivated(QModelIndex)));\n\n  connect(_tg_completer, SIGNAL(activated(QModelIndex)),\n          this, SLOT(onCompleterActivated(QModelIndex)));\n\n  construct();\n}\n\nDMRContactDialog::DMRContactDialog(DMRContact *contact, UserDatabase *users,\n                                   TalkGroupDatabase *tgs, Config *context, QWidget *parent)\n  : QDialog(parent), _myContact(new DMRContact(this)), _contact(contact),\n    _user_completer(nullptr), _tg_completer(nullptr), _config(context),\n    ui(new Ui::DMRContactDialog)\n{\n  setWindowTitle(tr(\"Edit DMR Contact\"));\n  _user_completer = new QCompleter(users, this);\n  _user_completer->setCompletionColumn(0);\n  _user_completer->setCaseSensitivity(Qt::CaseInsensitive);\n\n  _tg_completer = new QCompleter(tgs, this);\n  _tg_completer->setCompletionColumn(0);\n  _tg_completer->setCaseSensitivity(Qt::CaseInsensitive);\n\n  if (_contact)\n    _myContact->copy(*_contact);\n\n  connect(_user_completer, SIGNAL(activated(QModelIndex)),\n          this, SLOT(onCompleterActivated(QModelIndex)));\n\n  connect(_tg_completer, SIGNAL(activated(QModelIndex)),\n          this, SLOT(onCompleterActivated(QModelIndex)));\n\n  construct();\n}\n\nDMRContactDialog::~DMRContactDialog()\n{\n  delete ui;\n}\n\nvoid\nDMRContactDialog::construct() {\n  ui->setupUi(this);\n  Settings settings;\n\n  ui->typeComboBox->addItem(tr(\"Private Call\"), unsigned(DMRContact::PrivateCall));\n  ui->typeComboBox->addItem(tr(\"Group Call\"), unsigned(DMRContact::GroupCall));\n  ui->typeComboBox->addItem(tr(\"All Call\"), unsigned(DMRContact::AllCall));\n  if (DMRContact::PrivateCall == _myContact->type()) {\n    ui->typeComboBox->setCurrentIndex(0);\n    ui->nameLineEdit->setCompleter(_user_completer);\n  } else if (DMRContact::GroupCall == _myContact->type()) {\n    ui->typeComboBox->setCurrentIndex(1);\n    ui->nameLineEdit->setCompleter(_tg_completer);\n  } else {\n    ui->typeComboBox->setCurrentIndex(2);\n    ui->nameLineEdit->setCompleter(nullptr);\n    ui->nameLineEdit->setEnabled(false);\n  }\n  ui->nameLineEdit->setText(_myContact->name());\n  ui->numberLineEdit->setText(QString::number(_myContact->number()));\n  ui->numberLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(\"[0-9]+\")));\n  ui->ringCheckBox->setChecked(_myContact->ring());\n\n  ui->extensionView->setObjectName(\"dmrContactExtension\");\n  ui->extensionView->setObject(_myContact, _config);\n\n  connect(ui->typeComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onTypeChanged(int)));\n  connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nvoid\nDMRContactDialog::onTypeChanged(int idx) {\n  if (0 == idx) {\n    ui->numberLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(\"[0-9]+\")));\n    ui->numberLineEdit->setEnabled(true);\n    ui->nameLineEdit->setCompleter(_user_completer);\n  } else if (1 == idx) {\n    ui->numberLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(\"[0-9]+\")));\n    ui->numberLineEdit->setEnabled(true);\n    ui->nameLineEdit->setCompleter(_tg_completer);\n  } else if (2 == idx) {\n    ui->numberLineEdit->setText(\"16777215\");\n    ui->numberLineEdit->setEnabled(false);\n    ui->nameLineEdit->setCompleter(nullptr);\n  }\n}\n\nvoid\nDMRContactDialog::onCompleterActivated(const QModelIndex &idx) {\n  if (0 == ui->typeComboBox->currentIndex()) { // Private call\n    if (nullptr == _user_completer)\n      return;\n    UserDatabase *db = qobject_cast<UserDatabase *>(_user_completer->model());\n    if (nullptr == db)\n      return;\n    QAbstractProxyModel *model = qobject_cast<QAbstractProxyModel *>(_user_completer->completionModel());\n    if (nullptr == model)\n      return;\n    QModelIndex srcidx = model->mapToSource(idx);\n    ui->numberLineEdit->setText(QString::number(db->user(srcidx.row()).id));\n  } else if (1 == ui->typeComboBox->currentIndex()) { // Group call\n    if (nullptr == _tg_completer)\n      return;\n    TalkGroupDatabase *db = qobject_cast<TalkGroupDatabase *>(_tg_completer->model());\n    if (nullptr == db)\n      return;\n    QAbstractProxyModel *model = qobject_cast<QAbstractProxyModel *>(_tg_completer->completionModel());\n    if (nullptr == model)\n      return;\n    QModelIndex srcidx = model->mapToSource(idx);\n    ui->numberLineEdit->setText(QString::number(db->talkgroup(srcidx.row()).id));\n  }\n}\n\nDMRContact *\nDMRContactDialog::contact()\n{\n  _myContact->setType(DMRContact::Type(ui->typeComboBox->currentData().toUInt()));\n  _myContact->setName(ui->nameLineEdit->text().simplified());\n  _myContact->setNumber(ui->numberLineEdit->text().toUInt());\n  _myContact->setRing(ui->ringCheckBox->isChecked());\n\n  DMRContact *contact = _myContact;\n  if (_contact) {\n    _contact->copy(*_myContact);\n    contact = _contact;\n  } else {\n    _myContact->setParent(nullptr);\n  }\n\n  return contact;\n}\n\n\n\n/* ****************************************************************************************** *\n * Implementation of DMRContactSelect\n * ****************************************************************************************** */\nDMRContactSelect::DMRContactSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[None]\"), QVariant::fromValue<DMRContact*>(nullptr));\n  for (int i=0; i<config->contacts()->count(); i++) {\n    if (config->contacts()->get(i)->is<DMRContact>()) {\n      auto contact = config->contacts()->get(i)->as<DMRContact>();\n      addItem(contact->name(), QVariant::fromValue(contact));\n    }\n  }\n}\n\n\nvoid\nDMRContactSelect::setContact(DMRContact *contact) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<DMRContact*>() == contact) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nDMRContact *\nDMRContactSelect::contact() const {\n  return currentData().value<DMRContact*>();\n}\n\n"
  },
  {
    "path": "src/dmrcontactdialog.hh",
    "content": "#ifndef DMRCONTACTDIALOG_HH\n#define DMRCONTACTDIALOG_HH\n\n#include <QDialog>\n#include <QComboBox>\n\nnamespace Ui {\n  class DMRContactDialog;\n}\n\nclass QCompleter;\nclass UserDatabase;\nclass TalkGroupDatabase;\nclass DMRContact;\nclass Config;\n\nclass DMRContactDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit DMRContactDialog(UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent=nullptr);\n  explicit DMRContactDialog(DMRContact *contact, UserDatabase *users, TalkGroupDatabase *tgs, Config *context, QWidget *parent=nullptr);\n  ~DMRContactDialog();\n\npublic:\n  DMRContact *contact();\n\nprotected slots:\n  void onTypeChanged(int idx);\n  void onCompleterActivated(const QModelIndex &idx);\n\nprotected:\n  void construct();\n\nprivate:\n  DMRContact *_myContact;\n  DMRContact *_contact;\n  QCompleter *_user_completer;\n  QCompleter *_tg_completer;\n  Config *_config;\n  Ui::DMRContactDialog *ui;\n};\n\n\n\nclass DMRContactSelect: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  DMRContactSelect(Config *config, QWidget *parent=nullptr);\n\n  void setContact(DMRContact *contact);\n  DMRContact *contact() const;\n};\n\n\n#endif // DMRCONTACTDIALOG_HH\n"
  },
  {
    "path": "src/dmrcontactdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DMRContactDialog</class>\n <widget class=\"QDialog\" name=\"DMRContactDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>392</width>\n    <height>296</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"typeLabel\">\n         <property name=\"text\">\n          <string>Type</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"typeComboBox\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nameLabel\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"nameLineEdit\"/>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"numberLabel\">\n         <property name=\"text\">\n          <string>Number</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"numberLineEdit\"/>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <widget class=\"QLabel\" name=\"ringLabel\">\n         <property name=\"text\">\n          <string>Ring</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"ringCheckBox\"/>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DMRContactDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DMRContactDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dmriddialog.cc",
    "content": "#include \"dmriddialog.hh\"\n#include \"ui_dmriddialog.h\"\n#include \"settings.hh\"\n#include <QIntValidator>\n\nDMRIDDialog::DMRIDDialog(DMRRadioID *radioid, Config *context, QWidget *parent) :\n  QDialog(parent), ui(new Ui::DMRIDDialog), _myID(new DMRRadioID(this)), _editID(radioid),\n  _config(context)\n{\n  ui->setupUi(this);\n  _myID->copy(*_editID);\n\n  construct();\n}\n\nDMRIDDialog::DMRIDDialog(Config *context, QWidget *parent) :\n  QDialog(parent), ui(new Ui::DMRIDDialog), _myID(new DMRRadioID(this)), _editID(nullptr),\n  _config(context)\n{\n  ui->setupUi(this);\n\n  construct();\n}\n\nvoid\nDMRIDDialog::construct() {\n  Settings settings;\n  ui->name->setText(_myID->name());\n  ui->dmrID->setText(QString::number(_myID->number()));\n  ui->dmrID->setValidator(new QIntValidator(1, 16777215));\n\n  ui->extensionView->setObjectName(\"dmrRadioIdExtension\");\n  ui->extensionView->setObject(_myID, _config);\n}\n\nDMRIDDialog::~DMRIDDialog() {\n  delete ui;\n}\n\nDMRRadioID *\nDMRIDDialog::radioId() {\n  _myID->setName(ui->name->text().simplified());\n  _myID->setNumber(ui->dmrID->text().toUInt());\n\n  if (_editID) {\n    _editID->copy(*_myID);\n    _myID->deleteLater();\n    _myID = _editID;\n  } else {\n    _myID->setParent(nullptr);\n  }\n  return _myID;\n}\n"
  },
  {
    "path": "src/dmriddialog.hh",
    "content": "#ifndef DMRIDDIALOG_HH\n#define DMRIDDIALOG_HH\n\n#include <QDialog>\n#include \"radioid.hh\"\n\nnamespace Ui {\n  class DMRIDDialog;\n}\n\nclass DMRIDDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit DMRIDDialog(Config *context, QWidget *parent = nullptr);\n  DMRIDDialog(DMRRadioID *radioid, Config *context, QWidget *parent = nullptr);\n  ~DMRIDDialog();\n\n  DMRRadioID *radioId();\n\nprotected:\n  void construct();\n\nprivate:\n  Ui::DMRIDDialog *ui;\n  DMRRadioID *_myID;\n  DMRRadioID *_editID;\n  Config *_config;\n};\n\n#endif // DMRIDDIALOG_HH\n"
  },
  {
    "path": "src/dmriddialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DMRIDDialog</class>\n <widget class=\"QDialog\" name=\"DMRIDDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"tab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nameLabel\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"name\"/>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"idLabel\">\n         <property name=\"text\">\n          <string>DMR ID</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"dmrID\"/>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"tab_2\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"ExtensionView\" name=\"extensionView\" native=\"true\"/>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DMRIDDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DMRIDDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/dtmfcontactdialog.cc",
    "content": "#include \"dtmfcontactdialog.hh\"\n#include \"ui_dtmfcontactdialog.h\"\n\n#include <QRegularExpressionValidator>\n#include \"contact.hh\"\n#include \"settings.hh\"\n\nDTMFContactDialog::DTMFContactDialog(Config *context, QWidget *parent) :\n  QDialog(parent), _myContact(new DTMFContact(this)), _contact(nullptr), _config(context),\n  ui(new Ui::DTMFContactDialog)\n{\n  setWindowTitle(tr(\"Create DTMF Contact\"));\n  construct();\n}\n\nDTMFContactDialog::DTMFContactDialog(DTMFContact *contact, Config *context, QWidget *parent) :\n  QDialog(parent), _myContact(new DTMFContact(this)), _contact(contact), _config(context),\n  ui(new Ui::DTMFContactDialog)\n{\n  setWindowTitle(tr(\"Edit DMR Contact\"));\n  if (_contact)\n    _myContact->copy(*_contact);\n\n  construct();\n}\n\nDTMFContactDialog::~DTMFContactDialog() {\n  delete ui;\n}\n\nvoid\nDTMFContactDialog::construct() {\n  ui->setupUi(this);\n  Settings settings;\n\n  ui->nameLineEdit->setText(_myContact->name());\n  ui->numberLineEdit->setText(_myContact->number());\n  ui->numberLineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(\"[0-9a-dA-D\\\\*#]+\")));\n  ui->ringCheckBox->setChecked(_myContact->ring());\n\n  ui->extensionView->setObjectName(\"dtmfContactExtension\");\n  ui->extensionView->setObject(_myContact, _config);\n}\n\nDTMFContact *\nDTMFContactDialog::contact() {\n  _myContact->setName(ui->nameLineEdit->text().simplified());\n  _myContact->setNumber(ui->numberLineEdit->text().simplified());\n  _myContact->setRing(ui->ringCheckBox->isChecked());\n\n  DTMFContact *contact = _myContact;\n  if (_contact) {\n    _contact->copy(*_myContact);\n    contact = _contact;\n  } else {\n    _myContact->setParent(nullptr);\n  }\n\n  return contact;\n}\n\n"
  },
  {
    "path": "src/dtmfcontactdialog.hh",
    "content": "#ifndef DTMFCONTACTDIALOG_HH\n#define DTMFCONTACTDIALOG_HH\n\n#include <QDialog>\n\nnamespace Ui {\n  class DTMFContactDialog;\n}\n\nclass DTMFContact;\nclass Config;\n\nclass DTMFContactDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit DTMFContactDialog(Config *context, QWidget *parent = nullptr);\n  DTMFContactDialog(DTMFContact *contact, Config *context, QWidget *parent = nullptr);\n  ~DTMFContactDialog();\n\n  DTMFContact *contact();\n\nprotected:\n  void construct();\n\nprivate:\n  DTMFContact *_myContact;\n  DTMFContact *_contact;\n  Config *_config;\n  Ui::DTMFContactDialog *ui;\n};\n\n#endif // DTMFCONTACTDIALOG_HH\n"
  },
  {
    "path": "src/dtmfcontactdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>DTMFContactDialog</class>\n <widget class=\"QDialog\" name=\"DTMFContactDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>379</width>\n    <height>252</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nameLabel\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"nameLineEdit\"/>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"numberLabel\">\n         <property name=\"text\">\n          <string>Number</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"numberLineEdit\"/>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"ringLabel\">\n         <property name=\"text\">\n          <string>Ring</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"ringCheckBox\"/>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>DTMFContactDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>DTMFContactDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/errormessageview.cc",
    "content": "#include \"errormessageview.hh\"\n#include \"ui_errormessageview.h\"\n\nErrorMessageView::ErrorMessageView(const ErrorStack &stack, QWidget *parent) :\n  QDialog(parent), ui(new Ui::ErrorMessageView)\n{\n  ui->setupUi(this);\n\n  QFont font = ui->errorMessage->font(); font.setBold(true);ui->errorMessage->setFont(font);\n\n  if (stack.isEmpty()) {\n    setWindowTitle(tr(\"Error: Unknown.\"));\n    ui->errorMessage->setText(\"An unknown error has occurred.\");\n    ui->errorStack->setVisible(false);\n    return;\n  }\n\n  setWindowTitle(tr(\"Error: %1\").arg(stack.message(0).message()));\n  if (1 == stack.count())\n    ui->errorMessage->setText(stack.message(0).message());\n  else\n    ui->errorMessage->setText(\n          stack.message(0).message() +\n          \":\" + stack.message(stack.count()-1).message());\n  ui->errorStack->setText(stack.format());\n}\n\nErrorMessageView::~ErrorMessageView()\n{\n  delete ui;\n}\n"
  },
  {
    "path": "src/errormessageview.hh",
    "content": "#ifndef ERRORMESSAGEVIEW_HH\n#define ERRORMESSAGEVIEW_HH\n\n#include <QDialog>\n#include \"errorstack.hh\"\n\nnamespace Ui {\n  class ErrorMessageView;\n}\n\nclass ErrorMessageView : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit ErrorMessageView(const ErrorStack &stack, QWidget *parent = nullptr);\n  ~ErrorMessageView();\n\nprivate:\n  Ui::ErrorMessageView *ui;\n};\n\n#endif // ERRORMESSAGEVIEW_HH\n"
  },
  {
    "path": "src/errormessageview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ErrorMessageView</class>\n <widget class=\"QDialog\" name=\"ErrorMessageView\">\n  <property name=\"windowModality\">\n   <enum>Qt::ApplicationModal</enum>\n  </property>\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"errorMessage\">\n     <property name=\"text\">\n      <string notr=\"true\">TextLabel</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"label_2\">\n     <property name=\"text\">\n      <string>Traceback:</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"errorStack\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"font\">\n      <font>\n       <family>Sans Serif</family>\n      </font>\n     </property>\n     <property name=\"text\">\n      <string notr=\"true\">TextLabel</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::PlainText</enum>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n     </property>\n     <property name=\"margin\">\n      <number>5</number>\n     </property>\n     <property name=\"indent\">\n      <number>-1</number>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Close</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ErrorMessageView</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ErrorMessageView</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/extensionview.cc",
    "content": "#include \"extensionview.hh\"\n#include \"ui_extensionview.h\"\n#include \"propertydelegate.hh\"\n#include \"extensionwrapper.hh\"\n#include \"settings.hh\"\n#include <QMessageBox>\n\n\nExtensionView::ExtensionView(QWidget *parent) :\n  QWidget(parent), ui(new Ui::ExtensionView), _model(nullptr)\n{\n  ui->setupUi(this);\n  ui->view->setModel(&_proxy);\n  ui->view->setItemDelegateForColumn(1, &_editor);\n  ui->view->setSelectionMode(QAbstractItemView::SingleSelection);\n  ui->view->setSelectionBehavior(QAbstractItemView::SelectRows);\n\n  ui->create->setEnabled(false);\n  ui->remove->setEnabled(false);\n\n  connect(ui->view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),\n          this, SLOT(onSelectionChanged(QItemSelection,QItemSelection)));\n  connect(ui->create, SIGNAL(clicked(bool)), this, SLOT(onCreate()));\n  connect(ui->remove, SIGNAL(clicked(bool)), this, SLOT(onDelete()));\n  connect(ui->view->header(), SIGNAL(sectionCountChanged(int,int)),\n          this, SLOT(loadSectionState()));\n  connect(ui->view->header(), SIGNAL(sectionResized(int,int,int)),\n          this, SLOT(storeSectionState()));\n}\n\nExtensionView::~ExtensionView()\n{\n  delete ui;\n}\n\nvoid\nExtensionView::setObject(ConfigItem *obj, Config *context) {\n  if (nullptr != _model)\n    _model->deleteLater();\n  _model = new PropertyWrapper(obj, this);\n  _proxy.setSourceModel(_model);\n  _editor.setConfig(context);\n}\n\n\nvoid\nExtensionView::onSelectionChanged(const QItemSelection &current, const QItemSelection &last) {\n  Q_UNUSED(last)\n  // If nothing is selected disable both\n  if (current.isEmpty() || (nullptr == _model)) {\n    ui->create->setEnabled(false);\n    ui->remove->setEnabled(false);\n    return;\n  }\n\n  // Get selected row\n  QModelIndex row = ui->view->selectionModel()->selectedRows().first();\n  if (! row.isValid()) {\n    ui->create->setEnabled(false);\n    ui->remove->setEnabled(false);\n    return;\n  }\n\n  if (_model->isProperty(_proxy.mapToSource(row))) {\n    ConfigItem *obj = _model->parentObject(_proxy.mapToSource(row));\n    QMetaProperty prop = _model->propertyAt(_proxy.mapToSource(row));\n    if ((nullptr == obj) || (! prop.isValid())) {\n      ui->create->setEnabled(false);\n      ui->remove->setEnabled(false);\n      return;\n    }\n\n    if (propIsInstance<ConfigItem>(prop)) {\n      ui->create->setEnabled(false);\n      ui->remove->setEnabled(false);\n      if (prop.read(obj).value<ConfigItem*>()) {\n        ui->create->setEnabled(false);\n        ui->remove->setEnabled(prop.isWritable());\n      } else {\n        ui->create->setEnabled(prop.isWritable());\n        ui->remove->setEnabled(false);\n      }\n    } else if (propIsInstance<ConfigObjectList>(prop)) {\n      ui->create->setEnabled(true);\n      ui->remove->setEnabled(false);\n    }\n  } else if (_model->isListElement(_proxy.mapToSource(row))) {\n    ui->create->setEnabled(false);\n    ui->remove->setEnabled(true);\n  }\n}\n\nvoid\nExtensionView::onCreate() {\n  if ((! ui->view->selectionModel()->hasSelection()) || (nullptr == _model))\n    return;\n\n  QModelIndex item = _proxy.mapToSource(\n        ui->view->selectionModel()->selectedRows(0).first());\n\n  if (! _model->isProperty(item))\n    return;\n\n  QMetaProperty prop = _model->propertyAt(item);\n  ConfigItem *obj = _model->parentObject(item);\n  if ((nullptr == obj) || (! prop.isValid()))\n    return;\n\n  if (propIsInstance<ConfigItem>(prop) && (! _model->createInstanceAt(item))) {\n    QMessageBox::critical(nullptr, tr(\"Cannot create extension.\"),\n                          tr(\"Cannot create extension, consider reporting a bug.\"));\n    return;\n  } else if (propIsInstance<ConfigObjectList>(prop) && !_model->createElementAt(item)) {\n    QMessageBox::critical(nullptr, tr(\"Cannot create list element.\"),\n                          tr(\"Cannot create list element, consider reporting a bug.\"));\n    return;\n  }\n\n  ui->view->selectionModel()->clearSelection();\n}\n\nvoid\nExtensionView::onDelete() {\n  if ((! ui->view->selectionModel()->hasSelection()) || (nullptr == _model))\n    return;\n  QModelIndex item = _proxy.mapToSource(\n        ui->view->selectionModel()->selectedRows(0).first());\n  if (_model->isProperty(item))\n    _model->deleteInstanceAt(item);\n  else if (_model->isListElement(item))\n    _model->deleteElementAt(item);\n  ui->view->selectionModel()->clearSelection();\n}\n\nvoid\nExtensionView::loadSectionState() {\n  Settings settings;\n  ui->view->header()->restoreState(settings.headerState(this->objectName()));\n}\n\nvoid\nExtensionView::storeSectionState() {\n  Settings settings;\n  settings.setHeaderState(this->objectName(), ui->view->header()->saveState());\n}\n"
  },
  {
    "path": "src/extensionview.hh",
    "content": "#ifndef EXTENSIONVIEW_HH\n#define EXTENSIONVIEW_HH\n\n#include <QWidget>\n#include \"extensionwrapper.hh\"\n#include \"propertydelegate.hh\"\n\nnamespace Ui {\n  class ExtensionView;\n}\n\nclass ConfigItem;\n\n\nclass ExtensionView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ExtensionView(QWidget *parent = nullptr);\n  ~ExtensionView() override;\n\npublic slots:\n  void setObject(ConfigItem *obj, Config *context);\n\nprotected slots:\n  void onSelectionChanged(const QItemSelection &current, const QItemSelection &last);\n  void onCreate();\n  void onDelete();\n  void loadSectionState();\n  void storeSectionState();\n\nprivate:\n  Ui::ExtensionView *ui;\n  PropertyWrapper   *_model;\n  ExtensionProxy     _proxy;\n  PropertyDelegate   _editor;\n};\n\n#endif // EXTENSIONVIEW_HH\n"
  },
  {
    "path": "src/extensionview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ExtensionView</class>\n <widget class=\"QWidget\" name=\"ExtensionView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTreeView\" name=\"view\"/>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"create\">\n       <property name=\"text\">\n        <string>Create</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remove\">\n       <property name=\"text\">\n        <string>Remove</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/extensionwrapper.cc",
    "content": "#include \"extensionwrapper.hh\"\n#include <QMetaProperty>\n#include <QMetaObject>\n#include \"logger.hh\"\n#include \"configreference.hh\"\n#include \"configobjecttypeselectiondialog.hh\"\n#include \"frequency.hh\"\n#include \"interval.hh\"\n#include <QGeoCoordinate>\n#include \"level.hh\"\n\n\n/* ******************************************************************************************** *\n * Implementation of ExtensionProxy\n * ******************************************************************************************** */\nExtensionProxy::ExtensionProxy(QObject *parent)\n  : QIdentityProxyModel(parent)\n{\n  // pass...\n}\n\nvoid\nExtensionProxy::setSourceModel(QAbstractItemModel *sourceModel) {\n  QIdentityProxyModel::setSourceModel(sourceModel);\n\n  _indexP2S.clear(); _indexS2P.clear();\n  if (PropertyWrapper *model = qobject_cast<PropertyWrapper *>(sourceModel)) {\n    // Create root element maps\n    for (int s=0,p=0; s<model->rowCount(QModelIndex()); s++) {\n      if (model->isExtension(model->index(s,0, QModelIndex()))) {\n        _indexP2S[p] = s;\n        _indexS2P[s] = p;\n        p++;\n      }\n    }\n  }\n}\n\nint\nExtensionProxy::rowCount(const QModelIndex &parent) const {\n  if (nullptr == sourceModel())\n    return 0;\n\n  // Non root elements\n  if (parent.isValid())\n    return sourceModel()->rowCount(mapToSource(parent));\n\n  // root element\n  return _indexP2S.count();\n}\n\nint\nExtensionProxy::columnCount(const QModelIndex &parent) const {\n  if (nullptr == sourceModel())\n    return 0;\n  return sourceModel()->columnCount(mapToSource(parent));\n}\n\nQModelIndex\nExtensionProxy::index(int row, int column, const QModelIndex &parent) const {\n  if (parent.isValid())\n    return mapFromSource(sourceModel()->index(row, column, mapToSource(parent)));\n  return createIndex(row, column, qobject_cast<PropertyWrapper *>(sourceModel())->root());\n}\n\nQModelIndex\nExtensionProxy::parent(const QModelIndex &child) const {\n  if (! child.isValid())\n    return QModelIndex();\n\n  ConfigItem *root = qobject_cast<PropertyWrapper*>(sourceModel())->root();\n  QObject *pptr = reinterpret_cast<QObject*>(child.internalPointer());\n  if ((root == pptr) || (nullptr == pptr))\n    return QModelIndex();\n\n  QObject *gpptr = pptr->parent();\n  if (nullptr == gpptr)\n    return QModelIndex();\n\n  if (root == gpptr) {\n    // Search for index of parent root:\n    const QMetaObject *meta = root->metaObject();\n    for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n      QMetaProperty prop = meta->property(p);\n      if (! prop.isValid())\n        continue;\n      if (prop.read(root).value<ConfigItem *>() == pptr) {\n        return createIndex(_indexS2P[p-QObject::staticMetaObject.propertyCount()], 0,\n            reinterpret_cast<quintptr>(root));\n      }\n    }\n    return QModelIndex();\n  }\n\n  return mapFromSource(sourceModel()->parent(child));\n}\n\nQModelIndex\nExtensionProxy::mapToSource(const QModelIndex &proxyIndex) const {\n  // root maps to root\n  if (! proxyIndex.isValid())\n    return QModelIndex();\n  // If no source -> blub.\n  if (nullptr == sourceModel())\n    return QModelIndex();\n\n  // If index is not an immediate child of root -> map 1:1\n  if (proxyIndex.parent().isValid()) {\n    return sourceModel()->index(\n          proxyIndex.row(), proxyIndex.column(), mapToSource(proxyIndex.parent()));\n  }\n\n  // If index is an immediate child of root -> map only extensions\n  if (_indexP2S.contains(proxyIndex.row()))\n    return sourceModel()->index(\n          _indexP2S[proxyIndex.row()], proxyIndex.column(), QModelIndex());\n\n  return QModelIndex();\n}\n\nQModelIndex\nExtensionProxy::mapFromSource(const QModelIndex &sourceIndex) const {\n  // root maps to root\n  if (! sourceIndex.isValid())\n    return QModelIndex();\n  // If no source -> blub.\n  if (nullptr == sourceModel())\n    return QModelIndex();\n\n  // If index is not an immediate child of root -> map 1:1\n  if (sourceModel()->parent(sourceIndex).isValid()) {\n    return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());\n  }\n\n  if (_indexS2P.contains(sourceIndex.row()))\n    return createIndex(_indexS2P[sourceIndex.row()], sourceIndex.column(), sourceIndex.internalPointer());\n\n  return QModelIndex();\n}\n\n\n/* ******************************************************************************************** *\n * Implementation of PropertyWrapper\n * ******************************************************************************************** */\nPropertyWrapper::PropertyWrapper(ConfigItem *obj, QObject *parent)\n  : QAbstractItemModel(parent), _object(obj)\n{\n  if (_object) {\n    connect(_object, SIGNAL(beginClear()), this, SLOT(onItemClearing()));\n    connect(_object, SIGNAL(endClear()), this, SLOT(onItemCleared()));\n  }\n}\n\n\nConfigItem *\nPropertyWrapper::root() const {\n  return _object;\n}\n\nConfigItem *\nPropertyWrapper::item(const QModelIndex &item) const {\n  if (! item.isValid())\n    return nullptr;\n\n  // Get pointer to parent (either list or item)\n  QObject *pptr = _object;\n  if (nullptr != item.internalPointer())\n    pptr = reinterpret_cast<QObject *>(item.internalPointer());\n\n  if (ConfigItem *pobj = qobject_cast<ConfigItem*>(pptr)) {\n    // If parent is item find corresponding property\n    const QMetaObject *meta = pobj->metaObject();\n    int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount();\n    if (item.row() < pcount) {\n      QMetaProperty prop = meta->property(QObject::staticMetaObject.propertyCount() + item.row());\n      return prop.read(pobj).value<ConfigItem *>();\n    }\n  } else if (ConfigObjectList *plst = qobject_cast<ConfigObjectList*>(pptr)) {\n    // If parent is list\n    if (item.row() < plst->count())\n      return plst->get(item.row());\n  }\n\n  return nullptr;\n}\n\nConfigObjectList *\nPropertyWrapper::list(const QModelIndex &item) const {\n  if (! item.isValid())\n    return nullptr;\n\n  QObject *pptr = reinterpret_cast<QObject*>(item.internalPointer());\n  ConfigItem *pobj = qobject_cast<ConfigItem*>(pptr);\n  if (nullptr == pobj)\n    return nullptr;\n\n  const QMetaObject *meta = pobj->metaObject();\n  int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount();\n  if (item.row() < pcount) {\n    QMetaProperty prop = meta->property(QObject::staticMetaObject.propertyCount() + item.row());\n    return prop.read(pobj).value<ConfigObjectList *>();\n  }\n\n  return nullptr;\n}\n\nConfigObjectList *\nPropertyWrapper::parentList(const QModelIndex &index) const {\n  if (! index.isValid())\n    return nullptr;\n\n  QObject *pptr = reinterpret_cast<QObject *>(index.internalId());\n  if (nullptr == pptr)\n    return nullptr;\n\n  if (ConfigObjectList *lst = qobject_cast<ConfigObjectList*>(pptr))\n    return lst;\n\n  return nullptr;\n}\n\nConfigItem *\nPropertyWrapper::parentObject(const QModelIndex &index) const {\n  if (! index.isValid())\n     return _object;\n\n  QObject *pptr = reinterpret_cast<QObject*>(index.internalId());\n  return qobject_cast<ConfigItem*>(pptr);\n}\n\nQMetaProperty\nPropertyWrapper::propertyAt(const QModelIndex &index) const {\n  ConfigItem *pobj = parentObject(index);\n  const QMetaObject *meta = pobj->metaObject();\n\n  int propCount = meta->propertyCount() - QObject::staticMetaObject.propertyCount();\n  if (index.row() < propCount)\n    return meta->property(index.row()+QObject::staticMetaObject.propertyCount());\n  return QMetaProperty();\n}\n\n\nbool\nPropertyWrapper::isExtension(const QModelIndex &index) const {\n  if (! index.isValid())\n    return false;\n  QMetaProperty prop = propertyAt(index);\n  return propIsInstance<ConfigExtension>(prop);\n}\n\nbool\nPropertyWrapper::isProperty(const QModelIndex &index) const {\n  if (! index.isValid())\n    return true;\n  // Index is property if parent is an ConfigItem\n  QObject *pptr = reinterpret_cast<QObject *>(index.internalPointer());\n  return nullptr != qobject_cast<ConfigItem*>(pptr);\n}\n\nbool\nPropertyWrapper::isListElement(const QModelIndex &index) const {\n  if (! index.isValid())\n    return false;\n  // Index is list element if parent is ConfigObjectList\n  QObject *pptr = reinterpret_cast<QObject *>(index.internalPointer());\n  if (nullptr == pptr)\n    return false;\n  return nullptr != qobject_cast<ConfigObjectList*>(pptr);\n}\n\nbool\nPropertyWrapper::createInstanceAt(const QModelIndex &item) {\n  if (! isProperty(item))\n    return false;\n\n  ConfigItem *obj = parentObject(item);\n  QMetaProperty prop = propertyAt(item);\n  if ((nullptr == obj) || (! prop.isValid()))\n    return false;\n  // Check type of property\n  if (! propIsInstance<ConfigItem>(prop))\n    return false;\n  // If property is already set -> abort\n  if (prop.read(obj).value<ConfigItem*>())\n    return false;\n  // Get TypeObject of property\n  if (QMetaType::UnknownType == prop.userType())\n    return false;\n  QMetaType type(prop.userType());\n  if (! (QMetaType::PointerToQObject & type.flags()))\n    return false;\n  const QMetaObject *propType = type.metaObject();\n  // Instantiate extension\n  ConfigItem *ext = qobject_cast<ConfigItem *>(\n        propType->newInstance(QGenericArgument(nullptr, obj)));\n  if (nullptr == ext)\n    return false;\n  // store item\n  beginInsertRows(item, 0, ext->metaObject()->propertyCount());\n  prop.write(obj, QVariant::fromValue(ext));\n  endInsertRows();\n  emit dataChanged(index(item.row(), 0, item.parent()),\n                   index(item.row(), 2, item.parent()));\n  emit dataChanged(index(0, 0, item),\n                   index(ext->metaObject()->propertyCount(),2, item));\n  return true;\n}\n\nbool\nPropertyWrapper::deleteInstanceAt(const QModelIndex &item) {\n  ConfigItem *obj = parentObject(item);\n  QMetaProperty prop = propertyAt(item);\n  if ((nullptr == obj) || (! prop.isValid()))\n    return false;\n  // Check type of property\n  if (! propIsInstance<ConfigItem>(prop))\n    return false;\n  // If property is set -> delete\n  if (prop.read(obj).value<ConfigItem*>()) {\n    if (! prop.isWritable())\n      return false;\n    beginRemoveRows(item, 0, rowCount(item));\n    prop.write(obj, QVariant(prop.metaType()));\n    endRemoveRows();\n    return true;\n  }\n  return false;\n}\n\nbool\nPropertyWrapper::createElementAt(const QModelIndex &item) {\n  ConfigItem *obj = parentObject(item);\n  QMetaProperty prop = propertyAt(item);\n  if ((nullptr == obj) || (! prop.isValid()))\n    return false;\n\n  ConfigObjectList *lst = prop.read(obj).value<ConfigObjectList*>();\n  if (nullptr == lst)\n    return false;\n\n  ConfigObjectTypeSelectionDialog dialog(lst->elementTypes());\n  if (QDialog::Accepted != dialog.exec())\n    return true;\n\n  QMetaObject type= dialog.selectedType();\n\n  // Instantiate element\n  ConfigObject *element = qobject_cast<ConfigObject *>(\n        type.newInstance(QGenericArgument(nullptr, lst)));\n  if (nullptr == element)\n    return false;\n  element->setName(tr(\"new element\"));\n\n  // store item\n  beginInsertRows(item,lst->count(), lst->count()+1);\n  lst->add(element);\n  endInsertRows();\n  emit dataChanged(index(item.row(), 0, item.parent()),\n                   index(item.row(), 2, item.parent()));\n\n  QModelIndex elementIndex = createIndex(lst->count(), 0, lst);\n  beginInsertRows(elementIndex, 0, element->metaObject()->propertyCount());\n  endInsertRows();\n  emit dataChanged(index(0, 0, item),\n                   index(lst->count(), 2, item));\n  return true;\n}\n\nbool\nPropertyWrapper::deleteElementAt(const QModelIndex &item) {\n  ConfigObjectList *lst = parentList(item);\n  if (nullptr == lst)\n    return false;\n  if (item.row() >= lst->count())\n    return false;\n\n  beginRemoveRows(item, 0, rowCount(item));\n  lst->del(lst->get(item.row()));\n  endRemoveRows();\n\n  return true;\n}\n\nQModelIndex\nPropertyWrapper::index(int row, int column, const QModelIndex &parent) const {\n  if (! parent.isValid()) {\n    // Handle root element\n    const QMetaObject *meta = _object->metaObject();\n    int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount();\n    if (row < pcount)\n      return createIndex(row, column, _object);\n  } else if (ConfigItem *pobj = item(parent)) {\n    const QMetaObject *meta = pobj->metaObject();\n    int pcount = meta->propertyCount() - QObject::staticMetaObject.propertyCount();\n    if (row < pcount)\n      return createIndex(row, column, pobj);\n  } else if (ConfigObjectList *plst = list(parent)) {\n    if (row < plst->count())\n      return createIndex(row, column, plst);\n  }\n  return QModelIndex();\n}\n\nQModelIndex\nPropertyWrapper::parent(const QModelIndex &child) const {\n  if (! child.isValid())\n    return QModelIndex();\n\n  QObject *pptr = reinterpret_cast<QObject*>(child.internalPointer());\n  // Handle root\n  if (ConfigItem *pobj = qobject_cast<ConfigItem*>(pptr)) {\n    if (_object == pobj)\n      return QModelIndex();\n  }\n\n  QObject *gpptr = pptr->parent();\n  // Should not happen\n  if (nullptr == gpptr)\n    return QModelIndex();\n\n  if (ConfigItem *gp = qobject_cast<ConfigItem*>(gpptr)) {\n    // If grand parent is item:\n    // Search for parent in grand-parent's properties\n    const QMetaObject *meta = gp->metaObject();\n    for (int p=QObject::staticMetaObject.propertyCount(); p<meta->propertyCount(); p++) {\n      QMetaProperty prop = meta->property(p);\n      if (! prop.isValid())\n        continue;\n      if (prop.read(gp).value<QObject *>() == pptr) {\n        return createIndex(p-QObject::staticMetaObject.propertyCount(), 0, reinterpret_cast<quintptr>(gp));\n      }\n    }\n  } else if (ConfigObjectList *gp = qobject_cast<ConfigObjectList*>(gpptr)) {\n    // If grand parent is item:\n    // Search for parent in grand-parent's elements\n    for (int i=0; i<gp->count(); i++) {\n      if (pptr == gp->get(i))\n        return createIndex(i, 0, reinterpret_cast<quintptr>(gp));\n    }\n  }\n\n  return QModelIndex();\n}\n\nint\nPropertyWrapper::rowCount(const QModelIndex &parent) const {\n  if (! parent.isValid()) {\n    // If parent is root -> handle _object\n    const QMetaObject *meta = _object->metaObject();\n    return (meta->propertyCount() - QObject::staticMetaObject.propertyCount());\n  }\n\n  if (ConfigItem *pobj = item(parent)) {\n    // If parent is item -> return property count\n    const QMetaObject *meta = pobj->metaObject();\n    return (meta->propertyCount() - QObject::staticMetaObject.propertyCount());\n  } else if (ConfigObjectList *plst = list(parent)) {\n    // If parent is list -> return element count.\n    return plst->count();\n  }\n\n  return 0;\n}\n\nint\nPropertyWrapper::columnCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent)\n  return 3;\n}\n\nQt::ItemFlags\nPropertyWrapper::flags(const QModelIndex &index) const {\n  if (isProperty(index)) {\n    // check if property is a config object or atomic (or reference)\n    QMetaProperty prop = propertyAt(index);\n    // Object can be selected and expanded, but not edited directly\n    if (propIsInstance<ConfigItem>(prop) || propIsInstance<ConfigObjectList>(prop))\n      return Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n    // References are edited directly by combo-box. See PropertyDelegate. They cannot be expanded.\n    if (propIsInstance<ConfigObjectReference>(prop) && prop.isScriptable() && (1 == index.column()))\n      return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;\n    // Atomic properties are directly editable, see also PropertyDelegate. They cannot be expanded.\n    if (prop.isWritable() && (1 == index.column()))\n      return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemNeverHasChildren;\n    // Some default values.\n    return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;\n  } else if (isListElement(index)) {\n    return Qt::ItemIsSelectable | Qt::ItemIsEnabled;\n  }\n\n  return Qt::NoItemFlags;\n}\n\nbool\nPropertyWrapper::hasChildren(const QModelIndex &element) const {\n  // If root element\n  if (! element.isValid())\n    return true;\n  // Get parent object (might be item or list)\n  QObject *pptr = reinterpret_cast<QObject *>(element.internalId());\n\n  if (ConfigItem* pobj = qobject_cast<ConfigItem *>(pptr)) {\n    // If parent is item -> get addressed property\n    QMetaProperty prop = propertyAt(element);\n    // If property is an item or list (and is set)\n    if (propIsInstance<ConfigItem>(prop)) {\n      return nullptr != prop.read(pobj).value<ConfigItem*>();\n    } else if (propIsInstance<ConfigObjectList>(prop)) {\n      // If element and there are elements within the list.\n      ConfigObjectList *lst = prop.read(pobj).value<ConfigObjectList*>();\n      return (nullptr != lst) && (lst->count()>0);\n    }\n  } else if (ConfigObjectList *lst = qobject_cast<ConfigObjectList*>(pptr)) {\n    // If parent is a list, check row index. List elements are always objects and have children.\n    return lst->count() > element.row();\n  }\n\n  return false;\n}\n\nQVariant\nPropertyWrapper::headerData(int section, Qt::Orientation orientation, int role) const {\n  // Only handle horizontal display data\n  if ((Qt::Horizontal != orientation) || (Qt::DisplayRole != role))\n    return QAbstractItemModel::headerData(section, orientation, role);\n  // Dispatch by section index\n  switch (section) {\n  case 0: return tr(\"Property\");\n  case 1: return tr(\"Value\");\n  case 2: return tr(\"Description\");\n  default: break;\n  }\n  // default\n  return QAbstractItemModel::headerData(section, orientation, role);\n}\n\nQVariant\nPropertyWrapper::data(const QModelIndex &index, int role) const {\n  if (! index.isValid())\n    return QVariant();\n\n  if (isProperty(index)) {\n    ConfigItem *pobj = parentObject(index);\n    QMetaProperty prop = propertyAt(index);\n    if (0 == index.column()) {\n      if (Qt::DisplayRole == role)\n        return prop.name();\n    } else if ((2 == index.column()) && (Qt::DisplayRole == role)) {\n      if (propIsInstance<ConfigItem>(prop)) {\n        ConfigItem *item = prop.read(pobj).value<ConfigItem*>();\n        if (item && item->hasDescription())\n          return item->description();\n      }\n      if (pobj->hasDescription(prop))\n        return pobj->description(prop);\n      return QVariant();\n    } else if (Qt::ToolTipRole == role) {\n      if (propIsInstance<ConfigItem>(prop)) {\n        ConfigItem *item = prop.read(pobj).value<ConfigItem*>();\n        if (item && item->hasLongDescription())\n          return item->longDescription();\n      }\n      if (pobj->hasLongDescription(prop))\n        return pobj->longDescription(prop);\n      if (pobj->hasDescription(prop))\n        return pobj->description(prop);\n      return QVariant();\n    }\n\n    QVariant value = prop.read(pobj);\n    if (prop.isFlagType()) {\n      if (Qt::EditRole == role)\n        return value;\n      QMetaEnum e = prop.enumerator();\n      if (Qt::DisplayRole == role)\n        return e.valueToKeys(value.toInt());\n    } if (prop.isEnumType() && ((Qt::DisplayRole == role) || (Qt::EditRole == role))) {\n      QMetaEnum e = prop.enumerator();\n      const char *key = e.valueToKey(value.toInt());\n      if (nullptr == key) {\n        logError() << \"Cannot map value \" << value.toUInt()\n                   << \" to enum \" << e.name()\n                   << \". Ignore attribute but this points to an incompatibility in some codeplug. \"\n                   << \"Consider reporting it to https://github.com/hmatuschek/qdmr/issues.\";\n        return QVariant();\n      }\n      return QString(key);\n    } else if ((QMetaType::Bool == prop.typeId()) && (Qt::EditRole == role)) {\n      return value;\n    } else if ((QMetaType::Bool == prop.typeId()) && (Qt::DisplayRole == role)) {\n      if (value.toBool())\n        return tr(\"true\");\n      return tr(\"false\");\n    } else if ( ((QMetaType::Int == prop.typeId()) || (QMetaType::UInt == prop.typeId()) ||\n                 (QMetaType::Double == prop.typeId()) || (QMetaType::QString == prop.typeId()))\n                && ((Qt::DisplayRole == role) || (Qt::EditRole==role)) ) {\n      return value;\n    } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n      if (Qt::DisplayRole == role)\n        return value.value<Frequency>().format();\n      else if (Qt::EditRole == role)\n        return value;\n    } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n      if (Qt::DisplayRole == role) {\n        if (value.value<Interval>().isInfinite())\n          return QChar(0x221e);\n        return value.value<Interval>().format();\n      } else if (Qt::EditRole == role) {\n        return value;\n      }\n    } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n      if (Qt::DisplayRole == role) {\n        if (value.value<Level>().isInvalid())\n          return tr(\"None\");\n        if (value.value<Level>().isNull())\n          return tr(\"Off\");\n        return value.value<Level>().value();\n      } else if (Qt::EditRole == role) {\n        return value;\n      }\n    } else if (QMetaType::fromType<QGeoCoordinate>().id() == prop.typeId()) {\n      if (Qt::DisplayRole == role) {\n        if (value.value<QGeoCoordinate>().isValid())\n          return QString(\"%1 / %2\")\n              .arg(value.value<QGeoCoordinate>().latitude())\n              .arg(value.value<QGeoCoordinate>().longitude());\n        return tr(\"[None]\");\n      }\n      if (Qt::EditRole == role)\n        return value;\n    } else if (value.value<ConfigObjectReference *>() && (Qt::DisplayRole == role)) {\n      ConfigObjectReference *ref = value.value<ConfigObjectReference *>();\n      ConfigObject *obj = ref->as<ConfigObject>();\n      if (nullptr == obj)\n        return tr(\"[None]\");\n      return QString(\"%1 (%2)\").arg(obj->name()).arg(obj->metaObject()->className());\n    } else if (value.value<ConfigObjectReference *>() && (Qt::EditRole == role)) {\n      return value;\n    } else if (propIsInstance<ConfigItem>(prop)) {\n      ConfigItem *item = value.value<ConfigItem*>();\n      if (Qt::DisplayRole == role) {\n        if (nullptr == item)\n          return tr(\"[None]\");\n        else\n          return tr(\"Instance of %1\").arg(item->metaObject()->className());\n      }\n    } else if (propIsInstance<ConfigObjectList>(prop)) {\n      ConfigObjectList *lst = value.value<ConfigObjectList*>();\n      if (Qt::DisplayRole == role)\n        return tr(\"List of %1 instances\").arg(lst->classNames().join(\", \"));\n    } else if (Qt::DisplayRole == role) {\n      logWarn() << \"Unhandled property '\" << prop.name() << \"' of type \" << prop.typeName() << \".\";\n    }\n  } else if (isListElement(index)) {\n    ConfigObjectList *lst = parentList(index);\n    if (index.row() >= lst->count())\n      return QVariant();\n\n    if ((0 == index.column()) && (Qt::DisplayRole == role))\n      return lst->get(index.row())->name();\n    else if ((1 == index.column()) && (Qt::DisplayRole == role))\n      return lst->get(index.row())->description();\n    else if ((2 == index.column()) && (Qt::DisplayRole == role))\n      return lst->get(index.row())->longDescription();\n  }\n\n\n  return QVariant();\n}\n\nvoid\nPropertyWrapper::onItemClearing() {\n  beginResetModel();\n}\n\nvoid\nPropertyWrapper::onItemCleared() {\n  endResetModel();\n}\n"
  },
  {
    "path": "src/extensionwrapper.hh",
    "content": "#ifndef EXTENSIONWRAPPER_HH\n#define EXTENSIONWRAPPER_HH\n\n#include \"configobject.hh\"\n#include <QAbstractItemModel>\n#include <QIdentityProxyModel>\n#include <QMetaProperty>\n#include <QPointer>\n\n\nclass ExtensionProxy: public QIdentityProxyModel\n{\n  Q_OBJECT\n\npublic:\n  explicit ExtensionProxy(QObject *parent=nullptr);\n\npublic:\n  void setSourceModel(QAbstractItemModel *sourceModel);\n\n  int rowCount(const QModelIndex &parent) const;\n  int columnCount(const QModelIndex &parent) const;\n\n  QModelIndex index(int row, int column, const QModelIndex &parent) const;\n  QModelIndex parent(const QModelIndex &child) const;\n\n  QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;\n  //QItemSelection mapSelectionFromSource(const QItemSelection &selection) const;\n  QModelIndex mapToSource(const QModelIndex &proxyIndex) const;\n  //QItemSelection mapSelectionToSource(const QItemSelection &selection) const;\n\nprotected:\n  QHash<int, int> _indexS2P, _indexP2S;\n};\n\n\nclass PropertyWrapper: public QAbstractItemModel\n{\n  Q_OBJECT\n\npublic:\n  PropertyWrapper(ConfigItem *obj, QObject *parent=nullptr);\n\n  ConfigItem *root() const;\n  ConfigItem *item(const QModelIndex &item) const;\n  ConfigObjectList *list(const QModelIndex &item) const;\n\n  ConfigObjectList *parentList(const QModelIndex &index) const;\n  ConfigItem *parentObject(const QModelIndex &index) const;\n  QMetaProperty propertyAt(const QModelIndex &index) const;\n\n  bool isProperty(const QModelIndex &index) const;\n  bool isExtension(const QModelIndex &index) const;\n  bool isListElement(const QModelIndex &index) const;\n\n  bool createInstanceAt(const QModelIndex &item);\n  bool deleteInstanceAt(const QModelIndex &item);\n\n  bool createElementAt(const QModelIndex &item);\n  bool deleteElementAt(const QModelIndex &item);\n\n  QModelIndex index(int row, int column, const QModelIndex &parent) const;\n  QModelIndex parent(const QModelIndex &child) const;\n\n  int rowCount(const QModelIndex &parent) const;\n  int columnCount(const QModelIndex &parent) const;\n\n  Qt::ItemFlags flags(const QModelIndex &index) const;\n  bool hasChildren(const QModelIndex &parent) const;\n  QVariant data(const QModelIndex &index, int role) const;\n  QVariant headerData(int section, Qt::Orientation orientation, int role) const;\n\n\nprotected slots:\n  void onItemClearing();\n  void onItemCleared();\n\nprotected:\n  ConfigItem *_object;\n  QPointer<ConfigItem> _original;\n};\n\n#endif // EXTENSIONWRAPPER_HH\n"
  },
  {
    "path": "src/flageditdialog.cc",
    "content": "#include \"flageditdialog.hh\"\n#include \"ui_flageditdialog.h\"\n\nFlagEditDialog::FlagEditDialog(QMetaEnum metaFlag, QWidget *parent)\n  : QDialog(parent), ui(new Ui::FlagEditDialog), _metaFlag(metaFlag)\n{\n  ui->setupUi(this);\n  for (int i=0; i<metaFlag.keyCount(); i++) {\n    auto item = new QListWidgetItem(metaFlag.key(i));\n    item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable\n                   | Qt::ItemNeverHasChildren);\n    item->setData(Qt::UserRole, QByteArray(metaFlag.key(i)));\n    ui->list->addItem(item);\n  }\n}\n\n\nint\nFlagEditDialog::value() const {\n  QByteArray keys;\n  for (int i=0; i<ui->list->count(); i++) {\n    if (Qt::Checked == ui->list->item(i)->checkState()) {\n      if (! keys.isEmpty())\n        keys += \"|\";\n      keys.append(ui->list->item(i)->data(Qt::UserRole).toByteArray());\n    }\n  }\n  if (keys.isEmpty())\n    return 0;\n  return _metaFlag.keysToValue(keys);\n}\n\n\nvoid\nFlagEditDialog::setValue(int value) {\n  QSet<QByteArray> keys;\n  for (auto key: _metaFlag.valueToKeys(value).split('|'))\n    keys.insert(key);\n  for (int i=0; i<ui->list->count(); i++) {\n    if (keys.contains(ui->list->item(i)->data(Qt::UserRole).toByteArray()))\n      ui->list->item(i)->setCheckState(Qt::Checked);\n    else\n      ui->list->item(i)->setCheckState(Qt::Unchecked);\n  }\n}\n\nFlagEditDialog::~FlagEditDialog() {\n  delete ui;\n}\n"
  },
  {
    "path": "src/flageditdialog.hh",
    "content": "#ifndef FLAGEDITDIALOG_HH\n#define FLAGEDITDIALOG_HH\n\n#include <QDialog>\n#include <QMetaEnum>\n\nnamespace Ui {\n  class FlagEditDialog;\n}\n\nclass FlagEditDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit FlagEditDialog(QMetaEnum metaFlag, QWidget *parent = nullptr);\n  ~FlagEditDialog();\n\n  int value() const;\n  void setValue(int value);\n\nprivate:\n  Ui::FlagEditDialog *ui;\n  QMetaEnum _metaFlag;\n};\n\n#endif // FLAGEDITDIALOG_HH\n"
  },
  {
    "path": "src/flageditdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>FlagEditDialog</class>\n <widget class=\"QDialog\" name=\"FlagEditDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>247</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Select Flags</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QListWidget\" name=\"list\"/>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>FlagEditDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>FlagEditDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/fmchanneldialog.cc",
    "content": "#include \"fmchanneldialog.hh\"\n#include \"application.hh\"\n#include <QCompleter>\n#include \"settings.hh\"\n#include \"repeatercompleter.hh\"\n#include \"repeaterdatabase.hh\"\n#include \"ui_channeldialog.h\"\n#include \"squelchedit.hh\"\n#include \"admitselect.hh\"\n#include \"selectivecallbox.hh\"\n#include \"bandwidthselect.hh\"\n#include \"aprsselect.hh\"\n\n\n\n/* ********************************************************************************************* *\n * Implementation of FMChannelDialog\n * ********************************************************************************************* */\nFMChannelDialog::FMChannelDialog(Config *config, QWidget *parent)\n  : ChannelDialog(config, parent), _clone(nullptr), _orig(nullptr), _squelch(nullptr), _admit(nullptr),\n  _rxTone(nullptr), _txTone(nullptr), _bandwidth(nullptr), _aprs(nullptr)\n{\n  auto app = qobject_cast<Application *>(qApp);\n  auto filter = new FMRepeaterFilter(app->repeater(), app->position(), this);\n  filter->setSourceModel(app->repeater());\n  auto completer = new RepeaterCompleter(2, app->repeater(), this);\n  completer->setModel(filter);\n  ui->channelName->setCompleter(completer);\n  connect(completer, QOverload<const QModelIndex &>::of(&QCompleter::activated),\n          this, &FMChannelDialog::onRepeaterSelected);\n\n  ui->rightForm->addRow(tr(\"Squelch\"),\n                        _squelch = new ChannelSquelchEdit(config->settings()->audio()->squelch()));\n  ui->rightForm->addRow(tr(\"Tx Admit\"),\n                        _admit = new FMAdmitSelect());\n  ui->rightForm->addRow(tr(\"Rx Tone\"),\n                        _rxTone = new SelectiveCallBox());\n  ui->rightForm->addRow(tr(\"Tx Tone\"),\n                        _txTone = new SelectiveCallBox());\n  ui->rightForm->addRow(tr(\"Bandwidth\"),\n                        _bandwidth = new BandwidthSelect());\n  ui->rightForm->addRow(tr(\"APRS\"),\n                        _aprs = new FMAPRSSelect(config));\n}\n\n\nvoid\nFMChannelDialog::setChannel(FMChannel *channel) {\n  if (_clone) {\n    delete _clone;\n    _clone = nullptr;\n  }\n\n  _orig = channel;\n  if (_orig.isNull()) return;\n  _clone = _orig->clone()->as<FMChannel>();\n  _clone->setParent(this);\n  ChannelDialog::setChannel(_clone);\n\n  _squelch->setChannel(_clone);\n  _admit->setAdmit(_clone->admit());\n  _rxTone->setSelectiveCall(_clone->rxTone());\n  _txTone->setSelectiveCall(_clone->txTone());\n  _bandwidth->setBandwidth(_clone->bandwidth());\n  _aprs->setAPRSSystem(_clone->aprs());\n}\n\nvoid\nFMChannelDialog::accept()\n{\n  _squelch->accept();\n  _clone->setAdmit(_admit->admit());\n  _clone->setRXTone(_rxTone->selectiveCall());\n  _clone->setTXTone(_txTone->selectiveCall());\n  _clone->setBandwidth(_bandwidth->bandwidth());\n  _clone->setAPRS(_aprs->aprsSystem());\n\n  ChannelDialog::accept();\n\n  _orig->copy(*_clone);\n}\n\nvoid\nFMChannelDialog::onRepeaterSelected(const QModelIndex &index) {\n  auto app = qobject_cast<Application *>(qApp);\n\n  QModelIndex src = qobject_cast<QAbstractProxyModel*>(\n        ui->channelName->completer()->completionModel())->mapToSource(index);\n  src = qobject_cast<QAbstractProxyModel*>(\n        ui->channelName->completer()->model())->mapToSource(src);\n  Frequency rx = app->repeater()->get(src.row()).rxFrequency();\n  Frequency tx = app->repeater()->get(src.row()).txFrequency();\n  _rxTone->setSelectiveCall(app->repeater()->get(src.row()).rxTone());\n  _txTone->setSelectiveCall(app->repeater()->get(src.row()).txTone());\n  ui->rxFrequency->setText(rx.format());\n  ui->txFrequency->setText(tx.format());\n  updateOffsetFrequency();\n}\n\n\n\n"
  },
  {
    "path": "src/fmchanneldialog.hh",
    "content": "#ifndef FMCHANNELDIALOG_H\n#define FMCHANNELDIALOG_H\n\n#include \"channeldialog.hh\"\n#include \"config.hh\"\n\nclass ChannelSquelchEdit;\nclass FMAdmitSelect;\nclass SelectiveCallBox;\nclass BandwidthSelect;\nclass FMAPRSSelect;\n\n\nclass FMChannelDialog: public ChannelDialog\n{\n  Q_OBJECT\n\npublic:\n  FMChannelDialog(Config *config, QWidget *parent=nullptr);\n\n  void setChannel(FMChannel *fm);\n\npublic slots:\n  void accept() override;\n\nprotected slots:\n  void onRepeaterSelected(const QModelIndex &index);\n\nprotected:\n  FMChannel *_clone;\n  QPointer<FMChannel> _orig;\n  ChannelSquelchEdit *_squelch;\n  FMAdmitSelect   *_admit;\n  SelectiveCallBox *_rxTone, *_txTone;\n  BandwidthSelect *_bandwidth;\n  FMAPRSSelect *_aprs;\n};\n\n\n#endif // FMCHANNELDIALOG_H\n"
  },
  {
    "path": "src/generalsettingsview.cc",
    "content": "#include \"generalsettingsview.hh\"\n\n#include \"channel_type_edit.hh\"\n#include \"config.hh\"\n#include \"ui_generalsettingsview.h\"\n\nGeneralSettingsView::GeneralSettingsView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::GeneralSettingsView), _config(config) {\n  ui->setupUi(this);\n\n  ui->powerValue->setItemData(0, (unsigned)Channel::Power::Max);\n  ui->powerValue->setItemData(1, (unsigned)Channel::Power::High);\n  ui->powerValue->setItemData(2, (unsigned)Channel::Power::Mid);\n  ui->powerValue->setItemData(3, (unsigned)Channel::Power::Low);\n  ui->powerValue->setItemData(4, (unsigned)Channel::Power::Min);\n\n  ui->extensionView->setObjectName(\"radioSettingsExtension\");\n  ui->extensionView->setObject(_config->settings(), _config);\n  ui->bootMelodyEdit->setMelody(_config->settings()->tone()->bootMelody());\n  ui->callStartEdit->setMelody(_config->settings()->tone()->callStartMelody());\n  ui->callEndEdit->setMelody(_config->settings()->tone()->callEndMelody());\n  ui->channelIdleEdit->setMelody(_config->settings()->tone()->channelIdleMelody());\n  ui->callResetEdit->setMelody(_config->settings()->tone()->callResetMelody());\n\n  // update view from config\n  onConfigModified();\n\n  connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n  connect(ui->introLine1, &QLineEdit::editingFinished, [this]() {\n    this->_config->settings()->boot()->setMessage1(ui->introLine1->text().simplified());\n  });\n  connect(ui->introLine2, &QLineEdit::editingFinished, [this]() {\n    this->_config->settings()->boot()->setMessage2(ui->introLine2->text().simplified());\n  });\n\n  connect(ui->squelch, &QSpinBox::valueChanged, [this](int value) {\n    this->_config->settings()->audio()->setSquelch(Level::fromValue(value));\n    if (this->ui->dmrSquelchDefault->isChecked())\n      this->ui->dmrSquelch->setValue(value);\n  });\n  connect(ui->dmrSquelch, &QSpinBox::valueChanged, [this](int value) {\n    if (! this->ui->dmrSquelchDefault->isChecked())\n      this->_config->settings()->audio()->setDMRSquelch(Level::fromValue(value));\n  });\n  connect(ui->dmrSquelchDefault, &QCheckBox::toggled, [this](bool checked) {\n    if (checked) {\n      this->_config->settings()->audio()->disableDMRSquelch();\n      this->ui->dmrSquelch->setEnabled(false);\n    } else {\n      this->_config->settings()->audio()->setDMRSquelch(Level::fromValue(this->ui->dmrSquelch->value()));\n      this->ui->dmrSquelch->setEnabled(true);\n    }\n  });\n  connect(ui->mic, &QSpinBox::valueChanged, [this](int value) {\n    this->_config->settings()->audio()->setMicGain(Level::fromValue(value));\n  });\n  connect(ui->fmMicGain, &QSpinBox::valueChanged, [this] (int value) {\n    this->_config->settings()->audio()->setFMMicGain(Level::fromValue(value));\n  });\n  connect(ui->voxValue, &QSpinBox::valueChanged, [this](int value) {\n    if (0 == value)\n      this->_config->settings()->audio()->disableVox();\n    else\n      this->_config->settings()->audio()->setVox(Level::fromValue(value));\n  });\n  connect(ui->voxDelay, &QLineEdit::editingFinished, [this]() {\n    Interval delay; delay.parse(this->ui->voxDelay->text().simplified());\n    this->_config->settings()->audio()->setVOXDelay(delay);\n  });\n  connect(ui->maxSpeakerVolume, &QSpinBox::valueChanged, [this](int value) {\n    this->_config->settings()->audio()->setMaxSpeakerVolume(Level::fromValue(value));\n  });\n  connect(ui->maxHeadphoneVolume, &QSpinBox::valueChanged, [this](int value) {\n    this->_config->settings()->audio()->setMaxHeadphoneVolume(Level::fromValue(value));\n  });\n  connect(ui->speech, &QCheckBox::toggled, [this](bool checked) {\n    this->_config->settings()->audio()->enableSpeechSynthesis(checked);\n  });\n\n  connect(ui->disableAllTones, &QCheckBox::toggled, [this](bool checked) {\n    this->_config->settings()->tone()->enableSilent(checked);\n  });\n  connect(ui->keyToneVolume, &QSpinBox::valueChanged, [this](int value) {\n    this->_config->settings()->tone()->setKeyToneVolume(Level::fromValue(value));\n  });\n  connect(ui->bootMelodyEnable, &QCheckBox::toggled, [this](bool checked) {\n    this->_config->settings()->tone()->enableBootTone(checked);\n  });\n  connect(ui->talkPermitSelect, &ChannelTypeEdit::typesChanged, [this](Channel::Types types) {\n    this->_config->settings()->tone()->setTalkPermit(types);\n  });\n  connect(ui->callStartSelect, &ChannelTypeEdit::typesChanged, [this](Channel::Types types) {\n    this->_config->settings()->tone()->setCallStart(types);\n  });\n  connect(ui->callEndSelect, &ChannelTypeEdit::typesChanged, [this](Channel::Types types) {\n    this->_config->settings()->tone()->setCallEnd(types);\n  });\n  connect(ui->channelIdleSelect, &ChannelTypeEdit::typesChanged, [this](Channel::Types types) {\n    this->_config->settings()->tone()->setChannelIdle(types);\n  });\n  connect(ui->callResetEnable, &QCheckBox::toggled, [this](bool checked) {\n    this->_config->settings()->tone()->enableCallReset(checked);\n  });\n\n\n\n  connect(ui->powerValue, &QComboBox::currentIndexChanged, [this](int index) {\n    this->_config->settings()->setPower(this->ui->powerValue->itemData(index).value<Channel::Power>());\n  });\n  connect(ui->totValue, &QSpinBox::valueChanged, [this](int value) {\n    if (0 == value)\n      this->_config->settings()->disableTOT();\n    else\n      this->_config->settings()->setTOT(Interval::fromSeconds(value));\n  });\n}\n\nGeneralSettingsView::~GeneralSettingsView() {\n  delete ui;\n}\n\nvoid\nGeneralSettingsView::onConfigModified() {\n  // boot settings\n  ui->introLine1->setText(_config->settings()->boot()->message1());\n  ui->introLine2->setText(_config->settings()->boot()->message2());\n\n  // audio settings\n  ui->squelch->setValue(_config->settings()->audio()->squelch().value());\n  if (_config->settings()->audio()->dmrSquelchEnabled()) {\n    ui->dmrSquelch->setValue(_config->settings()->audio()->dmrSquelch().value());\n    ui->dmrSquelchDefault->setChecked(false);\n    ui->dmrSquelch->setEnabled(true);\n  } else {\n    ui->dmrSquelch->setValue(3);\n    ui->dmrSquelchDefault->setChecked(true);\n    ui->dmrSquelch->setEnabled(false);\n  }\n  ui->mic->setValue(_config->settings()->audio()->micGain().value());\n  ui->fmMicGain->setValue(_config->settings()->audio()->fmMicGain().value());\n  ui->voxValue->setValue(_config->settings()->audio()->vox().value());\n  ui->voxDelay->setText(_config->settings()->audio()->voxDelay().format());\n  ui->speech->setChecked(_config->settings()->audio()->speechSynthesisEnabled());\n\n  // Tone settings:\n  ui->disableAllTones->setChecked(_config->settings()->tone()->silent());\n  ui->keyToneVolume->setValue(_config->settings()->tone()->keyToneVolume().value());\n  ui->bootMelodyEnable->setChecked(_config->settings()->tone()->bootToneEnabled());\n  ui->talkPermitSelect->setTypes(_config->settings()->tone()->talkPermit());\n  ui->callStartSelect->setTypes(_config->settings()->tone()->callStart());\n  ui->callEndSelect->setTypes(_config->settings()->tone()->callEnd());\n  ui->channelIdleSelect->setTypes(_config->settings()->tone()->channelIdle());\n  ui->callResetEnable->setChecked(_config->settings()->tone()->callResetEnabled());\n\n  // channel default settings\n  switch(_config->settings()->power()) {\n  case Channel::Power::Max: ui->powerValue->setCurrentIndex(0); break;\n  case Channel::Power::High: ui->powerValue->setCurrentIndex(1); break;\n  case Channel::Power::Mid: ui->powerValue->setCurrentIndex(2); break;\n  case Channel::Power::Low: ui->powerValue->setCurrentIndex(3); break;\n  case Channel::Power::Min: ui->powerValue->setCurrentIndex(4); break;\n  }\n  if (_config->settings()->totDisabled())\n    ui->totValue->setValue(0);\n  else\n    ui->totValue->setValue(_config->settings()->tot().seconds());\n}\n"
  },
  {
    "path": "src/generalsettingsview.hh",
    "content": "#ifndef GENERALSETTINGSVIEW_HH\n#define GENERALSETTINGSVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nnamespace Ui {\n  class GeneralSettingsView;\n}\n\n\nclass GeneralSettingsView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit GeneralSettingsView(Config *config, QWidget *parent = nullptr);\n  ~GeneralSettingsView();\n\nprotected slots:\n  void onConfigModified();\n\nprivate:\n  Ui::GeneralSettingsView *ui;\n  Config *_config;\n};\n\n#endif // GENERALSETTINGSVIEW_HH\n"
  },
  {
    "path": "src/generalsettingsview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GeneralSettingsView</class>\n <widget class=\"QWidget\" name=\"GeneralSettingsView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>644</width>\n    <height>557</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n   <item>\n    <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n     <item>\n      <widget class=\"QTabWidget\" name=\"tabs\">\n       <property name=\"currentIndex\">\n        <number>0</number>\n       </property>\n       <widget class=\"QWidget\" name=\"bootSettings\">\n        <attribute name=\"title\">\n         <string>Boot Settings</string>\n        </attribute>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\" stretch=\"1,1\">\n         <item>\n          <layout class=\"QFormLayout\" name=\"bootLeftForm\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"introLine1Label\">\n             <property name=\"text\">\n              <string>Intro Line 1</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"introLine1\">\n             <property name=\"toolTip\">\n              <string>First greeting line (if supported by the radio).</string>\n             </property>\n             <property name=\"accessibleName\">\n              <string>Intro line 1</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"introLine2Label\">\n             <property name=\"text\">\n              <string>Intro Line 2</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"introLine2\">\n             <property name=\"toolTip\">\n              <string>Second greeting line (if supported by the radio).</string>\n             </property>\n             <property name=\"statusTip\">\n              <string/>\n             </property>\n             <property name=\"accessibleName\">\n              <string>Intro line 2</string>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QFormLayout\" name=\"bootRightForm\"/>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"audioSettings\">\n        <attribute name=\"title\">\n         <string>Audio Settings</string>\n        </attribute>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\" stretch=\"1,1\">\n         <item>\n          <layout class=\"QFormLayout\" name=\"audioLeftForm\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"micLabel\">\n             <property name=\"text\">\n              <string>Default Microphone Amplification</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"mic\">\n             <property name=\"toolTip\">\n              <string>Specifies the default (DMR) microphone amplifiction level.</string>\n             </property>\n             <property name=\"minimum\">\n              <number>1</number>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label\">\n             <property name=\"text\">\n              <string>FM Microphone Amplifiction</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"fmMicGain\">\n             <property name=\"specialValueText\">\n              <string extracomment=\"Special value indicating, that the global default mic gain should be used.\">default</string>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_6\">\n             <property name=\"text\">\n              <string>Default Squelch</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"squelch\">\n             <property name=\"specialValueText\">\n              <string extracomment=\"Special value for the squelch setting, disabling/opening the squelch.\">open</string>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n             <property name=\"value\">\n              <number>3</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_7\">\n             <property name=\"text\">\n              <string>DMR Squelch</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n             <item>\n              <widget class=\"QSpinBox\" name=\"dmrSquelch\">\n               <property name=\"sizePolicy\">\n                <sizepolicy hsizetype=\"Minimum\" vsizetype=\"Fixed\">\n                 <horstretch>0</horstretch>\n                 <verstretch>0</verstretch>\n                </sizepolicy>\n               </property>\n               <property name=\"specialValueText\">\n                <string extracomment=\"Special value for the squelch setting, disabling/opening the squelch.\">open</string>\n               </property>\n               <property name=\"maximum\">\n                <number>10</number>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"QCheckBox\" name=\"dmrSquelchDefault\">\n               <property name=\"toolTip\">\n                <string>Disables a separate squelch level for DMR. Uses the default one.</string>\n               </property>\n               <property name=\"text\">\n                <string extracomment=\"Label for a checkbox that disables a separate DMR squelch setting. The default squelch is then used.\">Default</string>\n               </property>\n              </widget>\n             </item>\n            </layout>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"voxLevelLabel\">\n             <property name=\"text\">\n              <string>VOX Sensitivity</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"voxValue\">\n             <property name=\"toolTip\">\n              <string>Specifies the VOX sensitivity.</string>\n             </property>\n             <property name=\"specialValueText\">\n              <string>Off</string>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_3\">\n             <property name=\"text\">\n              <string>VOX Delay</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"1\">\n            <widget class=\"QLineEdit\" name=\"voxDelay\">\n             <property name=\"toolTip\">\n              <string>Specifies the delay between voice detection and transmission. E.g. 500 ms.</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"6\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_4\">\n             <property name=\"text\">\n              <string>Maximum Speaker Volume</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"6\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"maxSpeakerVolume\">\n             <property name=\"toolTip\">\n              <string>Limits the maximum speaker volume.</string>\n             </property>\n             <property name=\"minimum\">\n              <number>1</number>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n             <property name=\"value\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"7\" column=\"0\">\n            <widget class=\"QLabel\" name=\"label_5\">\n             <property name=\"text\">\n              <string>Maximum Headphone Volume</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"7\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"maxHeadphoneVolume\">\n             <property name=\"toolTip\">\n              <string>Limits the maximum headphone volume.</string>\n             </property>\n             <property name=\"minimum\">\n              <number>1</number>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n             <property name=\"value\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"8\" column=\"0\">\n            <widget class=\"QLabel\" name=\"speechLabel\">\n             <property name=\"text\">\n              <string>Speech Synthesis</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"8\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"speech\">\n             <property name=\"text\">\n              <string/>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QFormLayout\" name=\"audioRightForm\"/>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"toneTab\">\n        <attribute name=\"title\">\n         <string>Tone Settings</string>\n        </attribute>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\" stretch=\"0,0\">\n         <item>\n          <layout class=\"QFormLayout\" name=\"toneLeftForm\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"disableAllTonesLabel\">\n             <property name=\"text\">\n              <string extracomment=\"Label for a checkbox disabling all tones.\">Disable all</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"disableAllTones\">\n             <property name=\"toolTip\">\n              <string>Disables all tones.</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"keyToneVolumeLabel\">\n             <property name=\"text\">\n              <string>Key tone volume</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"keyToneVolume\">\n             <property name=\"specialValueText\">\n              <string extracomment=\"Special value of a spin box indicating that keytones are disabled.\">off</string>\n             </property>\n             <property name=\"maximum\">\n              <number>10</number>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"smsToneLabel\">\n             <property name=\"text\">\n              <string>SMS tone</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"smsTone\">\n             <property name=\"toolTip\">\n              <string>Enables a tone on SMS reception.</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"ringtoneLabel\">\n             <property name=\"text\">\n              <string>Ringtone</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <widget class=\"QCheckBox\" name=\"ringtone\">\n             <property name=\"toolTip\">\n              <string>Enables the ringtone on incoming private calls.</string>\n             </property>\n             <property name=\"text\">\n              <string notr=\"true\"/>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"0\">\n            <widget class=\"QLabel\" name=\"talkPermitLabel\">\n             <property name=\"text\">\n              <string>Talk permit</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>\n             </property>\n            </widget>\n           </item>\n           <item row=\"4\" column=\"1\">\n            <widget class=\"ChannelTypeEdit\" name=\"talkPermitSelect\" native=\"true\"/>\n           </item>\n           <item row=\"5\" column=\"0\">\n            <widget class=\"QLabel\" name=\"bootMelodyLabel\">\n             <property name=\"text\">\n              <string>Boot melody</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>\n             </property>\n             <property name=\"buddy\">\n              <cstring>bootMelodyEnable</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"5\" column=\"1\">\n            <layout class=\"QVBoxLayout\" name=\"bootMelodyLayout\">\n             <item>\n              <widget class=\"QCheckBox\" name=\"bootMelodyEnable\">\n               <property name=\"text\">\n                <string extracomment=\"Label for a checkbox, enabling the boot melody\">Enabled</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"MelodyEdit\" name=\"bootMelodyEdit\" native=\"true\"/>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QFormLayout\" name=\"toneRightForm\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"callStartLabel\">\n             <property name=\"text\">\n              <string>Call Start</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>\n             </property>\n             <property name=\"buddy\">\n              <cstring>callStartSelect</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"callEndLabel\">\n             <property name=\"text\">\n              <string>Call End</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>\n             </property>\n             <property name=\"buddy\">\n              <cstring>callEndSelect</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"2\" column=\"0\">\n            <widget class=\"QLabel\" name=\"channelIdleLabel\">\n             <property name=\"text\">\n              <string>Channel Idle</string>\n             </property>\n             <property name=\"buddy\">\n              <cstring>channelIdleSelect</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"3\" column=\"0\">\n            <widget class=\"QLabel\" name=\"callResetLabel\">\n             <property name=\"text\">\n              <string>Call Reset</string>\n             </property>\n             <property name=\"alignment\">\n              <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignTop</set>\n             </property>\n             <property name=\"buddy\">\n              <cstring>callResetEnable</cstring>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <layout class=\"QVBoxLayout\" name=\"callStartLayout\">\n             <property name=\"spacing\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"ChannelTypeEdit\" name=\"callStartSelect\" native=\"true\"/>\n             </item>\n             <item>\n              <widget class=\"MelodyEdit\" name=\"callStartEdit\" native=\"true\"/>\n             </item>\n            </layout>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <layout class=\"QVBoxLayout\" name=\"callEndLayout\">\n             <property name=\"spacing\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"ChannelTypeEdit\" name=\"callEndSelect\" native=\"true\"/>\n             </item>\n             <item>\n              <widget class=\"MelodyEdit\" name=\"callEndEdit\" native=\"true\"/>\n             </item>\n            </layout>\n           </item>\n           <item row=\"2\" column=\"1\">\n            <layout class=\"QVBoxLayout\" name=\"channelIdleLayout\">\n             <property name=\"spacing\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"ChannelTypeEdit\" name=\"channelIdleSelect\" native=\"true\"/>\n             </item>\n             <item>\n              <widget class=\"MelodyEdit\" name=\"channelIdleEdit\" native=\"true\"/>\n             </item>\n            </layout>\n           </item>\n           <item row=\"3\" column=\"1\">\n            <layout class=\"QVBoxLayout\" name=\"callResetLayout\">\n             <property name=\"spacing\">\n              <number>0</number>\n             </property>\n             <item>\n              <widget class=\"QCheckBox\" name=\"callResetEnable\">\n               <property name=\"text\">\n                <string extracomment=\"Label for a checkbox enabling the call-reset tone.\">Enabled</string>\n               </property>\n              </widget>\n             </item>\n             <item>\n              <widget class=\"MelodyEdit\" name=\"callResetEdit\" native=\"true\"/>\n             </item>\n            </layout>\n           </item>\n          </layout>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"defaultValues\">\n        <attribute name=\"title\">\n         <string>Default Values</string>\n        </attribute>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_6\" stretch=\"1,1\">\n         <item>\n          <layout class=\"QFormLayout\" name=\"defaultLeftForm\">\n           <item row=\"0\" column=\"0\">\n            <widget class=\"QLabel\" name=\"powerLabel\">\n             <property name=\"text\">\n              <string>Power</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"0\" column=\"1\">\n            <widget class=\"QComboBox\" name=\"powerValue\">\n             <item>\n              <property name=\"text\">\n               <string>Max</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>High</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Mid</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Low</string>\n              </property>\n             </item>\n             <item>\n              <property name=\"text\">\n               <string>Min</string>\n              </property>\n             </item>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"0\">\n            <widget class=\"QLabel\" name=\"transmitTimeoutLabel\">\n             <property name=\"text\">\n              <string>Transmit Timeout</string>\n             </property>\n            </widget>\n           </item>\n           <item row=\"1\" column=\"1\">\n            <widget class=\"QSpinBox\" name=\"totValue\">\n             <property name=\"specialValueText\">\n              <string>Off</string>\n             </property>\n             <property name=\"suffix\">\n              <string notr=\"true\"> s</string>\n             </property>\n             <property name=\"maximum\">\n              <number>9999</number>\n             </property>\n            </widget>\n           </item>\n          </layout>\n         </item>\n         <item>\n          <layout class=\"QFormLayout\" name=\"defaultRightForm\"/>\n         </item>\n        </layout>\n       </widget>\n       <widget class=\"QWidget\" name=\"extensionTab\">\n        <attribute name=\"title\">\n         <string>Extensions</string>\n        </attribute>\n        <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n         <item>\n          <widget class=\"ExtensionView\" name=\"extensionView\" native=\"true\"/>\n         </item>\n        </layout>\n       </widget>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>MelodyEdit</class>\n   <extends>QWidget</extends>\n   <header>melody_edit.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ChannelTypeEdit</class>\n   <extends>QWidget</extends>\n   <header>channel_type_edit.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/gpssystemdialog.cc",
    "content": "#include \"gpssystemdialog.hh\"\n#include \"settings.hh\"\n\n\nGPSSystemDialog::GPSSystemDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myGPSSystem(new DMRAPRSSystem(this)), _gpsSystem(nullptr)\n{\n  setWindowTitle(tr(\"Create DMR APRS System\"));\n  construct();\n}\n\nGPSSystemDialog::GPSSystemDialog(Config *config, DMRAPRSSystem *gps, QWidget *parent)\n  : QDialog(parent), _config(config), _myGPSSystem(new DMRAPRSSystem(this)), _gpsSystem(gps)\n{\n  setWindowTitle(tr(\"Edit DMR APRS System\"));\n  if (_gpsSystem)\n    _myGPSSystem->copy(*_gpsSystem);\n  construct();\n}\n\nvoid\nGPSSystemDialog::construct() {\n  setupUi(this);\n  Settings settings;\n\n  // setup name entry\n  name->setText(_myGPSSystem->name());\n\n  // setup contact entry\n  for (int i=0; i<_config->contacts()->count(); i++) {\n    if (! _config->contacts()->get(i)->is<DMRContact>())\n      continue;\n    auto cont = _config->contacts()->get(i)->as<DMRContact>();\n    destination->addItem(cont->name(), QVariant::fromValue(cont));\n    if (_myGPSSystem->contact() == cont)\n      destination->setCurrentIndex(i);\n  }\n\n  // setup period\n  if (_myGPSSystem->periodDisabled())\n    period->setValue(0);\n  else\n    period->setValue(_myGPSSystem->period().seconds());\n\n  // setup revert channel\n  revChannel->addItem(tr(\"[Selected]\"), QVariant::fromValue<DMRChannel *>(nullptr));\n  if (! _myGPSSystem->hasRevertChannel())\n    revChannel->setCurrentIndex(0);\n  for (int i=1,j=0; j<_config->channelList()->count(); j++) {\n    if (! _config->channelList()->channel(j)->is<DMRChannel>())\n      continue;\n    revChannel->addItem(_config->channelList()->channel(j)->name(),\n                        QVariant::fromValue(_config->channelList()->channel(j)->as<DMRChannel>()));\n    if (_myGPSSystem->hasRevertChannel() &&\n        (_myGPSSystem->revertChannel() == _config->channelList()->channel(j)->as<DMRChannel>()))\n      revChannel->setCurrentIndex(i);\n    i++;\n  }\n\n  extensionView->setObjectName(\"dmrAPRSSystemExtension\");\n  extensionView->setObject(_myGPSSystem, _config);\n}\n\nDMRAPRSSystem *\nGPSSystemDialog::gpsSystem() {\n  _myGPSSystem->setName(name->text().simplified());\n  _myGPSSystem->setContact(destination->currentData().value<DMRContact*>());\n  if (0 == period->value())\n    _myGPSSystem->disablePeriod();\n  else\n    _myGPSSystem->setPeriod(Interval::fromSeconds(period->value()));\n  if (revChannel->currentData().isNull())\n    _myGPSSystem->resetRevertChannel();\n  else\n    _myGPSSystem->setRevertChannel(revChannel->currentData().value<DMRChannel*>());\n\n  DMRAPRSSystem *sys = _myGPSSystem;\n  if (_gpsSystem) {\n    _gpsSystem->copy(*_myGPSSystem);\n    sys = _gpsSystem;\n  } else {\n    _myGPSSystem->setParent(nullptr);\n  }\n\n  return sys;\n}\n"
  },
  {
    "path": "src/gpssystemdialog.hh",
    "content": "#ifndef GPSSYSTEMDIALOG_HH\n#define GPSSYSTEMDIALOG_HH\n\n#include \"config.hh\"\n\n#include <QDialog>\n#include \"ui_gpssystemdialog.h\"\n\nclass GPSSystemDialog : public QDialog, private Ui::GPSSystemDialog\n{\n  Q_OBJECT\n\npublic:\n  GPSSystemDialog(Config *config, QWidget *parent=nullptr);\n  GPSSystemDialog(Config *config, DMRAPRSSystem *gps, QWidget *parent=nullptr);\n\n  DMRAPRSSystem *gpsSystem();\n\nprotected:\n  void construct();\n\nprotected:\n  Config *_config;\n  DMRAPRSSystem *_myGPSSystem;\n  DMRAPRSSystem *_gpsSystem;\n};\n\n#endif // GPSSYSTEMDIALOG_HH\n"
  },
  {
    "path": "src/gpssystemdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GPSSystemDialog</class>\n <widget class=\"QDialog\" name=\"GPSSystemDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit GPS System</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"label\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"name\"/>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"destination\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"label_2\">\n         <property name=\"text\">\n          <string>Destination</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"label_3\">\n         <property name=\"text\">\n          <string>Update period</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QSpinBox\" name=\"period\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"suffix\">\n          <string notr=\"true\">s</string>\n         </property>\n         <property name=\"minimum\">\n          <number>60</number>\n         </property>\n         <property name=\"maximum\">\n          <number>2700</number>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <widget class=\"QLabel\" name=\"label_4\">\n         <property name=\"text\">\n          <string>Revert Channel</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"1\">\n        <widget class=\"QComboBox\" name=\"revChannel\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>GPSSystemDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>GPSSystemDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/grouplistsview.cc",
    "content": "#include \"grouplistsview.hh\"\n#include \"ui_grouplistsview.h\"\n#include \"rxgrouplistdialog.hh\"\n#include \"config.hh\"\n\n#include <QHeaderView>\n#include <QMessageBox>\n\n\nGroupListsView::GroupListsView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::GroupListsView), _config(config)\n{\n  ui->setupUi(this);\n\n  ui->listView->setModel(new GroupListsWrapper(_config->rxGroupLists(), ui->listView));\n\n  connect(ui->addRXGroup, SIGNAL(clicked()), this, SLOT(onAddRxGroup()));\n  connect(ui->remRXGroup, SIGNAL(clicked()), this, SLOT(onRemRxGroup()));\n  connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditRxGroup(unsigned)));\n\n}\n\nGroupListsView::~GroupListsView() {\n  delete ui;\n}\n\n\nvoid\nGroupListsView::onAddRxGroup() {\n  RXGroupListDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->listView->hasSelection())\n    row = ui->listView->selection().second+1;\n  _config->rxGroupLists()->add(dialog.groupList(), row);\n}\n\nvoid\nGroupListsView::onRemRxGroup() {\n  // Check if there is any groups selected\n  if (! ui->listView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete RX group list\"),\n          tr(\"Cannot delete RX group lists: You have to select a group list first.\"));\n    return;\n  }\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->listView->selection();\n  int rowcount = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->rxGroupLists()->list(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete RX group list?\"), tr(\"Delete RX group list %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete RX group list?\"), tr(\"Delete %1 RX group lists?\").arg(rowcount)))\n      return;\n  }\n  // collect all selected group lists\n  // need to collect them first as rows change when deleting\n  QList<RXGroupList *> lists; lists.reserve(rowcount);\n  for (int row=rows.first; row<=rows.second; row++)\n    lists.push_back(_config->rxGroupLists()->list(row));\n  // remove list\n  foreach (RXGroupList *list, lists)\n    _config->rxGroupLists()->del(list);\n}\n\nvoid\nGroupListsView::onEditRxGroup(unsigned row) {\n  RXGroupListDialog dialog(_config, _config->rxGroupLists()->list(row));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  dialog.groupList();\n}\n\n"
  },
  {
    "path": "src/grouplistsview.hh",
    "content": "#ifndef GROUPLISTSVIEW_HH\n#define GROUPLISTSVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nnamespace Ui {\n  class GroupListsView;\n}\n\n\nclass GroupListsView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit GroupListsView(Config *config, QWidget *parent = nullptr);\n  ~GroupListsView();\n\nprotected slots:\n  void onAddRxGroup();\n  void onRemRxGroup();\n  void onEditRxGroup(unsigned row);\n\nprivate:\n  Ui::GroupListsView *ui;\n  Config *_config;\n};\n\n#endif // GROUPLISTSVIEW_HH\n"
  },
  {
    "path": "src/grouplistsview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>GroupListsView</class>\n <widget class=\"QWidget\" name=\"GroupListsView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"ConfigObjectListView\" name=\"listView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addRXGroup\">\n       <property name=\"text\">\n        <string>Add RX Group</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt++</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remRXGroup\">\n       <property name=\"text\">\n        <string>Delete RX Group</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/hearhamrepeatersource.cc",
    "content": "#include \"hearhamrepeatersource.hh\"\n\n#include \"logger.hh\"\n\n#include <QJsonParseError>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QJsonValue>\n#include <QJsonArray>\n\n\nHearhamRepeaterSource::HearhamRepeaterSource(QObject *parent)\n  : DownloadableRepeaterDatabaseSource(\n      \"hearham.cache.json\",\n      QUrl(\"https://hearham.com/api/repeaters/v1\"), 5, parent)\n{\n  // pass...\n}\n\n\nbool\nHearhamRepeaterSource::parse(const QByteArray &json) {\n  QJsonParseError err;\n  auto doc = QJsonDocument::fromJson(json, &err);\n  if (doc.isNull()) {\n    logError() << \"Cannot received JSON: \" << err.errorString() << \".\";\n    return false;\n  }\n  if (! doc.isArray()) {\n    logError() << \"Malformed result.\";\n    return false;\n  }\n\n  QRegularExpression ccPattern(R\"(CC\\w*([0-9]{1,2}))\");\n  QRegularExpression dcsPattern(R\"(DCS\\w*([0-9]{3})\\w*(N|I|))\");\n\n  for (const QJsonValue &val: doc.array()) {\n    if (! val.isObject())\n      continue;\n    auto obj = val.toObject();\n\n    auto call = obj[\"callsign\"].toString().simplified().toUpper();\n    auto mode = obj[\"mode\"].toString();\n    auto rx = Frequency::fromHz(obj[\"frequency\"].toInt()),\n        tx = Frequency::fromHz(rx.inHz() + obj[\"offset\"].toInt());\n    auto location = QGeoCoordinate(obj[\"latitude\"].toDouble(), obj[\"longitude\"].toDouble());\n    auto qth = obj[\"city\"].toString();\n\n    if (\"FM\" == mode) {\n      SelectiveCall rxTone, txTone;\n      auto rxDCS = dcsPattern.match(obj[\"decode\"].toString());\n      auto txDCS = dcsPattern.match(obj[\"encode\"].toString());\n\n      if (rxDCS.isValid())\n        rxTone = SelectiveCall(rxDCS.captured(1).toUInt(), \"I\" == rxDCS.captured(2));\n      else if (0 != obj[\"decode\"].toString().toDouble())\n        rxTone = SelectiveCall(obj[\"decode\"].toString().toDouble());\n\n      if (txDCS.isValid())\n        txTone = SelectiveCall(txDCS.captured(1).toUInt(), \"I\" == txDCS.captured(2));\n      else if (0 != obj[\"encode\"].toString().toDouble())\n        txTone = SelectiveCall(obj[\"encode\"].toString().toDouble());\n\n      cache(RepeaterDatabaseEntry::fm(call, rx, tx, location, qth, rxTone, txTone));\n    } else if (\"DMR\" == mode) {\n      unsigned int colorCode = 0;\n      auto CC = ccPattern.match(obj[\"encode\"].toString());\n      if (CC.isValid())\n        colorCode = CC.captured(1).toUInt();\n\n      cache(RepeaterDatabaseEntry::dmr(call, rx, tx, location, qth, colorCode));\n    }\n  }\n\n  logDebug() << \"Loaded \" << _cache.size() << \" elements from \" << _url.toDisplayString() << \".\";\n\n  return true;\n}\n"
  },
  {
    "path": "src/hearhamrepeatersource.hh",
    "content": "#ifndef HEARHAMREPEATERSOURCE_HH\n#define HEARHAMREPEATERSOURCE_HH\n\n#include \"repeaterdatabase.hh\"\n\n\nclass HearhamRepeaterSource : public DownloadableRepeaterDatabaseSource\n{\nQ_OBJECT\n\npublic:\n  explicit HearhamRepeaterSource(QObject *parent = nullptr);\n\nprotected:\n  bool parse(const QByteArray &doc);\n};\n\n#endif // HEARHAMREPEATERSOURCE_HH\n"
  },
  {
    "path": "src/idselect.cc",
    "content": "#include \"idselect.hh\"\n#include \"config.hh\"\n#include \"radioid.hh\"\n\n\nDMRIdSelect::DMRIdSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[Default]\"), QVariant::fromValue(DefaultRadioID::get()));\n  for (int i=0; i<config->radioIDs()->count(); i++) {\n    if (config->radioIDs()->get(i)->is<DMRRadioID>()) {\n      DMRRadioID *id = config->radioIDs()->get(i)->as<DMRRadioID>();\n      addItem(id->name(), QVariant::fromValue(id));\n    }\n  }\n}\n\n\nvoid\nDMRIdSelect::setRadioId(DMRRadioID *id) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<DMRRadioID*>() == id) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nDMRRadioID *\nDMRIdSelect::radioId() const {\n  return currentData().value<DMRRadioID*>();\n}\n"
  },
  {
    "path": "src/idselect.hh",
    "content": "#ifndef IDSELECT_HH\n#define IDSELECT_HH\n\n#include \"config.hh\"\n#include <QComboBox>\n\nclass Config;\nclass DMRRadioID;\n\nclass DMRIdSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  DMRIdSelect(Config *config, QWidget *parent=nullptr);\n\n  void setRadioId(DMRRadioID *id);\n  DMRRadioID *radioId() const;\n\n};\n\n#endif // IDSELECT_HH\n"
  },
  {
    "path": "src/m17channeldialog.cc",
    "content": "#include \"m17channeldialog.hh\"\n#include \"rxgrouplistdialog.hh\"\n#include \"channel.hh\"\n#include \"config.hh\"\n#include \"m17contactdialog.hh\"\n#include \"ui_channeldialog.h\"\n\n\n/* ****************************************************************************************** *\n * N17 channel mode selection\n * ****************************************************************************************** */\nM17ChannelModeSelect::M17ChannelModeSelect(QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"Voice\"), QVariant::fromValue(M17Channel::Mode::Voice));\n  addItem(tr(\"Data\"), QVariant::fromValue(M17Channel::Mode::Data));\n  addItem(tr(\"Voice + Data\"), QVariant::fromValue(M17Channel::Mode::VoiceAndData));\n}\n\n\nvoid\nM17ChannelModeSelect::setMode(M17Channel::Mode mode) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<M17Channel::Mode>() == mode) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\n\nM17Channel::Mode\nM17ChannelModeSelect::mode() const {\n  return currentData().value<M17Channel::Mode>();\n}\n\n\n\n\n/* ****************************************************************************************** *\n * N17 channel dialog\n * ****************************************************************************************** */\nM17ChannelDialog::M17ChannelDialog(Config *config, QWidget *parent)\n  : ChannelDialog(config, parent), _channel(), _mode(nullptr), _access(nullptr),\n  _contact(nullptr)\n{\n  ui->rightForm->addRow(tr(\"Channel mode\"),\n                        _mode = new M17ChannelModeSelect());\n  ui->rightForm->addRow(tr(\"Access number\"),\n                        _access = new QSpinBox());\n  _access->setRange(0,15);\n  ui->rightForm->addRow(tr(\"Tx contact\"),\n                        _contact = new M17ContactSelect(config));\n  ui->rightForm->addRow(tr(\"Send position\"),\n                        _aprs = new QCheckBox(\"\"));\n}\n\n\nvoid\nM17ChannelDialog::setChannel(M17Channel *ch) {\n  ChannelDialog::setChannel(ch);\n  _channel = ch;\n\n  _mode->setMode(_channel->mode());\n  _access->setValue(_channel->accessNumber());\n  _contact->setContact(_channel->contact());\n  _aprs->setChecked(_channel->aprsEnabled());\n}\n\n\nvoid\nM17ChannelDialog::accept() {\n  _channel->setMode(_mode->mode());\n  _channel->setAccessNumber(_access->value());\n  _channel->setContact(_contact->contact());\n  _channel->enableAPRS(_aprs->isChecked());\n\n  ChannelDialog::accept();\n}\n"
  },
  {
    "path": "src/m17channeldialog.hh",
    "content": "#ifndef M17CHANNELDIALOG_HH\n#define M17CHANNELDIALOG_HH\n\n#include \"channeldialog.hh\"\n#include \"channel.hh\"\n#include <QComboBox>\n#include <QSpinBox>\n#include <QCheckBox>\n\nclass Config;\nclass M17ContactSelect;\n\n\nclass M17ChannelModeSelect: public QComboBox\n{\nQ_OBJECT\n\npublic:\n  explicit M17ChannelModeSelect(QWidget *parent=nullptr);\n\n  void setMode(M17Channel::Mode mode);\n  M17Channel::Mode mode() const;\n};\n\n\nclass M17ChannelDialog : public ChannelDialog\n{\n  Q_OBJECT\n\npublic:\n  M17ChannelDialog(Config *config, QWidget *parent = nullptr);\n\n  void setChannel(M17Channel *ch);\n\npublic slots:\n  void accept() override;\n\nprivate:\n  QPointer<M17Channel> _channel;\n  M17ChannelModeSelect *_mode;\n  QSpinBox *_access;\n  M17ContactSelect *_contact;\n  QCheckBox *_aprs;\n};\n\n#endif // M17CHANNELDIALOG_HH\n"
  },
  {
    "path": "src/m17contactdialog.cc",
    "content": "#include \"m17contactdialog.hh\"\n#include \"ui_m17contactdialog.h\"\n#include \"contact.hh\"\n#include \"settings.hh\"\n#include \"config.hh\"\n#include <QRegularExpressionValidator>\n\n\n/* ******************************************************************************************* *\n * M17 contact dialog\n * ******************************************************************************************* */\nM17ContactDialog::M17ContactDialog(Config *config, QWidget *parent) :\n  QDialog(parent), ui(new Ui::M17ContactDialog), _config(config), _contact(nullptr),\n  _myContact(new M17Contact(this))\n{    \n  construct();\n}\n\nM17ContactDialog::M17ContactDialog(Config *config, M17Contact *contact, QWidget *parent) :\n  QDialog(parent), ui(new Ui::M17ContactDialog), _config(config), _contact(contact),\n  _myContact(new M17Contact(this))\n{\n  if (_contact)\n    _myContact->copy(*_contact);\n\n  construct();\n}\n\nM17ContactDialog::~M17ContactDialog()\n{\n  delete ui;\n}\n\n\nvoid\nM17ContactDialog::construct() {\n  ui->setupUi(this);\n  Settings settings;\n\n  ui->call->setValidator(new QRegularExpressionValidator(QRegularExpression(R\"([A-Z0-9\\./\\-]{0,9})\")));\n\n  if (_contact) {\n    setWindowTitle(tr(\"Edit M17 Contact\"));\n    ui->name->setText(_contact->name());\n    ui->call->setText(_contact->call());\n    ui->broadcast->setChecked(_contact->isBroadcast());\n    ui->call->setEnabled(! _contact->isBroadcast());\n  } else {\n    setWindowTitle(tr(\"Create M17 Contact\"));\n  }\n\n  ui->extensionView->setObjectName(\"m17ContactExtension\");\n  ui->extensionView->setObject(_myContact, _config);\n\n  connect(ui->broadcast, SIGNAL(toggled(bool)), this, SLOT(onBroadcastToggled(bool)));\n  connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nM17Contact *\nM17ContactDialog::contact() const {\n\n  _myContact->setName(ui->name->text().simplified());\n  _myContact->setCall(ui->call->text());\n  _myContact->setBroadcast(ui->broadcast->isChecked());\n  _myContact->setRing(ui->ring->isChecked());\n\n  M17Contact *contact = _myContact;\n  if (_contact) {\n    _contact->copy(*_myContact);\n    contact = _contact;\n  } else {\n    _myContact->setParent(nullptr);\n  }\n\n  return contact;\n\n  return _contact;\n}\n\nvoid\nM17ContactDialog::onBroadcastToggled(bool enable) {\n  ui->call->setEnabled(! enable);\n}\n\n\n\n/* ******************************************************************************************* *\n * M17 contact selection\n * ******************************************************************************************* */\nM17ContactSelect::M17ContactSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[None]\"), QVariant::fromValue<M17Contact *>(nullptr));\n  for (int i=0; i<config->contacts()->count(); i++) {\n    if (! config->contacts()->contact(i)->is<M17Contact>())\n      continue;\n    auto contact = config->contacts()->contact(i)->as<M17Contact>();\n    addItem(contact->name(), QVariant::fromValue(contact));\n  }\n}\n\n\nvoid\nM17ContactSelect::setContact(M17Contact *contact) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<M17Contact*>()==contact) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\n\nM17Contact *\nM17ContactSelect::contact() const {\n  return currentData().value<M17Contact*>();\n}\n\n"
  },
  {
    "path": "src/m17contactdialog.hh",
    "content": "#ifndef M17CONTACTDIALOG_HH\n#define M17CONTACTDIALOG_HH\n\n#include <QDialog>\n#include <QComboBox>\n\nclass M17Contact;\nclass Config;\n\nnamespace Ui {\n  class M17ContactDialog;\n}\n\n\nclass M17ContactDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit M17ContactDialog(Config *config, QWidget *parent = nullptr);\n  M17ContactDialog(Config *config, M17Contact *contact, QWidget *parent = nullptr);\n  ~M17ContactDialog();\n\n  M17Contact *contact() const;\n\nprotected:\n  /** Assembles the GUI, populates elements. */\n  void construct();\n\nprotected slots:\n  void onBroadcastToggled(bool enable);\n\nprivate:\n  Ui::M17ContactDialog *ui;\n  Config *_config;\n  M17Contact *_contact;\n  M17Contact *_myContact;\n};\n\n\n\nclass M17ContactSelect: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  M17ContactSelect(Config *config, QWidget *parent=nullptr);\n\n  void setContact(M17Contact *contact);\n  M17Contact *contact() const;\n};\n\n#endif // M17CONTACTDIALOG_HH\n"
  },
  {
    "path": "src/m17contactdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>M17ContactDialog</class>\n <widget class=\"QDialog\" name=\"M17ContactDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit M17 Contact</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QFormLayout\" name=\"formLayout\">\n       <item row=\"0\" column=\"0\">\n        <widget class=\"QLabel\" name=\"nameLabel\">\n         <property name=\"text\">\n          <string>Name</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"0\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"name\">\n         <property name=\"toolTip\">\n          <string>The name of the contact.</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"0\">\n        <widget class=\"QLabel\" name=\"numberLabel\">\n         <property name=\"text\">\n          <string>Call</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"1\" column=\"1\">\n        <widget class=\"QLineEdit\" name=\"call\">\n         <property name=\"toolTip\">\n          <string>The callsign of the contact. Must be not longer than 9 chars, A-Z, 0-9, ., /, -.</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"0\">\n        <widget class=\"QLabel\" name=\"ringLabel\">\n         <property name=\"text\">\n          <string>Ring</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"3\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"ring\"/>\n       </item>\n       <item row=\"2\" column=\"0\">\n        <widget class=\"QLabel\" name=\"broadcastLabel\">\n         <property name=\"text\">\n          <string>Broadcast</string>\n         </property>\n        </widget>\n       </item>\n       <item row=\"2\" column=\"1\">\n        <widget class=\"QCheckBox\" name=\"broadcast\">\n         <property name=\"toolTip\">\n          <string>Sets this contact to be the M17 broadcast contact, the specified call is then ignored.</string>\n         </property>\n         <property name=\"text\">\n          <string/>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>M17ContactDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>M17ContactDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/main.cc",
    "content": "#include \"application.hh\"\n#include <QFile>\n#include <QTextStream>\n#include \"mainwindow.hh\"\n#include <QMessageBox>\n#include \"logger.hh\"\n#include \"settings.hh\"\n#include <QSplashScreen>\n\n#include <QCommandLineParser>\n\nint main(int argc, char *argv[])\n{\n  QTextStream out(stderr);\n  auto handler = new StreamLogHandler(out, LogMessage::Level::INFO);\n  Logger::get().addHandler(handler);\n\n  Application app(argc, argv);\n\n\n  // Parse options\n  QCommandLineParser parser;\n  parser.addHelpOption();\n  parser.addVersionOption();\n  parser.addPositionalArgument(\"codeplug\", QCoreApplication::translate(\"main\", \"Codeplug file to load.\"));\n  parser.addOption({\"loglevel\", QCoreApplication::translate(\"main\", \"Specifies applications log-level to stdout. Must be one of `debug`, `info`, `warning`, `error` or `fatal`.\"), \"loglevel\", \"info\"});\n  parser.parse(app.arguments());\n\n  // Handle args (if there are some)\n  if (parser.positionalArguments().count()) {\n    ErrorStack err;\n    if (! app.loadCodeplug(parser.positionalArguments().at(0), err))\n      logError() << \"Cannot open codeplug file '\" << argv[1] << \"': \" << err.format();\n  }\n\n  if (parser.isSet(\"loglevel\")) {\n    if (\"debug\" == parser.value(\"loglevel\")) {\n      handler->setMinLevel(LogMessage::Level::DEBUG);\n    } else if (\"info\" == parser.value(\"loglevel\")) {\n      handler->setMinLevel(LogMessage::Level::INFO);\n    } else if (\"warning\" == parser.value(\"loglevel\")) {\n      handler->setMinLevel(LogMessage::Level::WARNING);\n    } else if (\"error\" == parser.value(\"loglevel\")) {\n      handler->setMinLevel(LogMessage::Level::ERROR);\n    } else if (\"fatal\" == parser.value(\"loglevel\")) {\n      handler->setMinLevel(LogMessage::Level::FATAL);\n    }\n  }\n\n  auto mainWindow = app.mainWindow();\n  mainWindow->show();\n\n  Settings settings;\n  if (settings.showDisclaimer()) {\n    app.showAbout();\n    settings.setShowDisclaimer(false);\n  }\n\n  app.exec();\n\n  return 0;\n}\n"
  },
  {
    "path": "src/mainwindow.cc",
    "content": "#include \"mainwindow.hh\"\n#include \"ui_mainwindow.h\"\n#include <QProgressBar>\n#include <QCloseEvent>\n#include <QMessageBox>\n#include <QStyleHints>\n\n#include \"settings.hh\"\n#include \"logger.hh\"\n#include \"application.hh\"\n\n#include \"generalsettingsview.hh\"\n#include \"radioidlistview.hh\"\n#include \"contactlistview.hh\"\n#include \"grouplistsview.hh\"\n#include \"channellistview.hh\"\n#include \"zonelistview.hh\"\n#include \"scanlistsview.hh\"\n#include \"positioningsystemlistview.hh\"\n#include \"roamingchannellistview.hh\"\n#include \"roamingzonelistview.hh\"\n#include \"extensionview.hh\"\n#include \"talkgroupdatabase.hh\"\n#include \"satellitedatabase.hh\"\n\n\n\nMainWindow::MainWindow(Config *config, QWidget *parent)\n  : QMainWindow(parent), ui(new Ui::MainWindow)\n{\n  ui->setupUi(this);\n  Application *app = qobject_cast<Application*>(QApplication::instance());\n\n  Settings settings;\n  logDebug() << \"Create main window using icon theme '\" << QIcon::themeName() << \"'.\";\n\n  QProgressBar *progress = new QProgressBar();\n  progress->setObjectName(\"progress\");\n  ui->statusbar->addPermanentWidget(progress);\n  progress->setVisible(false);\n\n  ui->actionExportToCHIRP->setIcon(QIcon::fromTheme(\"document-export\"));\n  ui->actionImport->setIcon(QIcon::fromTheme(\"document-import\"));\n\n  ui->actionRefreshCallsignDB->setIcon(QIcon::fromTheme(\"document-download\"));\n  ui->actionRefreshTalkgroupDB->setIcon(QIcon::fromTheme(\"document-download\"));\n  ui->actionRefreshOrbitalElements->setIcon(QIcon::fromTheme(\"document-download\"));\n  ui->actionWriteSatellites->setIcon(QIcon::fromTheme(\"device-write-satellites\"));\n  ui->actionEditSatellites->setIcon(QIcon::fromTheme(\"edit-satellites\"));\n\n#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)\n  connect(qApp->styleHints(), &QStyleHints::colorSchemeChanged, this, [=](Qt::ColorScheme scheme) {\n      bool isDarkTheme = scheme == Qt::ColorScheme::Dark ? true : false;\n      QIcon::setThemeName(isDarkTheme ? \"dark\" : \"light\");\n  });\n#endif\n\n  connect(ui->actionNewCodeplug, SIGNAL(triggered()), app, SLOT(newCodeplug()));\n  connect(ui->actionOpenCodeplug, SIGNAL(triggered()), app, SLOT(loadCodeplug()));\n  connect(ui->actionSaveCodeplug, SIGNAL(triggered()), app, SLOT(saveCodeplug()));\n  connect(ui->actionExportToCHIRP, SIGNAL(triggered()), app, SLOT(exportCodeplugToChirp()));\n  connect(ui->actionImport, SIGNAL(triggered()), app, SLOT(importCodeplug()));\n  connect(ui->actionQuit, SIGNAL(triggered()), this, SLOT(close()));\n  connect(ui->actionAbout, SIGNAL(triggered()), app, SLOT(showAbout()));\n  connect(ui->actionSettings, SIGNAL(triggered()), app, SLOT(showSettings()));\n  connect(ui->actionHelp, SIGNAL(triggered()), app, SLOT(showHelp()));\n\n  connect(ui->actionRefreshCallsignDB, &QAction::triggered, app->user(), &UserDatabase::download);\n  QObject::connect(app->user(), &UserDatabase::error, this, [this](const QString &msg) {\n    this->ui->statusbar->showMessage(tr(\"Cannot update callsign DB: %1\").arg(msg), 10000);\n  }, Qt::QueuedConnection);\n  QObject::connect(app->user(), &UserDatabase::loaded, this, [this]() {\n    this->ui->statusbar->showMessage(tr(\"Callsign database updated & loaded.\"), 10000);\n  }, Qt::QueuedConnection);\n\n  connect(ui->actionRefreshTalkgroupDB, &QAction::triggered, app->talkgroup(), &TalkGroupDatabase::download);\n  QObject::connect(app->talkgroup(), &TalkGroupDatabase::error, this, [this](const QString &msg) {\n    this->ui->statusbar->showMessage(tr(\"Cannot update talkgroup DB: %1\").arg(msg), 10000);\n  }, Qt::QueuedConnection);\n  QObject::connect(app->talkgroup(), &TalkGroupDatabase::loaded, this, [this]() {\n    this->ui->statusbar->showMessage(tr(\"Talkgroup database updated & loaded.\"), 10000);\n  }, Qt::QueuedConnection);\n\n  connect(ui->actionRefreshOrbitalElements, &QAction::triggered, app->satellite(), &SatelliteDatabase::update);\n  QObject::connect(app->satellite(), &SatelliteDatabase::error, this, [this](const QString &msg) {\n    this->ui->statusbar->showMessage(tr(\"Cannot update orbital elements: %1\").arg(msg), 10000);\n  }, Qt::QueuedConnection);\n  QObject::connect(app->satellite(), &SatelliteDatabase::loaded, this, [this]() {\n    this->ui->statusbar->showMessage(tr(\"Orbital elements updated & loaded.\"), 10000);\n  }, Qt::QueuedConnection);\n\n  connect(ui->actionUploadCallsignDB, SIGNAL(triggered()), app, SLOT(uploadCallsignDB()));\n  connect(app->user(), &UserDatabase::readyChanged, this, [this](bool ready){\n    this->ui->actionUploadCallsignDB->setEnabled(ready);\n  }, Qt::QueuedConnection);\n  ui->actionUploadCallsignDB->setEnabled(app->user()->ready());\n\n  connect(ui->actionEditSatellites, SIGNAL(triggered()), app, SLOT(editSatellites()));\n  connect(ui->actionDetectDevice, SIGNAL(triggered()), app, SLOT(detectRadio()));\n  connect(ui->actionVerifyCodeplug, SIGNAL(triggered()), app, SLOT(verifyCodeplug()));\n  connect(ui->actionDownload, SIGNAL(triggered()), app, SLOT(downloadCodeplug()));\n  connect(ui->actionUpload, SIGNAL(triggered()), app, SLOT(uploadCodeplug()));\n  connect(ui->actionWriteSatellites, SIGNAL(triggered()), app, SLOT(uploadSatellites()));\n\n  // Wire-up \"General Settings\" view\n  _generalSettings = new GeneralSettingsView(config);\n  ui->tabs->addTab(_generalSettings, tr(\"Settings\"));\n\n  // Wire-up \"Radio IDs\" view\n  _radioIdTab = new RadioIDListView(config);\n  ui->tabs->addTab(_radioIdTab, tr(\"Radio IDs\"));\n  // Wire-up \"Contact List\" view\n  ui->tabs->addTab(new ContactListView(config), tr(\"Contacts\"));\n  // Wire-up \"RX Group List\" view\n  ui->tabs->addTab(new GroupListsView(config), tr(\"Group Lists\"));\n  // Wire-up \"Channel List\" view\n  ui->tabs->addTab(new ChannelListView(config), tr(\"Channels\"));\n  // Wire-up \"Zone List\" view\n  ui->tabs->addTab(new ZoneListView(config), tr(\"Zones\"));\n  // Wire-up \"Scan List\" view\n  ui->tabs->addTab(new ScanListsView(config), tr(\"Scan Lists\"));\n  // Wire-up \"GPS System List\" view\n  ui->tabs->addTab(new PositioningSystemListView(config), tr(\"GPS/APRS\"));\n  // Wire-up \"Roaming Zone List\" view\n  ui->tabs->addTab(new RoamingChannelListView(config), tr(\"Roaming Channels\"));\n  // Wire-up \"Roaming Zone List\" view\n  _roamingZoneList = new RoamingZoneListView(config);\n  ui->tabs->addTab(_roamingZoneList, tr(\"Roaming Zones\"));\n  // Wire-up \"extension view\" for direct editing\n  _extensionView = new ExtensionView();\n  _extensionView->setObject(config, config);\n  ui->tabs->addTab(_extensionView, tr(\"Extensions\"));\n\n  restoreGeometry(settings.mainWindowState());\n\n  connect(config, &ConfigItem::modified, [this, config]() {\n    this->setWindowModified(config->isModified());\n  });\n}\n\n\nvoid\nMainWindow::closeEvent(QCloseEvent *event) {\n  if (qobject_cast<Application*>(Application::instance())->isModified()) {\n    if (QMessageBox::Ok != QMessageBox::question(nullptr, tr(\"Unsaved changes to codeplug.\"),\n                                                 tr(\"There are unsaved changes to the current codeplug. \"\n                                                    \"These changes are lost if you proceed.\"),\n                                                 QMessageBox::Cancel|QMessageBox::Ok))\n      event->ignore();\n    return;\n  }\n\n  Settings().setMainWindowState(saveGeometry());\n  event->accept();\n}\n"
  },
  {
    "path": "src/mainwindow.hh",
    "content": "#ifndef MAINWINDOW_HH\n#define MAINWINDOW_HH\n\n#include <QMainWindow>\n\nclass Config;\nclass GeneralSettingsView;\nclass RadioIDListView;\nclass RoamingZoneListView;\nclass ExtensionView;\nnamespace Ui {\n  class MainWindow;\n}\n\n\nclass MainWindow: public QMainWindow\n{\n  Q_OBJECT\n\npublic:\n  explicit MainWindow(Config *config, QWidget *parent=nullptr);\n\nprotected:\n  void closeEvent(QCloseEvent *event);\n\nprivate:\n  Ui::MainWindow *ui;\n  GeneralSettingsView *_generalSettings;\n  RadioIDListView *_radioIdTab;\n  RoamingZoneListView *_roamingZoneList;\n  ExtensionView *_extensionView;\n};\n\n#endif // MAINWINDOW_HH\n"
  },
  {
    "path": "src/mainwindow.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>MainWindow</class>\n <widget class=\"QMainWindow\" name=\"MainWindow\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>1280</width>\n    <height>800</height>\n   </rect>\n  </property>\n  <property name=\"minimumSize\">\n   <size>\n    <width>800</width>\n    <height>600</height>\n   </size>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">qdmr [*]</string>\n  </property>\n  <property name=\"windowIcon\">\n   <iconset theme=\"qdmr\"/>\n  </property>\n  <property name=\"documentMode\">\n   <bool>true</bool>\n  </property>\n  <property name=\"unifiedTitleAndToolBarOnMac\">\n   <bool>true</bool>\n  </property>\n  <widget class=\"QWidget\" name=\"centralwidget\">\n   <property name=\"sizePolicy\">\n    <sizepolicy hsizetype=\"Expanding\" vsizetype=\"Expanding\">\n     <horstretch>0</horstretch>\n     <verstretch>0</verstretch>\n    </sizepolicy>\n   </property>\n   <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n    <property name=\"leftMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"rightMargin\">\n     <number>0</number>\n    </property>\n    <property name=\"bottomMargin\">\n     <number>0</number>\n    </property>\n    <item>\n     <widget class=\"QTabWidget\" name=\"tabs\">\n      <property name=\"accessibleName\">\n       <string notr=\"true\">Tablist</string>\n      </property>\n      <property name=\"accessibleDescription\">\n       <string notr=\"true\">There are several tabs providing general settings, contact, Rx group, channel, zone and scan lists.</string>\n      </property>\n      <property name=\"currentIndex\">\n       <number>-1</number>\n      </property>\n     </widget>\n    </item>\n   </layout>\n  </widget>\n  <widget class=\"QMenuBar\" name=\"menubar\">\n   <property name=\"geometry\">\n    <rect>\n     <x>0</x>\n     <y>0</y>\n     <width>1280</width>\n     <height>23</height>\n    </rect>\n   </property>\n   <widget class=\"QMenu\" name=\"menuFile\">\n    <property name=\"title\">\n     <string>File</string>\n    </property>\n    <addaction name=\"actionNewCodeplug\"/>\n    <addaction name=\"actionOpenCodeplug\"/>\n    <addaction name=\"actionSaveCodeplug\"/>\n    <addaction name=\"actionExportToCHIRP\"/>\n    <addaction name=\"actionImport\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionQuit\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuDevice\">\n    <property name=\"title\">\n     <string>Device</string>\n    </property>\n    <addaction name=\"actionDetectDevice\"/>\n    <addaction name=\"actionVerifyCodeplug\"/>\n    <addaction name=\"separator\"/>\n    <addaction name=\"actionDownload\"/>\n    <addaction name=\"actionUpload\"/>\n    <addaction name=\"actionUploadCallsignDB\"/>\n    <addaction name=\"actionWriteSatellites\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuHelp\">\n    <property name=\"title\">\n     <string>Help</string>\n    </property>\n    <addaction name=\"actionAbout\"/>\n    <addaction name=\"actionHelp\"/>\n   </widget>\n   <widget class=\"QMenu\" name=\"menuDatabases\">\n    <property name=\"title\">\n     <string>Databases</string>\n    </property>\n    <addaction name=\"actionRefreshCallsignDB\"/>\n    <addaction name=\"actionRefreshTalkgroupDB\"/>\n    <addaction name=\"actionRefreshOrbitalElements\"/>\n    <addaction name=\"actionEditSatellites\"/>\n   </widget>\n   <addaction name=\"menuFile\"/>\n   <addaction name=\"menuDevice\"/>\n   <addaction name=\"menuDatabases\"/>\n   <addaction name=\"menuHelp\"/>\n  </widget>\n  <widget class=\"QStatusBar\" name=\"statusbar\">\n   <property name=\"sizeGripEnabled\">\n    <bool>false</bool>\n   </property>\n  </widget>\n  <widget class=\"QToolBar\" name=\"toolBar\">\n   <property name=\"windowTitle\">\n    <string notr=\"true\">toolBar</string>\n   </property>\n   <property name=\"accessibleName\">\n    <string>Toolbar</string>\n   </property>\n   <attribute name=\"toolBarArea\">\n    <enum>TopToolBarArea</enum>\n   </attribute>\n   <attribute name=\"toolBarBreak\">\n    <bool>false</bool>\n   </attribute>\n   <addaction name=\"actionNewCodeplug\"/>\n   <addaction name=\"actionOpenCodeplug\"/>\n   <addaction name=\"actionSaveCodeplug\"/>\n   <addaction name=\"separator\"/>\n   <addaction name=\"actionDetectDevice\"/>\n   <addaction name=\"actionVerifyCodeplug\"/>\n   <addaction name=\"actionDownload\"/>\n   <addaction name=\"actionUpload\"/>\n   <addaction name=\"actionUploadCallsignDB\"/>\n   <addaction name=\"actionWriteSatellites\"/>\n   <addaction name=\"separator\"/>\n   <addaction name=\"actionSettings\"/>\n   <addaction name=\"actionAbout\"/>\n   <addaction name=\"actionHelp\"/>\n  </widget>\n  <action name=\"actionNewCodeplug\">\n   <property name=\"icon\">\n    <iconset theme=\"document-new\"/>\n   </property>\n   <property name=\"text\">\n    <string>New</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Creates a new Codeplug.</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+N</string>\n   </property>\n  </action>\n  <action name=\"actionOpenCodeplug\">\n   <property name=\"icon\">\n    <iconset theme=\"document-open\"/>\n   </property>\n   <property name=\"text\">\n    <string>Open ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Imports a codeplug from &amp;quot;conf&amp;quot; files.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+O</string>\n   </property>\n  </action>\n  <action name=\"actionSaveCodeplug\">\n   <property name=\"icon\">\n    <iconset theme=\"document-save-as\"/>\n   </property>\n   <property name=\"text\">\n    <string>Save ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Saves the codeplug in a &amp;quot;conf&amp;quot; file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+S</string>\n   </property>\n  </action>\n  <action name=\"actionQuit\">\n   <property name=\"icon\">\n    <iconset theme=\"application-exit\"/>\n   </property>\n   <property name=\"text\">\n    <string>Quit</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Quits the application.</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+Q</string>\n   </property>\n  </action>\n  <action name=\"actionDetectDevice\">\n   <property name=\"icon\">\n    <iconset theme=\"device-search\"/>\n   </property>\n   <property name=\"text\">\n    <string>Detect</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Detect connected radios.</string>\n   </property>\n  </action>\n  <action name=\"actionVerifyCodeplug\">\n   <property name=\"icon\">\n    <iconset theme=\"document-verify\"/>\n   </property>\n   <property name=\"text\">\n    <string>Verify</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Verifies the current codeplug with connected radios.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>Ctrl+R</string>\n   </property>\n  </action>\n  <action name=\"actionDownload\">\n   <property name=\"icon\">\n    <iconset theme=\"device-read\"/>\n   </property>\n   <property name=\"text\">\n    <string>Read</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Reads a codeplug from connected radios.</string>\n   </property>\n  </action>\n  <action name=\"actionUpload\">\n   <property name=\"icon\">\n    <iconset theme=\"device-write\"/>\n   </property>\n   <property name=\"text\">\n    <string>Write</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Writes the codeplug to the connected radio.</string>\n   </property>\n  </action>\n  <action name=\"actionAbout\">\n   <property name=\"icon\">\n    <iconset theme=\"help-about\"/>\n   </property>\n   <property name=\"text\">\n    <string>About qdmr</string>\n   </property>\n  </action>\n  <action name=\"actionHelp\">\n   <property name=\"icon\">\n    <iconset theme=\"help-contents\"/>\n   </property>\n   <property name=\"text\">\n    <string>Help</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Read the handbook.</string>\n   </property>\n   <property name=\"shortcut\">\n    <string>F1</string>\n   </property>\n  </action>\n  <action name=\"actionSettings\">\n   <property name=\"icon\">\n    <iconset theme=\"application-settings\"/>\n   </property>\n   <property name=\"text\">\n    <string>Settings</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Shows settings dialog</string>\n   </property>\n  </action>\n  <action name=\"actionUploadCallsignDB\">\n   <property name=\"icon\">\n    <iconset theme=\"device-write-callsign\"/>\n   </property>\n   <property name=\"text\">\n    <string>Write Callsign DB</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Writes call-sign DB to radio.</string>\n   </property>\n  </action>\n  <action name=\"actionRefreshCallsignDB\">\n   <property name=\"text\">\n    <string>Refresh Callsign DB</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Refreshes the downloaded callsign DB</string>\n   </property>\n  </action>\n  <action name=\"actionRefreshTalkgroupDB\">\n   <property name=\"icon\">\n    <iconset theme=\"document-download\"/>\n   </property>\n   <property name=\"text\">\n    <string>Refresh Talkgroup DB</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Refreshes the downloaded talkgroup DB</string>\n   </property>\n  </action>\n  <action name=\"actionExportToCHIRP\">\n   <property name=\"text\">\n    <string>Export to CHIRP ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Exports all FM channels to CHIRP CSV.</string>\n   </property>\n  </action>\n  <action name=\"actionImport\">\n   <property name=\"text\">\n    <string>Import ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Imports and merges a codeplug into the current one.</string>\n   </property>\n  </action>\n  <action name=\"actionRefreshOrbitalElements\">\n   <property name=\"text\">\n    <string>Refresh Orbital Elements</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Refreshes the orbital elements.</string>\n   </property>\n   <property name=\"visible\">\n    <bool>true</bool>\n   </property>\n  </action>\n  <action name=\"actionEditSatellites\">\n   <property name=\"text\">\n    <string>Edit Satellites ...</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Opens an editor to edit your satellite database.</string>\n   </property>\n   <property name=\"visible\">\n    <bool>true</bool>\n   </property>\n  </action>\n  <action name=\"actionWriteSatellites\">\n   <property name=\"text\">\n    <string>Write satellites</string>\n   </property>\n   <property name=\"toolTip\">\n    <string>Writes the orbital elements and transponder information onto the connected device.</string>\n   </property>\n   <property name=\"visible\">\n    <bool>true</bool>\n   </property>\n  </action>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/melody_edit.cc",
    "content": "#include \"melody_edit.hh\"\n\n#include \"melody.hh\"\n#include \"melody_player.hh\"\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QPushButton>\n#include <QRegularExpressionValidator>\n#include <QSpinBox>\n\nMelodyEdit::MelodyEdit(QWidget *parent) :\n  QWidget(parent), _melody(), _bpm(new QSpinBox(this)),\n  _melodyEdit(new QLineEdit(this)), _player(new MelodyPlayer(this))\n{\n  _bpm->setRange(10,1000);\n  _bpm->setSuffix(tr(\"bpm\", \"Beats per minute. Unit in a spin box.\"));\n  _melodyEdit->setToolTip(tr(\"Specify the melody in Lilypond format.\",\n    \"Tooltip for a melody entry field.\"));\n  _melodyEdit->setValidator(\n    new QRegularExpressionValidator(\n      QRegularExpression(R\"(([a-zA-Z]+)([,]+|[']+|)(1|2|4|8|16|)(\\.|))\"\n        R\"((\\h+([a-zA-Z]+)([,]+|[']+|)(1|2|4|8|16|)(\\.|))*)\")));\n  _melodyEdit->setPlaceholderText(tr(\"Melody\"));\n\n  auto play = new QPushButton();\n  play->setIcon(QIcon::fromTheme(\"media-play\"));\n  play->setCheckable(true);\n  play->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);\n  connect(play, &QPushButton::toggled, _player, &MelodyPlayer::togglePlay);\n  connect(_player, &MelodyPlayer::stateChanged, play, &QPushButton::setChecked);\n\n  auto hbox = new QHBoxLayout(this);\n  hbox->addWidget(_bpm);\n  hbox->addWidget(_melodyEdit);\n  hbox->addWidget(play);\n  setLayout(hbox);\n\n  _bpm->setEnabled(false);\n  _melodyEdit->setEnabled(false);\n\n  connect(_bpm, &QSpinBox::valueChanged, [this](int value) {\n    if (!_melody.isNull())\n      _melody->setBPM(value);\n  });\n  connect(_melodyEdit, &QLineEdit::editingFinished, [this]() {\n    if (! _melody.isNull())\n      _melody->fromLilypond(this->_melodyEdit->text().simplified());\n  });\n}\n\nvoid\nMelodyEdit::setMelody(Melody *melody) {\n  if (! _melody.isNull())\n    disconnect(_melody, &Melody::modified, this, &MelodyEdit::onMelodyChanged);\n  _melody = melody;\n  if (! _melody.isNull()) {\n    onMelodyChanged(_melody);\n    connect(_melody, &ConfigItem::modified, this, &MelodyEdit::onMelodyChanged);\n  }\n  _bpm->setEnabled(! _melody.isNull());\n  _melodyEdit->setEnabled(! _melody.isNull());\n  _player->setMelody(melody);\n}\n\nvoid\nMelodyEdit::onMelodyChanged(ConfigItem *item) {\n  Q_UNUSED(item);\n  _bpm->setValue(_melody->bpm());\n  _melodyEdit->setText(_melody->toLilypond());\n  _player->togglePlay(false);\n}"
  },
  {
    "path": "src/melody_edit.hh",
    "content": "#ifndef QDMR_MELODY_EDIT_HH\n#define QDMR_MELODY_EDIT_HH\n\n#include <QWidget>\n#include <QPointer>\n\n#include \"melody_player.hh\"\n\nclass Melody;\nclass QSpinBox;\nclass QLineEdit;\nclass ConfigItem;\n\nclass MelodyEdit: public QWidget\n{\nQ_OBJECT\n\npublic:\n  explicit MelodyEdit(QWidget *parent = nullptr);\n\npublic slots:\n  /** Sets the melody to edit. */\n  void setMelody(Melody *melody);\n\nprotected slots:\n  /** Applies the changes to the wrapped melody object. */\n  void onMelodyChanged(ConfigItem *item);\n\nprivate:\n  QPointer<Melody> _melody;\n  QSpinBox *_bpm;\n  QLineEdit *_melodyEdit;\n  MelodyPlayer *_player;\n};\n\n\n\n#endif //QDMR_MELODY_EDIT_HH\n"
  },
  {
    "path": "src/melody_player.cc",
    "content": "#include \"melody_player.hh\"\n#include \"melody_stream.hh\"\n#include <QAudioDevice>\n#include <QMediaDevices>\n#include <QAudioSink>\n#include <QAudioFormat>\n\n\nMelodyPlayer::MelodyPlayer(QObject *parent)\n: QObject(parent), _stream(new MelodyStream(this)), _playbackSink(nullptr)\n{\n  QAudioDevice info(QMediaDevices::defaultAudioOutput());\n  if (! info.isFormatSupported(_stream->audioFormat()))\n    return;\n  _playbackSink = new QAudioSink(_stream->audioFormat(), this);\n  connect(_playbackSink, &QAudioSink::stateChanged, [this](QAudio::State state) {\n    emit stateChanged(state == QAudio::ActiveState);\n  });\n}\n\nMelodyPlayer::~MelodyPlayer() {\n  _playbackSink->stop();\n  _stream->close();\n}\n\nvoid\nMelodyPlayer::setMelody(Melody *melody) {\n  if (_stream->isOpen())\n    _stream->close();\n  _stream->setMelody(melody);\n  _stream->open(QIODevice::ReadOnly);\n}\n\nvoid\nMelodyPlayer::togglePlay(bool enable) {\n  if (nullptr == _playbackSink)\n    return;\n\n  if (enable && (QAudio::ActiveState != _playbackSink->state()))\n      _playbackSink->start(_stream);\n  else if (! enable && (QAudio::ActiveState == _playbackSink->state()))\n    _playbackSink->stop();\n}\n"
  },
  {
    "path": "src/melody_player.hh",
    "content": "#ifndef QDMR_MELODY_PLAYER_HH\n#define QDMR_MELODY_PLAYER_HH\n\n#include <QObject>\n\n\nclass Melody;\nclass MelodyStream;\nclass QAudioSink;\n\n/** Plays some melody. */\nclass MelodyPlayer: public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit MelodyPlayer(QObject *parent = nullptr);\n  virtual ~MelodyPlayer();\n\npublic slots:\n  void setMelody(Melody *melody);\n  void togglePlay(bool play);\n\nsignals:\n  void stateChanged(bool playing);\n\nprotected:\n  MelodyStream *_stream;\n  QAudioSink *_playbackSink;\n};\n\n#endif //QDMR_MELODY_PLAYER_HH\n"
  },
  {
    "path": "src/positioningsystemlistview.cc",
    "content": "#include \"positioningsystemlistview.hh\"\n#include \"ui_positioningsystemlistview.h\"\n#include \"config.hh\"\n\n#include \"aprssystemdialog.hh\"\n#include \"gpssystemdialog.hh\"\n#include \"settings.hh\"\n\n#include <QMessageBox>\n#include <QHeaderView>\n\nPositioningSystemListView::PositioningSystemListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::PositioningSystemListView), _config(config)\n{\n  Settings settings;\n\n  ui->setupUi(this);\n\n  connect(ui->aprsTableView->header(), SIGNAL(sectionCountChanged(int,int)),\n          this, SLOT(loadPositioningSectionState()));\n  connect(ui->aprsTableView->header(), SIGNAL(sectionResized(int,int,int)),\n          this, SLOT(storePositioningSectionState()));\n\n  ui->aprsTableView->setModel(new PositioningSystemListWrapper(_config->posSystems(), ui->aprsTableView));\n\n  if (settings.hideGSPNote())\n    ui->gpsNote->setVisible(false);\n\n  connect(ui->addGPS, SIGNAL(clicked()), this, SLOT(onAddGPS()));\n  connect(ui->addAPRS, SIGNAL(clicked()), this, SLOT(onAddAPRS()));\n  connect(ui->remGPS, SIGNAL(clicked()), this, SLOT(onRemGPS()));\n  connect(ui->aprsTableView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditGPS(unsigned)));\n  connect(ui->gpsNote, SIGNAL(linkActivated(QString)), this, SLOT(onHideGPSNote()));\n\n\n}\n\nPositioningSystemListView::~PositioningSystemListView() {\n  delete ui;\n}\n\n\nvoid\nPositioningSystemListView::onAddGPS() {\n  GPSSystemDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->aprsTableView->hasSelection())\n    row = ui->aprsTableView->selection().second+1;\n  _config->posSystems()->add(dialog.gpsSystem(), row);\n}\n\nvoid\nPositioningSystemListView::onAddAPRS() {\n  APRSSystemDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->aprsTableView->hasSelection())\n    row = ui->aprsTableView->selection().second+1;\n  _config->posSystems()->add(dialog.aprsSystem(), row);\n}\n\nvoid\nPositioningSystemListView::onRemGPS() {\n  if (! ui->aprsTableView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete GPS system\"),\n          tr(\"Cannot delete GPS system: You have to select a GPS system first.\"));\n    return;\n  }\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->aprsTableView->selection();\n  int rowcount = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->posSystems()->system(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete positioning system?\"), tr(\"Delete positioning system %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete positioning system?\"), tr(\"Delete %1 positioning systems?\").arg(rowcount)))\n      return;\n  }\n\n  // collect all selected systems\n  // need to collect them first as rows change when deleting systems\n  QList<PositionReportingSystem *> systems; systems.reserve(rowcount);\n  for(int row=rows.first; row<=rows.second; row++)\n    systems.push_back(_config->posSystems()->system(row));\n  // remove systems\n  foreach (PositionReportingSystem *system, systems)\n    _config->posSystems()->del(system);\n}\n\nvoid\nPositioningSystemListView::onEditGPS(unsigned row) {\n  PositionReportingSystem *sys = _config->posSystems()->system(row);\n\n  if (sys->is<DMRAPRSSystem>()) {\n    GPSSystemDialog dialog(_config, sys->as<DMRAPRSSystem>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n    dialog.gpsSystem();\n  } else if (sys->is<FMAPRSSystem>()) {\n    APRSSystemDialog dialog(_config, sys->as<FMAPRSSystem>());\n    if (QDialog::Accepted != dialog.exec())\n      return;\n    dialog.aprsSystem();\n  }\n}\n\nvoid\nPositioningSystemListView::onHideGPSNote() {\n  Settings setting; setting.setHideGPSNote(true);\n  ui->gpsNote->setVisible(false);\n}\n\nvoid\nPositioningSystemListView::loadPositioningSectionState() {\n  Settings settings;\n  ui->aprsTableView->header()->restoreState(settings.headerState(\"positioningList\"));\n}\nvoid\nPositioningSystemListView::storePositioningSectionState() {\n  Settings settings;\n  settings.setHeaderState(\"positioningList\", ui->aprsTableView->header()->saveState());\n}\n\n"
  },
  {
    "path": "src/positioningsystemlistview.hh",
    "content": "#ifndef POSITIONINGSYSTEMLISTVIEW_HH\n#define POSITIONINGSYSTEMLISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\n\nnamespace Ui {\n  class PositioningSystemListView;\n}\n\n\nclass PositioningSystemListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit PositioningSystemListView(Config *config, QWidget *parent = nullptr);\n  ~PositioningSystemListView();\n\nprotected slots:\n  void onAddGPS();\n  void onAddAPRS();\n  void onRemGPS();\n  void onEditGPS(unsigned);\n  void onHideGPSNote();\n  void loadPositioningSectionState();\n  void storePositioningSectionState();\n\nprivate:\n  Ui::PositioningSystemListView *ui;\n  Config *_config;\n};\n\n#endif // POSITIONINGSYSTEMLISTVIEW_HH\n"
  },
  {
    "path": "src/positioningsystemlistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>PositioningSystemListView</class>\n <widget class=\"QWidget\" name=\"PositioningSystemListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>445</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"gpsNote\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">padding:10px;border: 2px solid black; border-radius: 10px;</string>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::Shape::NoFrame</enum>\n     </property>\n     <property name=\"frameShadow\">\n      <enum>QFrame::Shadow::Plain</enum>\n     </property>\n     <property name=\"lineWidth\">\n      <number>2</number>\n     </property>\n     <property name=\"midLineWidth\">\n      <number>0</number>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::TextFormat::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n     <property name=\"margin\">\n      <number>5</number>\n     </property>\n     <property name=\"indent\">\n      <number>-1</number>\n     </property>\n     <property name=\"openExternalLinks\">\n      <bool>false</bool>\n     </property>\n     <property name=\"textInteractionFlags\">\n      <set>Qt::TextInteractionFlag::LinksAccessibleByKeyboard|Qt::TextInteractionFlag::LinksAccessibleByMouse</set>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"ConfigObjectTableView\" name=\"aprsTableView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_12\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addGPS\">\n       <property name=\"text\">\n        <string>Add GPS System</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+G</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"addAPRS\">\n       <property name=\"enabled\">\n        <bool>true</bool>\n       </property>\n       <property name=\"text\">\n        <string>Add APRS System</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+A</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remGPS\">\n       <property name=\"text\">\n        <string>Delete Position System</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectTableView</class>\n   <extends>QWidget</extends>\n   <header>configobjecttableview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/propertydelegate.cc",
    "content": "#include \"propertydelegate.hh\"\n#include \"extensionwrapper.hh\"\n\n#include <QListWidget>\n#include <QComboBox>\n#include <QSpinBox>\n#include <QLineEdit>\n#include <QDoubleValidator>\n#include \"flageditdialog.hh\"\n#include \"configreference.hh\"\n#include \"extensionwrapper.hh\"\n#include \"config.hh\"\n#include \"frequency.hh\"\n#include \"interval.hh\"\n\n\nPropertyDelegate::PropertyDelegate(QObject *parent)\n  : QStyledItemDelegate(parent), _config(nullptr)\n{\n  // pass...\n}\n\nvoid\nPropertyDelegate::setConfig(Config *config) {\n  _config = config;\n}\n\nconst PropertyWrapper *\nPropertyDelegate::getModel(const QAbstractItemModel *model) {\n  while (const QAbstractProxyModel *proxy = qobject_cast<const QAbstractProxyModel *>(model))\n    model = proxy->sourceModel();\n  return qobject_cast<const PropertyWrapper*>(model);\n}\n\nQWidget *\nPropertyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const {\n  Q_UNUSED(option)\n  const PropertyWrapper *model = getModel(index.model());\n  QMetaProperty prop = model->propertyAt(index);\n  ConfigItem *obj = model->parentObject(index);\n\n  if (! prop.isValid())\n    return nullptr;\n\n  // Dispatch by type\n  if (prop.isFlagType()) {\n    return new FlagEditDialog(prop.enumerator(), parent);\n  } if (prop.isEnumType()) {\n    return new QComboBox(parent);\n  } else if (QMetaType::Bool == prop.typeId()) {\n    return new QComboBox(parent);\n  } else if (QMetaType::Int == prop.typeId()) {\n    QSpinBox *edit = new QSpinBox(parent);\n    edit->setMinimum(std::numeric_limits<int>::min());\n    edit->setMaximum(std::numeric_limits<int>::max());\n    return edit;\n  } else if (QMetaType::UInt == prop.typeId()) {\n    QSpinBox *edit = new QSpinBox(parent);\n    edit->setMinimum(0);\n    edit->setMaximum(std::numeric_limits<int>::max());\n    return edit;\n  } else if (QMetaType::Double == prop.typeId()) {\n    QLineEdit *edit = new QLineEdit(parent);\n    edit->setValidator(new QDoubleValidator(edit));\n    return edit;\n  } else if (QMetaType::QString == prop.typeId()) {\n    return new QLineEdit(parent);\n  } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n    return new QLineEdit(parent);\n  } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n    return new QLineEdit(parent);\n  } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n    auto box = new QComboBox(parent);\n    box->addItem(tr(\"None\"), QVariant::fromValue(Level::invalid()));\n    box->addItem(tr(\"Off\"), QVariant::fromValue(Level::null()));\n    for (uint i=1; i<=10; i++)\n      box->addItem(QString::number(i), QVariant::fromValue(Level::fromValue(i)));\n    return box;\n  } else if (QMetaType::fromType<QGeoCoordinate>() == prop.metaType()) {\n    return new QLineEdit(parent);\n  } else if (prop.read(obj).value<ConfigObjectReference *>()) {\n    return new QComboBox(parent);\n  } else if (propIsInstance<ConfigItem>(prop)) {\n    return nullptr;\n  }\n  return nullptr;\n}\n\nvoid\nPropertyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const {\n  const PropertyWrapper *model = getModel(index.model());\n  ConfigItem *obj = model->parentObject(index);\n  QMetaProperty prop = model->propertyAt(index);\n\n  if (! prop.isValid())\n    return;\n\n  // Dispatch by type\n  if (prop.isFlagType()) {\n    auto box = dynamic_cast<FlagEditDialog *>(editor);\n    box->setValue(prop.read(obj).toInt());\n  } else if (prop.isEnumType()) {\n    QComboBox *box = dynamic_cast<QComboBox *>(editor);\n    QMetaEnum etype = prop.enumerator();\n    for (int i=0; i<etype.keyCount(); i++) {\n        box->addItem(etype.key(i), QVariant(etype.value(i)));\n        if (etype.value(i) == prop.read(obj).toInt())\n          box->setCurrentIndex(i);\n    }\n  } else if (QMetaType::Bool == prop.typeId()) {\n    QComboBox *box = dynamic_cast<QComboBox *>(editor);\n    box->addItem(tr(\"False\"), false);\n    box->addItem(tr(\"True\"), true);\n    if (prop.read(obj).toBool())\n      box->setCurrentIndex(1);\n    else\n      box->setCurrentIndex(0);\n  } else if ((QMetaType::Int == prop.typeId()) || (QMetaType::UInt == prop.typeId())) {\n    dynamic_cast<QSpinBox *>(editor)->setValue(prop.read(obj).toInt());\n  } else if (QMetaType::Double == prop.typeId()) {\n    dynamic_cast<QLineEdit *>(editor)->setText(QString::number(prop.read(obj).toDouble()));\n  } else if (QMetaType::QString == prop.typeId()) {\n    dynamic_cast<QLineEdit *>(editor)->setText(prop.read(obj).toString());\n  } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n    dynamic_cast<QLineEdit *>(editor)->setText(prop.read(obj).value<Frequency>().format());\n  } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n    dynamic_cast<QLineEdit *>(editor)->setText(prop.read(obj).value<Interval>().format());\n  } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n    auto box = dynamic_cast<QComboBox*>(editor);\n    auto level = prop.read(obj).value<Level>();\n    for (int i=0; i<box->count(); i++)\n      if (box->itemData(i).value<Level>() == level)\n        box->setCurrentIndex(i);\n  } else if (prop.read(obj).value<ConfigObjectReference *>()) {\n    ConfigObjectReference *ref = prop.read(obj).value<ConfigObjectReference *>();\n    // Find all matching elements in config that can be referenced\n    QSet<ConfigItem *> items;\n    if (_config)\n      _config->findItemsOfTypes(ref->elementTypeNames(), items);\n    // Assemble combo box with references\n    QComboBox *box = dynamic_cast<QComboBox*>(editor);\n    box->addItem(tr(\"[None]\"), QVariant::fromValue<ConfigObject*>(nullptr));\n    foreach (ConfigItem *item, items) {\n      if (! item->is<ConfigObject>())\n        continue;\n      ConfigObject *o = item->as<ConfigObject>();\n      box->addItem(o->name(),QVariant::fromValue<ConfigObject*>(o));\n    }\n  }\n}\n\nvoid\nPropertyDelegate::setModelData(QWidget *editor, QAbstractItemModel *abstractmodel, const QModelIndex &index) const {\n  Q_UNUSED(abstractmodel);\n  const PropertyWrapper *model = getModel(index.model());\n  ConfigItem *obj = model->parentObject(index);\n  QMetaProperty prop = model->propertyAt(index);\n  if (! prop.isValid())\n    return;\n\n  // Dispatch by type\n  if (prop.isFlagType()) {\n    auto edit = dynamic_cast<FlagEditDialog *>(editor);\n    prop.write(obj, edit->value());\n  } else if (prop.isEnumType()) {\n    prop.write(obj, dynamic_cast<QComboBox *>(editor)->currentData());\n  } else if (QMetaType::Bool == prop.typeId()) {\n    prop.write(obj, dynamic_cast<QComboBox *>(editor)->currentData());\n  } else if ((QMetaType::Int == prop.typeId()) || (QMetaType::UInt == prop.typeId())) {\n    prop.write(obj, dynamic_cast<QSpinBox *>(editor)->value());\n  } else if (QMetaType::Double == prop.typeId()) {\n    prop.write(obj, dynamic_cast<QLineEdit *>(editor)->text().toDouble());\n  } else if (QMetaType::QString == prop.typeId()) {\n    prop.write(obj, dynamic_cast<QLineEdit *>(editor)->text());\n  } else if (QMetaType::fromType<Frequency>() == prop.metaType()) {\n    Frequency f;\n    if (f.parse(dynamic_cast<QLineEdit *>(editor)->text()))\n      prop.write(obj, QVariant::fromValue(f));\n  } else if (QMetaType::fromType<Interval>() == prop.metaType()) {\n    Interval I;\n    if (I.parse(dynamic_cast<QLineEdit *>(editor)->text()))\n      prop.write(obj, QVariant::fromValue(I));\n  } else if (QMetaType::fromType<Level>() == prop.metaType()) {\n    prop.write(obj, qobject_cast<QComboBox*>(editor)->currentData());\n  } else if (prop.read(obj).value<ConfigObjectReference *>()) {\n    ConfigObjectReference *ref = prop.read(obj).value<ConfigObjectReference *>();\n    ref->set(dynamic_cast<QComboBox *>(editor)->currentData().value<ConfigObject*>());\n  }\n}\n"
  },
  {
    "path": "src/propertydelegate.hh",
    "content": "#ifndef PROPERTYDELEGATE_HH\n#define PROPERTYDELEGATE_HH\n\n#include <QStyledItemDelegate>\n\nclass Config;\nclass PropertyWrapper;\n\nclass PropertyDelegate : public QStyledItemDelegate\n{\n  Q_OBJECT\n\npublic:\n  explicit PropertyDelegate(QObject *parent=nullptr);\n\n  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;\n  void setEditorData(QWidget *editor, const QModelIndex &index) const;\n  void setModelData(QWidget *editor, QAbstractItemModel *abstractmodel, const QModelIndex &index) const;\n\n  void setConfig(Config *config);\n\nprotected:\n  static const PropertyWrapper *getModel(const QAbstractItemModel *model);\n\nprotected:\n  Config *_config;\n};\n\n#endif // PROPERTYDELEGATE_HH\n"
  },
  {
    "path": "src/radioidlistview.cc",
    "content": "#include \"radioidlistview.hh\"\n#include \"ui_radioidlistview.h\"\n#include \"config.hh\"\n#include \"configobjecttableview.hh\"\n#include \"configitemwrapper.hh\"\n#include \"dmriddialog.hh\"\n#include \"settings.hh\"\n#include <QMessageBox>\n#include <QHeaderView>\n\nRadioIDListView::RadioIDListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::RadioIDListView), _config(config)\n{\n  ui->setupUi(this);\n\n  connect(ui->radioIdTableView->header(), SIGNAL(sectionCountChanged(int,int)),\n          this, SLOT(loadHeaderState()));\n  connect(ui->radioIdTableView->header(), SIGNAL(sectionResized(int,int,int)),\n          this, SLOT(storeHeaderState()));\n\n  ui->radioIdTableView->setModel(new RadioIdListWrapper(_config->radioIDs(), ui->radioIdTableView));\n  ui->defaultID->setModel(new RadioIdListWrapper(_config->radioIDs(), ui->defaultID));\n  ui->defaultID->setModelColumn(1);\n\n  connect(_config, SIGNAL(modified(ConfigItem*)), this, SLOT(onConfigModified()));\n  connect(ui->addID, SIGNAL(clicked(bool)), this, SLOT(onAddID()));\n  connect(ui->delID, SIGNAL(clicked(bool)), this, SLOT(onDeleteID()));\n  connect(ui->radioIdTableView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditID(unsigned)));\n  connect(ui->defaultID, SIGNAL(currentIndexChanged(int)), this, SLOT(onDefaultIDSelected(int)));\n}\n\nRadioIDListView::~RadioIDListView() {\n  delete ui;\n}\n\nvoid\nRadioIDListView::onConfigModified() {\n  if (_config->settings()->defaultId()) {\n    ui->defaultID->setCurrentIndex(_config->radioIDs()->indexOf(_config->settings()->defaultId()));\n  } else {\n    ui->defaultID->setCurrentIndex(-1);\n  }\n}\n\nvoid\nRadioIDListView::onAddID() {\n  DMRIDDialog dialog(_config);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row = -1;\n  if (ui->radioIdTableView->hasSelection())\n    row = ui->radioIdTableView->selection().second;\n  _config->radioIDs()->add(dialog.radioId(), row);\n}\n\nvoid\nRadioIDListView::onDeleteID() {\n  if (! ui->radioIdTableView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete radio IDs\"),\n          tr(\"Cannot delete radio IDs: You have to select a radio ID first.\"));\n    return;\n  }\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->radioIdTableView->selection();\n  int numrows = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->radioIDs()->get(rows.first)->as<RadioID>()->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete radio ID?\"), tr(\"Delete radio ID %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete scan lists?\"), tr(\"Delete %1 scan lists?\").arg(numrows)))\n      return;\n  }\n  // collect all selected scan lists\n  // need to collect them first as rows change when deleting\n  QList<RadioID *> ids; ids.reserve(numrows);\n  for(int i=rows.first; i<=rows.second; i++)\n    ids.push_back(_config->radioIDs()->get(i)->as<RadioID>());\n  // remove\n  foreach (RadioID *id, ids)\n    _config->radioIDs()->del(id);\n}\n\nvoid\nRadioIDListView::onEditID(unsigned row) {\n  if ((int(row) >= _config->radioIDs()->count()) ||\n      (! _config->radioIDs()->get(row)->as<DMRRadioID>()))\n    return;\n  auto id = _config->radioIDs()->get(row)->as<DMRRadioID>();\n  DMRIDDialog dialog(id, _config);\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  // Apply changes\n  dialog.radioId();\n}\n\nvoid\nRadioIDListView::onDefaultIDSelected(int idx) {\n  if (((idx < 0) || (idx >= _config->radioIDs()->count())\n       || (! _config->radioIDs()->get(idx)->is<DMRRadioID>())))\n    return;\n  _config->settings()->setDefaultId(_config->radioIDs()->get(idx)->as<DMRRadioID>());\n}\n\nvoid\nRadioIDListView::loadHeaderState() {\n  ui->radioIdTableView->header()->restoreState(Settings().headerState(\"radioIDList\"));\n}\nvoid\nRadioIDListView::storeHeaderState() {\n  Settings().setHeaderState(\"radioIDList\", ui->radioIdTableView->header()->saveState());\n}\n\n\n\n"
  },
  {
    "path": "src/radioidlistview.hh",
    "content": "#ifndef RADIOIDLISTVIEW_HH\n#define RADIOIDLISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\n\nnamespace Ui {\n  class RadioIDListView;\n}\n\n\nclass RadioIDListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit RadioIDListView(Config *config, QWidget *parent = nullptr);\n  ~RadioIDListView();\n\nprotected slots:\n  void onConfigModified();\n  void onAddID();\n  void onDeleteID();\n  void onEditID(unsigned row);\n  void onDefaultIDSelected(int idx);\n  void loadHeaderState();\n  void storeHeaderState();\n\nprivate:\n  Ui::RadioIDListView *ui;\n  Config *_config;\n};\n\n#endif // RADIOIDLISTVIEW_HH\n"
  },
  {
    "path": "src/radioidlistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RadioIDListView</class>\n <widget class=\"QWidget\" name=\"RadioIDListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Default Radio ID</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QComboBox\" name=\"defaultID\"/>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"ConfigObjectTableView\" name=\"radioIdTableView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Expanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addID\">\n       <property name=\"text\">\n        <string>Add Radio ID</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"delID\">\n       <property name=\"text\">\n        <string>Delete Radio ID</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectTableView</class>\n   <extends>QWidget</extends>\n   <header>configobjecttableview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/radioidrepeatersource.cc",
    "content": "#include \"radioidrepeatersource.hh\"\n\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QJsonValue>\n#include <QJsonObject>\n#include <QJsonParseError>\n\n#include \"logger.hh\"\n\n\n\nRadioidRepeaterSource::RadioidRepeaterSource(QObject *parent)\n  : DownloadableRepeaterDatabaseSource(\n      \"radioidrepeater.cache.json\",\n      QUrl(\"https://radioid.net/static/map.json\"),\n      5, parent)\n{\n  // pass...\n}\n\n\nbool\nRadioidRepeaterSource::parse(const QByteArray &json) {\n  QJsonParseError err;\n  auto doc = QJsonDocument::fromJson(json, &err);\n  if (doc.isNull()) {\n    logError() << \"Cannot received JSON: \" << err.errorString() << \".\";\n    return false;\n  }\n\n  if ((! doc.isObject()) || (! doc.object().contains(\"markers\")) || (! doc.object()[\"markers\"].isArray())) {\n    logError() << \"Malformed result.\";\n    return false;\n  }\n\n  QJsonArray list = doc.object()[\"markers\"].toArray();\n  for (auto val: list) {\n    if (! val.isObject())\n      continue;\n\n    auto repeater = val.toObject();\n    if (\"ACTIVE\" != repeater[\"status\"].toString())\n      continue;\n\n    auto call = repeater[\"callsign\"].toString().simplified().toUpper();\n    auto rx = Frequency::fromMHz(repeater[\"frequency\"].toString().toDouble());\n    auto tx = Frequency::fromMHz(rx.inMHz() +  repeater[\"offset\"].toString().toDouble());\n    auto colorCode = repeater[\"color_code\"].toInt();\n    auto position = QGeoCoordinate(repeater[\"lat\"].toString().toDouble(),\n        repeater[\"lng\"].toString().toDouble());\n    auto qth = repeater[\"city\"].toString();\n\n    cache(RepeaterDatabaseEntry::dmr(call, rx, tx, position, qth, colorCode));\n  }\n\n  logDebug() << \"Loaded \" << _cache.size() << \" elements from \" << _url.toDisplayString() << \".\";\n  return true;\n}\n"
  },
  {
    "path": "src/radioidrepeatersource.hh",
    "content": "#ifndef RADIOIDREPEATERSOURCE_HH\n#define RADIOIDREPEATERSOURCE_HH\n\n#include \"repeaterdatabase.hh\"\n\n\nclass RadioidRepeaterSource : public DownloadableRepeaterDatabaseSource\n{\nQ_OBJECT\n\npublic:\n  explicit RadioidRepeaterSource(QObject *parent = nullptr);\n\nprotected:\n  bool parse(const QByteArray &doc);\n};\n\n#endif // RADIOIDREPEATERSOURCE_HH\n"
  },
  {
    "path": "src/radioselectiondialog.cc",
    "content": "#include \"radioselectiondialog.hh\"\n#include \"ui_radioselectiondialog.h\"\n\nRadioSelectionDialog::RadioSelectionDialog(const USBDeviceDescriptor &device, QWidget *parent) :\n  QDialog(parent),\n  ui(new Ui::RadioSelectionDialog)\n{\n  ui->setupUi(this);\n\n  _radios = RadioInfo::allRadios(device);\n  foreach (RadioInfo info, _radios) {\n    ui->radios->addItem(QString(\"%1 %2\").arg(info.manufacturer(), info.name()));\n  }\n\n  if (!_radios.isEmpty())\n    ui->radios->setCurrentIndex(0);\n\n  connect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nRadioSelectionDialog::~RadioSelectionDialog()\n{\n  delete ui;\n}\n\nRadioInfo\nRadioSelectionDialog::radioInfo() const {\n  return _radios.at(ui->radios->currentIndex());\n}\n\n\n"
  },
  {
    "path": "src/radioselectiondialog.hh",
    "content": "#ifndef RADIOSELECTIONDIALOG_HH\n#define RADIOSELECTIONDIALOG_HH\n\n#include <QDialog>\n#include \"usbdevice.hh\"\n#include \"radioinfo.hh\"\n\nnamespace Ui {\n  class RadioSelectionDialog;\n}\n\nclass RadioSelectionDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit RadioSelectionDialog(const USBDeviceDescriptor &device, QWidget *parent = nullptr);\n  ~RadioSelectionDialog();\n\n  RadioInfo radioInfo() const;\n\nprivate:\n  Ui::RadioSelectionDialog *ui;\n  QList<RadioInfo> _radios;\n};\n\n#endif // RADIOSELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/radioselectiondialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RadioSelectionDialog</class>\n <widget class=\"QDialog\" name=\"RadioSelectionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Cannot auto-detect radio</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"message\">\n     <property name=\"text\">\n      <string>Select a specific radio</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QComboBox\" name=\"radios\"/>\n   </item>\n   <item>\n    <widget class=\"QLabel\" name=\"description\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"text\">\n      <string/>\n     </property>\n     <property name=\"alignment\">\n      <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RadioSelectionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RadioSelectionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/releasenotes.cc",
    "content": "#include \"releasenotes.hh\"\n#include \"config.h\"\n#include \"settings.hh\"\n#include \"logger.hh\"\n\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QNetworkReply>\n#include <QMessageBox>\n\n\nReleaseNotes::ReleaseNotes(QObject *parent)\n  : QObject(parent)\n{\n  connect(&_net, SIGNAL(finished(QNetworkReply*)), this, SLOT(onResponse(QNetworkReply*)));\n}\n\nvoid\nReleaseNotes::checkForUpdate() {\n  Settings settings;\n  if (! settings.isUpdated())\n    return;\n\n  QString url = QString(\"https://api.github.com/repos/hmatuschek/qdmr/releases/tags/v%1\");\n  _net.get(QNetworkRequest(QUrl(url.arg(VERSION_STRING))));\n}\n\nvoid\nReleaseNotes::onResponse(QNetworkReply *reply) {\n  if (reply->error()) {\n    show(tr(\"Cannot download release notes from https://github.com/hmatuschek/qdmr\\n\\t %1\")\n         .arg(reply->errorString()));\n    return;\n  }\n\n  QByteArray data = reply->readAll();\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(data, &err);\n  if (QJsonParseError::NoError != err.error) {\n    logWarn() << \"JSON error in release data: \" << err.errorString();\n  }\n  if (! doc.isObject()) {\n    show(tr(\"Cannot read release notes from https://github.com/hmatuschek/qdmr\\n\\t\"\n            \"Release is not a JSON object!\"));\n    return;\n  }\n  if (! doc.object().contains(\"body\")) {\n    show(tr(\"Cannot read release notes from https://github.com/hmatuschek/qdmr\\n\\t\"\n            \"Release does not contain a release note.\"));\n    return;\n  }\n\n  show(doc.object()[\"body\"].toString());\n}\n\nvoid\nReleaseNotes::show(const QString &notes) {\n  QMessageBox::information(\n        nullptr, tr(\"qDMR was updated to version %1\").arg(VERSION_STRING),\n        notes);\n  Settings().markUpdated();\n}\n"
  },
  {
    "path": "src/releasenotes.hh",
    "content": "#ifndef RELEASENOTES_HH\n#define RELEASENOTES_HH\n\n#include <QObject>\n#include <QNetworkAccessManager>\n\n\nclass ReleaseNotes : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit ReleaseNotes(QObject *parent = nullptr);\n\n  void checkForUpdate();\n\npublic slots:\n  void show(const QString &notes);\n\nprotected slots:\n  void onResponse(QNetworkReply *reply);\n\nprotected:\n  QNetworkAccessManager _net;\n};\n\n#endif // RELEASENOTES_HH\n"
  },
  {
    "path": "src/repeaterbooksource.cc",
    "content": "#include \"repeaterbooksource.hh\"\n\n#include <QNetworkReply>\n#include <QRegularExpressionMatch>\n#include <QUrlQuery>\n#include <QJsonParseError>\n#include <QJsonDocument>\n#include <QJsonArray>\n#include <QJsonObject>\n#include <QJsonValue>\n\n#include \"logger.hh\"\n#include \"settings.hh\"\n#include \"config.h\"\n\n\nRepeaterBookSource::RepeaterBookSource(QObject *parent)\n  : CachedRepeaterDatabaseSource(\"repeaterbook.cache.json\", parent), _network(),\n    _currentReply(nullptr),\n    _callsignPattern(R\"re(([a-z]|[a-z0-9][a-z]|[a-z][a-z0-9])[0-9]+[a-z]*)re\",\n                     QRegularExpression::CaseInsensitiveOption)\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(onRequestFinished(QNetworkReply*)));\n}\n\n\nbool\nRepeaterBookSource::load(const QString &queryCall) {\n  // Cancel running requests\n  if (_currentReply)\n    _currentReply->abort();\n\n  QRegularExpressionMatch match = _callsignPattern.match(queryCall);\n  if (! match.hasMatch())\n    return false;\n  QString call = match.captured().toUpper();\n  logDebug() << \"Search for (partial) call '\" << call << \"'.\";\n\n  QUrl url;\n  if (Region::World == Settings().repeaterBookRegion())\n    url = QUrl(\"https://www.repeaterbook.com/api/exportROW.php\");\n  else\n    url = QUrl(\"https://www.repeaterbook.com/api/export.php\");\n\n  QUrlQuery query;\n  query.addQueryItem(\"callsign\", QString(\"%1%\").arg(call));\n  url.setQuery(query);\n  QNetworkRequest request(url);\n  request.setHeader(QNetworkRequest::UserAgentHeader,\n                    \"qdmr \" VERSION_STRING \" https://dm3mat.darc.de/qdmr/\");\n  logDebug() << \"Query RepeaterBook at \" << url.toString()\n             << \" as '\" << request.header(QNetworkRequest::UserAgentHeader).toString() << \"'.\";\n\n  _currentReply = _network.get(request);\n\n  return true;\n}\n\n\nvoid\nRepeaterBookSource::onRequestFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    logError() << \"Cannot download repeater list: \" << reply->errorString();\n    reply->deleteLater();\n    _currentReply = nullptr;\n    return;\n  }\n\n  QByteArray content = reply->readAll();\n  QJsonParseError err;\n  QJsonDocument doc = QJsonDocument::fromJson(content, &err);\n  if (doc.isNull()) {\n    logError() << \"Cannot parse response: \" << err.errorString() << \".\";\n    logDebug() << \"Got '\" << content << \"'.\";\n    reply->deleteLater();\n    _currentReply = nullptr;\n    return;\n  }\n\n  QString query = QUrlQuery(reply->request().url())\n      .queryItemValue(\"callsign\", QUrl::FullyDecoded).remove(\"%\");\n\n  reply->deleteLater();\n  _currentReply = nullptr;\n\n  if ((! doc.isObject()) || (! doc.object().contains(\"results\")) || (! doc.object()[\"results\"].isArray())) {\n    logError() << \"Cannot parse response: Unexpected structure.\";\n    reply->deleteLater();\n    _currentReply = nullptr;\n    return;\n  }\n\n  QJsonArray results = doc.object()[\"results\"].toArray();\n  foreach (const QJsonValue &rep, results) {\n    if (! rep.isObject())\n      continue;\n    QJsonObject obj = rep.toObject();\n\n    // Handle basic properties\n    auto call = obj[\"Callsign\"].toString().simplified().toUpper();\n    auto rxFrequency = Frequency::fromMHz(obj[\"Frequency\"].toString().toDouble());\n    auto txFrequency = Frequency::fromMHz(obj[\"Input Freq\"].toString().toDouble());\n    auto location = QGeoCoordinate(obj[\"Lat\"].toString().toDouble(), obj[\"Long\"].toString().toDouble());\n    auto qth = obj[\"Nearest City\"].toString();\n\n    auto type = RepeaterDatabaseEntry::Type::Invalid;\n    SelectiveCall rxTone, txTone;\n    unsigned int colorCode = 0;\n    QDateTime updated;\n\n    if (obj[\"FM Analog\"].toString() == \"Yes\") {\n      type = RepeaterDatabaseEntry::Type::FM;\n      if (obj.contains(\"PL\"))\n        txTone = SelectiveCall(obj[\"PL\"].toString().toDouble());\n      if (obj.contains(\"TSQ\"))\n        rxTone = SelectiveCall(obj[\"TSQ\"].toString().toDouble());\n    }\n\n    if (obj[\"DMR\"].toString() == \"Yes\") {\n      type = RepeaterDatabaseEntry::Type::DMR;\n      if (obj.contains(\"DMR Color Code\"))\n        colorCode = obj[\"DMR Color Code\"].toString().toInt();\n    }\n\n    if (obj.contains(\"timestamp\"))\n      updated = QDateTime::fromString(obj[\"timestamp\"].toString(), Qt::ISODate);\n\n    if (RepeaterDatabaseEntry::Type::FM == type) {\n      cache(RepeaterDatabaseEntry::fm(call, rxFrequency, txFrequency, location, qth,\n                                      rxTone, txTone, updated));\n    } else if (RepeaterDatabaseEntry::Type::DMR == type) {\n      cache(RepeaterDatabaseEntry::dmr(call, rxFrequency, txFrequency, location, qth,\n                                       colorCode, updated));\n    }\n  }\n\n  logDebug() << \"Updated repeater cache with \" << results.count() << \" entries.\";\n\n  saveCache();\n}\n"
  },
  {
    "path": "src/repeaterbooksource.hh",
    "content": "#ifndef REPEATERBOOKSOURCE_HH\n#define REPEATERBOOKSOURCE_HH\n\n#include \"repeaterdatabase.hh\"\n#include <QNetworkAccessManager>\n\n\nclass RepeaterBookSource : public CachedRepeaterDatabaseSource\n{\n  Q_OBJECT\n\npublic:\n  enum Region {\n    World, NorthAmerica\n  };\n  Q_ENUM(Region)\n\npublic:\n  explicit RepeaterBookSource(QObject *parent = nullptr);\n\nprotected slots:\n  void onRequestFinished(QNetworkReply *reply);\n\nprotected:\n  bool load(const QString &call) override;\n\nprotected:\n  QNetworkAccessManager _network;\n  QNetworkReply *_currentReply;\n  QRegularExpression _callsignPattern;\n};\n\n#endif // REPEATERBOOKSOURCE_HH\n"
  },
  {
    "path": "src/repeatercompleter.cc",
    "content": "#include \"repeatercompleter.hh\"\n#include \"repeaterdatabase.hh\"\n\n\n/* ********************************************************************************************* *\n * RepeaterCompleter\n * ********************************************************************************************* */\nRepeaterCompleter::RepeaterCompleter(int minPrefixLength, RepeaterDatabase *repeater, QObject *parent)\n  : QCompleter(parent), _repeaters(repeater),\n    _minPrefixLength(minPrefixLength)\n{\n  setModel(_repeaters);\n  setCaseSensitivity(Qt::CaseInsensitive);\n}\n\nQStringList\nRepeaterCompleter::splitPath(const QString &path) const {\n  if (path.length() >= _minPrefixLength)\n    _repeaters->query(path);\n  return QCompleter::splitPath(path);\n}\n\n\n\n/* ********************************************************************************************* *\n * NearestRepeaterFilter\n * ********************************************************************************************* */\nNearestRepeaterFilter::NearestRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent)\n  : QSortFilterProxyModel(parent), _repeater(repeater), _location(location)\n{\n  setSourceModel(repeater);\n  sort(0);\n}\n\nbool\nNearestRepeaterFilter::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const {\n  if (! _location.isValid())\n    return source_left.row() < source_right.row();\n\n  double ldist = _location.distanceTo(_repeater->get(source_left.row()).location());\n  double rdist = _location.distanceTo(_repeater->get(source_right.row()).location());\n\n  return ldist < rdist;\n}\n\n\n/* ********************************************************************************************* *\n * DMRRepeaterFilter\n * ********************************************************************************************* */\nDMRRepeaterFilter::DMRRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent)\n  : NearestRepeaterFilter(repeater, location, parent)\n{\n  invalidateFilter();\n}\n\nbool\nDMRRepeaterFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const  {\n  Q_UNUSED(source_parent);\n  if (source_row >= _repeater->rowCount(QModelIndex()))\n    return false;\n  return RepeaterDatabaseEntry::Type::DMR == _repeater->get(source_row).type();\n}\n\n\n/* ********************************************************************************************* *\n * FMRepeaterFilter\n * ********************************************************************************************* */\nFMRepeaterFilter::FMRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent)\n  : NearestRepeaterFilter(repeater, location, parent)\n{\n  invalidateFilter();\n}\n\nbool\nFMRepeaterFilter::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const  {\n  Q_UNUSED(source_parent);\n  if (source_row >= _repeater->rowCount(QModelIndex()))\n    return false;\n  return RepeaterDatabaseEntry::Type::FM == _repeater->get(source_row).type();\n}\n\n"
  },
  {
    "path": "src/repeatercompleter.hh",
    "content": "#ifndef REPEATERCOMPLETER_HH\n#define REPEATERCOMPLETER_HH\n\n#include <QCompleter>\n#include <QGeoCoordinate>\n#include <QSortFilterProxyModel>\n\nclass RepeaterDatabase;\n\n\nclass RepeaterCompleter: public QCompleter\n{\n  Q_OBJECT\n\npublic:\n  explicit RepeaterCompleter(int minPrefixLength, RepeaterDatabase *repeater, QObject *parent=nullptr);\n\n  QStringList splitPath(const QString &path) const override;\n\nprotected:\n  RepeaterDatabase *_repeaters;\n  int _minPrefixLength;\n};\n\n\n\nclass NearestRepeaterFilter: public QSortFilterProxyModel\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit NearestRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr);\n\nprotected:\n  bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;\n\nprotected:\n  RepeaterDatabase *_repeater;\n  QGeoCoordinate _location;\n};\n\n\n\n/** A filter proxy for DMR repeaters.\n * @ingroup util */\nclass DMRRepeaterFilter: public NearestRepeaterFilter\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit DMRRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr);\n\nprotected:\n  bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;\n};\n\n\n\n/** A filter proxy for analog FM repeaters.\n * @ingroup util */\nclass FMRepeaterFilter: public NearestRepeaterFilter\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit FMRepeaterFilter(RepeaterDatabase *repeater, const QGeoCoordinate &location, QObject *parent=nullptr);\n\nprotected:\n  bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;\n};\n\n\n#endif // REPEATERCOMPLETER_HH\n"
  },
  {
    "path": "src/repeaterdatabase.cc",
    "content": "#include \"repeaterdatabase.hh\"\n\n#include \"config.h\"\n#include \"logger.hh\"\n#include \"utils.hh\"\n#include <QApplication>\n#include <QDir>\n#include <QFile>\n#include <QJsonArray>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QNetworkReply>\n#include <QStandardPaths>\n#include <QtConcurrent>\n\n/* ********************************************************************************************* *\n * Helper functions\n * ********************************************************************************************* */\nstatic const QSet<double> _aprs_frequencies = {\n  144.390, 144.575, 144.660, 144.800, 144.930, 145.175, 145.570, 432.500\n};\n\ninline QString bandName(const Frequency &F) {\n  if (30 >= F.inMHz())\n    return \"HF\";\n  else if (300 >= F.inMHz())\n    return \"VHF\";\n  else if (3000 >= F.inMHz())\n    return \"UHF\";\n  else if (30000 >= F.inMHz())\n    return \"SHF\";\n  return \"EHF\";\n}\n\ninline QString bandName(const Frequency &rx, const Frequency & tx) {\n  QString rband = bandName(rx), tband=bandName(tx);\n  if ((rx == tx) && (_aprs_frequencies.contains(rx.inMHz())))\n    return \"APRS\";\n  else if (rband == tband)\n    return rband;\n  return QString(\"%1/%2\").arg(rband,tband);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RepeaterDatabaseEntry\n * ********************************************************************************************* */\nRepeaterDatabaseEntry::RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency,\n                                             const Frequency &txFrequency,\n                                             const QGeoCoordinate &location,\n                                             const QString &qth,\n                                             const SelectiveCall &rxTone,\n                                             const SelectiveCall &txTone,\n                                             const QDateTime &updated, const QDateTime& loaded)\n  : _type(Type::FM), _call(call), _rxFrequency(rxFrequency), _txFrequency(txFrequency),\n    _location(location), _qth(qth), _rxTone(rxTone), _txTone(txTone), _colorCode(0),\n    _updated(updated), _loaded(loaded)\n{\n  // pass...\n}\n\nRepeaterDatabaseEntry::RepeaterDatabaseEntry(const QString &call, const Frequency &rxFrequency,\n                                             const Frequency &txFrequency,\n                                             const QGeoCoordinate &location, const QString &qth,\n                                             unsigned int colorCode,\n                                             const QDateTime &updated, const QDateTime& loaded)\n  : _type(Type::DMR), _call(call), _rxFrequency(rxFrequency), _txFrequency(txFrequency),\n    _location(location), _qth(qth), _rxTone(), _txTone(), _colorCode(colorCode),\n    _updated(updated), _loaded(loaded)\n{\n  // pass...\n}\n\nRepeaterDatabaseEntry::RepeaterDatabaseEntry()\n  : _type(Type::Invalid), _call(), _rxFrequency(), _txFrequency(), _location(),\n    _rxTone(), _txTone(), _colorCode(0), _updated()\n{\n  // pass...\n}\n\nbool\nRepeaterDatabaseEntry::operator==(const RepeaterDatabaseEntry &other) const {\n  if (other._type != _type)\n    return false;\n  // Invalid entries are all equal\n  if (Type::Invalid == _type)\n    return true;\n\n  return (other._call == _call) && (other._rxFrequency == _rxFrequency);\n}\n\nbool\nRepeaterDatabaseEntry::operator<(const RepeaterDatabaseEntry &other) const {\n  return (_type < other._type) || (_call < other._call) || (_rxFrequency < other._rxFrequency);\n}\n\nQJsonValue\nRepeaterDatabaseEntry::toJson() const {\n  QJsonObject obj;\n\n  switch (type()) {\n  case Type::Invalid: return QJsonValue();\n  case Type::FM: obj.insert(\"type\", \"FM\"); break;\n  case Type::DMR: obj.insert(\"type\", \"DMR\"); break;\n  case Type::M17: obj.insert(\"type\", \"M17\"); break;\n  }\n\n  obj.insert(\"call\", call());\n\n  obj.insert(\"rx\", rxFrequency().format());\n  if (txFrequency().inHz())\n    obj.insert(\"tx\", txFrequency().format());\n\n  obj.insert(\"latitude\", location().latitude());\n  obj.insert(\"longitude\", location().longitude());\n  obj.insert(\"qth\", qth());\n\n  if (Type::FM == type()) {\n    if (rxTone().isInvalid()) obj.insert(\"rx-tone\", rxTone().format());\n    if (txTone().isInvalid()) obj.insert(\"tx-tone\", txTone().format());\n  } else if (Type::DMR == type()) {\n    obj.insert(\"color-code\", (int)colorCode());\n  }\n\n  if (updated().isValid())\n    obj.insert(\"updated\", updated().toString(Qt::ISODate));\n  if (loaded().isValid())\n    obj.insert(\"loaded\", loaded().toString(Qt::ISODate));\n\n  return obj;\n}\n\n\nRepeaterDatabaseEntry &\nRepeaterDatabaseEntry::operator+=(const RepeaterDatabaseEntry &other) {\n  if ((_type != other.type()) || (_call != other.call()))\n    return *this;\n\n  if ((!_loaded.isValid()) || (_loaded < other._loaded))\n    return (*this)=other;\n\n  if ((!_updated.isValid()) || (_updated < other._updated))\n    return (*this)=other;\n\n  if (! _location.isValid())\n    _location = other._location;\n  if (_qth.isEmpty())\n    _qth = other.qth();\n\n  if (Type::FM == _type) {\n    if (_rxTone.isInvalid())\n      _rxTone = other.rxTone();\n    if (_txTone.isInvalid())\n      _txTone = other.txTone();\n  } else if (Type::DMR == _type) {\n    if (0 == _colorCode)\n      _colorCode = other.colorCode();\n  }\n\n  return *this;\n}\n\n\nbool\nRepeaterDatabaseEntry::isValid() const {\n  return (Type::Invalid != _type) && (! _call.isEmpty()) && (_rxFrequency.inHz())\n      && (_txFrequency.inHz());\n}\n\nRepeaterDatabaseEntry::Type\nRepeaterDatabaseEntry::type() const {\n  return _type;\n}\n\nconst QString &\nRepeaterDatabaseEntry::call() const {\n  return _call;\n}\n\nconst Frequency &\nRepeaterDatabaseEntry::rxFrequency() const {\n  return _rxFrequency;\n}\nconst Frequency &\nRepeaterDatabaseEntry::txFrequency() const {\n  return _txFrequency;\n}\n\nconst QGeoCoordinate &\nRepeaterDatabaseEntry::location() const {\n  return _location;\n}\n\nQString\nRepeaterDatabaseEntry::locator() const {\n  return deg2loc(_location);\n}\n\nconst QString &\nRepeaterDatabaseEntry::qth() const {\n  return _qth;\n}\n\nconst SelectiveCall &\nRepeaterDatabaseEntry::rxTone() const {\n  return _rxTone;\n}\nconst SelectiveCall &\nRepeaterDatabaseEntry::txTone() const {\n  return _txTone;\n}\n\nunsigned int\nRepeaterDatabaseEntry::colorCode() const {\n  return _colorCode;\n}\n\nconst QDateTime &\nRepeaterDatabaseEntry::updated() const {\n  return _updated;\n}\nconst QDateTime &\nRepeaterDatabaseEntry::loaded() const {\n  return _loaded;\n}\n\n\nRepeaterDatabaseEntry\nRepeaterDatabaseEntry::fm(const QString &call, const Frequency &rxFrequency,\n                          const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth,\n                          const SelectiveCall &rxTone, const SelectiveCall &txTone,\n                          const QDateTime &updated, const QDateTime& loaded)\n{\n  return RepeaterDatabaseEntry(\n    call.simplified().toUpper(), rxFrequency, txFrequency,\n        location, qth, rxTone, txTone, updated, loaded);\n}\n\nRepeaterDatabaseEntry\nRepeaterDatabaseEntry::dmr(const QString &call, const Frequency &rxFrequency,\n                           const Frequency &txFrequency, const QGeoCoordinate &location, const QString &qth,\n                           unsigned int colorCode,\n                           const QDateTime &updated, const QDateTime& loaded)\n{\n  return RepeaterDatabaseEntry(\n    call.simplified().toUpper(), rxFrequency, txFrequency,\n        location, qth, colorCode, updated, loaded);\n}\n\nRepeaterDatabaseEntry\nRepeaterDatabaseEntry::fromJson(const QJsonObject &obj) {\n  if (obj.isEmpty() || (! obj.contains(\"type\")))\n    return RepeaterDatabaseEntry();\n\n  Type type = Type::Invalid;\n  if (\"FM\" == obj.value(\"type\").toString())\n    type = Type::FM;\n  else if (\"DMR\" == obj.value(\"type\").toString())\n    type = Type::DMR;\n  else if (\"M17\" == obj.value(\"type\").toString())\n    type = Type::M17;\n\n  QString call = obj.value(\"call\").toString().simplified().toUpper();\n\n  Frequency rx = Frequency::fromString(obj.value(\"rx\").toString()), tx;\n  if (obj.contains(\"tx\"))\n    tx = Frequency::fromString(obj.value(\"tx\").toString());\n\n  QGeoCoordinate location(obj.value(\"latitude\").toDouble(),\n                          obj.value(\"longitude\").toDouble());\n\n  QString qth = obj.value(\"qth\").toString();\n\n  QDateTime updated, loaded;\n  if (obj.contains(\"updated\"))\n    updated = QDateTime::fromString(obj.value(\"updated\").toString(), Qt::ISODate);\n  if (obj.contains(\"loaded\"))\n    loaded = QDateTime::fromString(obj.value(\"loaded\").toString(), Qt::ISODate);\n\n  if (Type::FM == type) {\n    SelectiveCall rxTone, txTone;\n    if (obj.contains(\"rx-tone\"))\n      rxTone = SelectiveCall::parseCTCSS(obj.value(\"rx-tone\").toString());\n    if (obj.contains(\"tx-tone\"))\n      txTone = SelectiveCall::parseCTCSS(obj.value(\"tx-tone\").toString());\n    return RepeaterDatabaseEntry::fm(\n          call, rx, tx, location, qth, rxTone, txTone, updated, loaded);\n  } else if (Type::DMR == type) {\n    unsigned int colorCode = 0;\n    if (obj.contains(\"color-code\"))\n      colorCode = obj.value(\"color-code\").toInt();\n    return RepeaterDatabaseEntry::dmr(\n          call, rx, tx, location, qth, colorCode, updated, loaded);\n  }\n\n  return RepeaterDatabaseEntry();\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RepeaterDatabaseSource\n * ********************************************************************************************* */\nRepeaterDatabaseSource::RepeaterDatabaseSource(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\nvoid\nRepeaterDatabaseSource::setSearchRadius(const QGeoCoordinate &position, unsigned int radius) {\n  Q_UNUSED(position); Q_UNUSED(radius);\n}\n\nbool\nRepeaterDatabaseSource::query(const QString &call) {\n  return this->load(call);\n}\n\nunsigned int\nRepeaterDatabaseSource::count() const {\n  return 0;\n}\n\nRepeaterDatabaseEntry\nRepeaterDatabaseSource::get(unsigned int idx) const {\n  Q_UNUSED(idx);\n  return RepeaterDatabaseEntry();\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of CachedRepeaterDatabaseSource\n * ********************************************************************************************* */\nCachedRepeaterDatabaseSource::CachedRepeaterDatabaseSource(const QString &filename, QObject *parent)\n  : RepeaterDatabaseSource{parent}, _parsing()\n{\n  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);\n\n  QDir directory;\n  if ((! directory.exists(path)) && (!directory.mkpath(path))) {\n    logError() << \"Cannot create path '\" << path << \"'.\";\n    return;\n  }\n\n  QFileInfo info(path + \"/\" + filename);\n  _cacheFile.setFileName(info.absoluteFilePath());\n  if (! info.isFile() || !info.isReadable())\n    return;\n\n  _parsing = QtConcurrent::run([this](){ QList<RepeaterDatabaseEntry> entries; this->parseCache(entries); return entries; })\n                 .then([this](const QList<RepeaterDatabaseEntry> &entries) { return this->loadEntries(entries); });\n}\n\n\nbool\nCachedRepeaterDatabaseSource::loadCache() {\n  QList<RepeaterDatabaseEntry> entries;\n  if (! parseCache(entries))\n    return false;\n  return loadEntries(entries);\n}\n\nbool\nCachedRepeaterDatabaseSource::parseCache(QList<RepeaterDatabaseEntry> &entries) {\n  if (! _cacheFile.open(QIODevice::ReadOnly)) {\n    logError() << \"Cannot open cache '\" << _cacheFile.fileName()\n               << \"': \" << _cacheFile.errorString() << \".\";\n    return false;\n  }\n\n  QJsonParseError error;\n  QJsonDocument doc = QJsonDocument::fromJson(_cacheFile.readAll(), &error);\n  _cacheFile.close();\n\n  if (doc.isNull()) {\n    logError() << \"Cannot parse cache '\" << _cacheFile.fileName()\n               << \"': \" << error.errorString() << \".\";\n    return false;\n  }\n\n  if (! doc.isArray()) {\n    logError() << \"Malformed cache file.\";\n    return false;\n  }\n\n  entries.clear();\n  for (QJsonValue obj: doc.array()) {\n    if (! obj.isObject())\n      continue;\n    RepeaterDatabaseEntry entry = RepeaterDatabaseEntry::fromJson(obj.toObject());\n    if (! entry.isValid())\n      continue;\n    entries.append(entry);\n  }\n\n  logDebug() << \"Loaded \" << entries.size() << \" entries from '\" << _cacheFile.fileName() << \"'.\";\n  return true;\n}\n\nbool CachedRepeaterDatabaseSource::loadEntries(const QList<RepeaterDatabaseEntry> &entries) {\n  _cache.clear();\n  for (auto entry: entries) {\n    if (! entry.isValid())\n      continue;\n    _cache.append(entry);\n    emit updated(entry);\n  }\n\n  return true;\n}\n\n\nvoid\nCachedRepeaterDatabaseSource::saveCache() {\n  if (! _cacheFile.open(QIODevice::WriteOnly)) {\n    logError() << \"Cannot open cache '\" << _cacheFile.fileName()\n               << \"': \" << _cacheFile.errorString() << \".\";\n    return;\n  }\n\n  QJsonArray entries;\n  for (auto entry: _cache)\n    entries.append(entry.toJson());\n  if (! _cacheFile.write(QJsonDocument(entries).toJson())) {\n    logError() << \"Cannot write cache '\" << _cacheFile.fileName()\n               << \"': \" << _cacheFile.errorString() << \".\";\n  }\n  _cacheFile.flush();\n  _cacheFile.close();\n}\n\n\nvoid\nCachedRepeaterDatabaseSource::cache(const RepeaterDatabaseEntry &entry) {\n  if (! _indices.contains(entry)) {\n    _indices[entry] = _cache.size();\n    _cache.append(entry);\n  } else {\n    _cache[_indices[entry]] += entry;\n  }\n\n  emit updated(entry);\n}\n\n\nunsigned int\nCachedRepeaterDatabaseSource::count() const {\n  return _cache.size();\n}\n\nRepeaterDatabaseEntry\nCachedRepeaterDatabaseSource::get(unsigned int idx) const {\n  return _cache.value(idx, RepeaterDatabaseEntry());\n}\n\nbool\nCachedRepeaterDatabaseSource::query(const QString &call) {\n  QString query = call.simplified().toUpper();\n  QDateTime newest;\n  for (const RepeaterDatabaseEntry &entry: _cache) {\n    if (entry.call().startsWith(query) && entry.loaded().isValid())\n      if ((! newest.isValid()) || (newest < entry.loaded()))\n        newest = entry.loaded();\n  }\n\n  if (newest.isValid() && (newest.daysTo(QDateTime::currentDateTime()) <= _maxAge))\n    return true;\n\n  return RepeaterDatabaseSource::query(call);\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of DownloadableRepeaterDatabaseSource\n * ********************************************************************************************* */\nDownloadableRepeaterDatabaseSource::DownloadableRepeaterDatabaseSource(\n    const QString &filename, const QUrl &source, unsigned int maxAge, QObject *parent)\n  : CachedRepeaterDatabaseSource(filename, parent), _url(source), _maxAge(maxAge),\n    _network(), _currentReply(nullptr)\n{\n  connect(&_network, SIGNAL(finished(QNetworkReply*)),\n          this, SLOT(onRequestFinished(QNetworkReply*)));\n\n  if (needsUpdate())\n    QTimer::singleShot(0, this, &DownloadableRepeaterDatabaseSource::download);\n}\n\nbool\nDownloadableRepeaterDatabaseSource::needsUpdate() const {\n  QFileInfo info(_cacheFile);\n  return (! info.exists()) || (info.lastModified().daysTo(QDateTime::currentDateTime()) > _maxAge);\n}\n\n\nbool\nDownloadableRepeaterDatabaseSource::load(const QString &call) {\n  Q_UNUSED(call);\n  // No action needed, downloads entire dataset.\n  return true;\n}\n\n\nvoid\nDownloadableRepeaterDatabaseSource::download() {\n  // If there is already a current request, done.\n  if (_currentReply)\n    return;\n\n  QNetworkRequest request(_url);\n  if (! prepareRequest(request))\n    return;\n\n  logDebug() << \"Query \" << _url.toString() << \"'.\";\n  _currentReply = _network.get(request);\n}\n\n\nbool\nDownloadableRepeaterDatabaseSource::prepareRequest(QNetworkRequest &request) {\n  if (request.hasRawHeader(\"X-API-Token\") && request.rawHeader(\"X-API-Token\").isEmpty()) {\n    logError() << \"An empty API token is set!\";\n    return false;\n  }\n\n  if (! request.hasRawHeader(\"User-Agent\")) {\n    request.setHeader(\n          QNetworkRequest::UserAgentHeader,\n          QLatin1String(\"qdmr/%1 (%2)\").arg(VERSION_STRING).arg(QGuiApplication::platformName()));\n  }\n\n  return true;\n}\n\n\nvoid\nDownloadableRepeaterDatabaseSource::onRequestFinished(QNetworkReply *reply) {\n  if (reply->error()) {\n    logError() << \"Cannot download repeater list: \" << reply->errorString();\n    reply->deleteLater();\n    _currentReply = nullptr;\n    return;\n  }\n\n  QByteArray content = reply->readAll();\n\n  reply->deleteLater();\n  _currentReply = nullptr;\n\n  if (parse(content))\n    saveCache();\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of RepeaterDatabase\n * ********************************************************************************************* */\nRepeaterDatabase::RepeaterDatabase(QObject *parent)\n  : QAbstractListModel{parent}, _searchPosition(), _searchRadius(0), _sources(), _indices(), _entries()\n{\n  // pass...\n}\n\n\nvoid\nRepeaterDatabase::setSearchRadius(const QGeoCoordinate &position, unsigned int radius) {\n  if ((0 == radius) || !position.isValid())\n    return;\n  for (auto source: _sources) {\n    source->setSearchRadius(position, radius);\n  }\n}\n\n\nvoid\nRepeaterDatabase::addSource(RepeaterDatabaseSource *source) {\n  if ((nullptr == source) || _sources.contains(source))\n    return;\n\n  for (unsigned int i=0; i<source->count(); i++) {\n    merge(source->get(i));\n  }\n\n  _sources.append(source);\n  source->setParent(this);\n  connect(source, &RepeaterDatabaseSource::updated,\n          this, &RepeaterDatabase::merge,\n          Qt::QueuedConnection);\n}\n\n\nvoid\nRepeaterDatabase::merge(const RepeaterDatabaseEntry &entry) {\n  if (_indices.contains(entry)) {\n    int row = _indices[entry];\n    RepeaterDatabaseEntry myEntry(_entries[row]);\n    if ((! myEntry.updated().isValid()) ||\n        (myEntry.updated().isValid() && entry.updated().isValid() &&\n         (myEntry.updated() < entry.updated()))) {\n      _entries[_indices[entry]] = entry;\n      emit dataChanged(index(row), index(row));\n    }\n    return;\n  }\n\n  int row = _entries.size();\n  beginInsertRows(QModelIndex(), row, row);\n  _entries.append(entry);\n  _indices[entry] = row;\n  endInsertRows();\n}\n\n\nint\nRepeaterDatabase::rowCount(const QModelIndex &parent) const {\n  Q_UNUSED(parent);\n  return _entries.size();\n}\n\nRepeaterDatabaseEntry\nRepeaterDatabase::get(unsigned int idx) const {\n  if (idx >= (unsigned int)_entries.size())\n    return RepeaterDatabaseEntry();\n  return _entries[idx];\n}\n\nQVariant\nRepeaterDatabase::data(const QModelIndex &index, int role) const {\n  if (index.row() >= _entries.count())\n    return QVariant();\n\n  if (Qt::EditRole == role) {\n    return _entries[index.row()].call();\n  } else if (Qt::DisplayRole == role) {\n    return QString(\"%1 (%2, %3, %4)\")\n        .arg(_entries[index.row()].call())\n        .arg(_entries[index.row()].qth())\n        .arg(bandName(_entries[index.row()].rxFrequency(),\n             _entries[index.row()].txFrequency()))\n        .arg(_entries[index.row()].locator());\n  }\n  return QVariant();\n}\n\n\nbool\nRepeaterDatabase::query(const QString &call) {\n  for (auto source: _sources) {\n    source->query(call);\n  }\n  return true;\n}\n\n\n\n"
  },
  {
    "path": "src/repeaterdatabase.hh",
    "content": "#ifndef REPEATERDATABASE_HH\n#define REPEATERDATABASE_HH\n\n#include <QAbstractListModel>\n#include <QGeoCoordinate>\n#include <QDateTime>\n#include <QFile>\n#include <QCompleter>\n#include <QSortFilterProxyModel>\n#include <QNetworkAccessManager>\n#include <QFuture>\n\n#include \"frequency.hh\"\n#include \"signaling.hh\"\n\n\nclass QNetworkReply;\n\n\n\n/** Repeater database entry.\n * Just a collection of some meaningful information for FM and DMR repeater. */\nclass RepeaterDatabaseEntry\n{\npublic:\n  /** The possible repeater types. */\n  enum class Type {\n    Invalid, FM, DMR, M17\n  };\n\nprotected:\n  /** FM Constructor. */\n  RepeaterDatabaseEntry(const QString &call,\n                        const Frequency &rxFrequency, const Frequency &txFrequency,\n                        const QGeoCoordinate &location,\n                        const QString &qth,\n                        const SelectiveCall &rxTone,\n                        const SelectiveCall &txTone,\n                        const QDateTime &updated,\n                        const QDateTime &loaded);\n\n  /** DMR Constructor. */\n  RepeaterDatabaseEntry(const QString &call,\n                        const Frequency &rxFrequency, const Frequency &txFrequency,\n                        const QGeoCoordinate &location,\n                        const QString &qth,\n                        unsigned int colorCode,\n                        const QDateTime &updated,\n                        const QDateTime &loaded);\n\npublic:\n  /** Default constructor, constructs an invalid entry. */\n  RepeaterDatabaseEntry();\n  /** Copy constructor. */\n  RepeaterDatabaseEntry(const RepeaterDatabaseEntry &other) = default;\n  /** Assignment operator. */\n  RepeaterDatabaseEntry &operator=(const RepeaterDatabaseEntry &other) = default;\n  /** Comparison. */\n  bool operator==(const RepeaterDatabaseEntry &other) const;\n  /** Comparison. */\n  bool operator<(const RepeaterDatabaseEntry &other) const;\n  /** Update operator. */\n  RepeaterDatabaseEntry &operator +=(const RepeaterDatabaseEntry &other);\n\n  QJsonValue toJson() const;\n\n  bool isValid() const;\n\n  Type type() const;\n  const QString &call() const;\n  const Frequency &rxFrequency() const;\n  const Frequency &txFrequency() const;\n\n  const QGeoCoordinate &location() const;\n  QString locator() const;\n  const QString &qth() const;\n\n  const SelectiveCall &rxTone() const;\n  const SelectiveCall &txTone() const;\n\n  unsigned int colorCode() const;\n\n  const QDateTime &updated() const;\n  const QDateTime &loaded() const;\n\npublic:\n  static RepeaterDatabaseEntry fm(const QString &call,\n                                  const Frequency &rxFrequency, const Frequency &txFrequency,\n                                  const QGeoCoordinate &location,\n                                  const QString &qth=\"\",\n                                  const SelectiveCall &rxTone=SelectiveCall(),\n                                  const SelectiveCall &txTone=SelectiveCall(),\n                                  const QDateTime &updated = QDateTime(),\n                                  const QDateTime &loaded = QDateTime::currentDateTime());\n\n  static RepeaterDatabaseEntry dmr(const QString &call,\n                                   const Frequency &rxFrequency, const Frequency &txFrequency,\n                                   const QGeoCoordinate &location,\n                                   const QString &qth=\"\",\n                                   unsigned int colorCode = 0,\n                                   const QDateTime &updated = QDateTime(),\n                                   const QDateTime &loaded = QDateTime::currentDateTime());\n\n  static RepeaterDatabaseEntry fromJson(const QJsonObject &obj);\n\nprotected:\n  Type           _type;\n  QString        _call;\n  Frequency      _rxFrequency;\n  Frequency      _txFrequency;\n  QGeoCoordinate _location;\n  QString        _qth;\n  SelectiveCall  _rxTone;\n  SelectiveCall  _txTone;\n  unsigned int   _colorCode;\n  QDateTime      _updated;\n  QDateTime      _loaded;\n};\n\n\n\n/** Base class for all database sources. */\nclass RepeaterDatabaseSource: public QObject\n{\n  Q_OBJECT\n\nprotected:\n  explicit RepeaterDatabaseSource(QObject *parent = nullptr);\n\npublic:\n  /** Sets the search radius (needed for some sources). */\n  virtual void setSearchRadius(const QGeoCoordinate &position, unsigned int radius);\n  /** Query some (partial-) call. */\n  virtual bool query(const QString &call);\n\n  /** Returns the number of stored entries. By default, none are stored. */\n  virtual unsigned int count() const;\n  /** Returns the i-th stored entry. */\n  virtual RepeaterDatabaseEntry get(unsigned int idx) const;\n\nsignals:\n  /** Gets emitted, once an entry gets updated or is added.\n   * @warning This signal may originate in a separate thread.*/\n  void updated(const RepeaterDatabaseEntry &entry);\n\nprotected:\n  /** Needs to be implemented to query new entries. */\n  virtual bool load(const QString &call) = 0;\n};\n\n\n\n/** Base class for all cached database sources. */\nclass CachedRepeaterDatabaseSource: public RepeaterDatabaseSource\n{\n  Q_OBJECT\n\nprotected:\n  CachedRepeaterDatabaseSource(const QString &filename, QObject *parent = nullptr);\n\npublic:\n  unsigned int count() const override;\n  RepeaterDatabaseEntry get(unsigned int idx) const override;\n\n  bool query(const QString &call) override;\n\nprotected:\n  bool loadCache();\n  bool parseCache(QList<RepeaterDatabaseEntry> &entries);\n  bool loadEntries(const QList<RepeaterDatabaseEntry> &entries);\n  void cache(const RepeaterDatabaseEntry &entry);\n  void saveCache();\n\nprotected:\n  unsigned int _maxAge;\n  QFile _cacheFile;\n  QMap<RepeaterDatabaseEntry, unsigned int> _indices;\n  QVector<RepeaterDatabaseEntry> _cache;\n  QFuture<void> _parsing;\n};\n\n\n\nclass DownloadableRepeaterDatabaseSource: public CachedRepeaterDatabaseSource\n{\n  Q_OBJECT\n\nprotected:\n  DownloadableRepeaterDatabaseSource(const QString &filename, const QUrl &source,\n                                     unsigned int maxAge=5,\n                                     QObject *parent=nullptr);\n\npublic:\n  bool needsUpdate() const;\n\nprotected:\n  /** Gets called to prepare a network request to fetch the data. */\n  virtual bool prepareRequest(QNetworkRequest &request);\n  virtual bool parse(const QByteArray &doc) = 0;\n  bool load(const QString &call) override;\n\nprotected slots:\n  void onRequestFinished(QNetworkReply *reply);\n  void download();\n\nprotected:\n  QUrl _url;\n  unsigned int _maxAge;\n  QNetworkAccessManager _network;\n  QNetworkReply *_currentReply;\n};\n\n\n\n/** Base class of all repeater databases. */\nclass RepeaterDatabase : public QAbstractListModel\n{\n  Q_OBJECT\n\npublic:\n  /** Constructor. */\n  explicit RepeaterDatabase(QObject *parent = nullptr);\n\n  /** Some sources require a search area to be set. This method forwards the search radius to all\n   * registered sources. */\n  void setSearchRadius(const QGeoCoordinate &coordinate, unsigned int radius);\n\n  RepeaterDatabaseEntry get(unsigned int idx) const;\n  void addSource(RepeaterDatabaseSource *source);\n\n  virtual bool query(const QString &call);\n\n  int rowCount(const QModelIndex &parent = QModelIndex()) const;\n  QVariant data(const QModelIndex &index, int role) const;\n\n\nprotected slots:\n  void merge(const RepeaterDatabaseEntry &entry);\n\n\nprotected:\n  QGeoCoordinate _searchPosition;\n  int _searchRadius;\n  QList<RepeaterDatabaseSource *> _sources;\n  QMap<RepeaterDatabaseEntry, unsigned int> _indices;\n  QVector<RepeaterDatabaseEntry> _entries;\n};\n\n\n\n\n#endif // REPEATERDATABASE_HH\n"
  },
  {
    "path": "src/repeatermapsource.cc",
    "content": "#include \"repeatermapsource.hh\"\n#include \"logger.hh\"\n#include <QJsonArray>\n#include <QJsonDocument>\n#include <QJsonObject>\n#include <QJsonParseError>\n#include <QJsonValue>\n#include <QUrlQuery>\n\n#include <qtimer.h>\n\nRepeaterMapSource::RepeaterMapSource(const QByteArray &apiToken, const QGeoCoordinate &position,\n  unsigned int radius, QObject *parent)\n  : DownloadableRepeaterDatabaseSource(\n      \"repeatermap.cache.json\",\n      QUrl(\"https://repeatermap.de/api/v1/repeaters.php\"),\n      5, parent), _apiToken(apiToken), _searchPosition(position), _searchRadius(radius)\n{\n  // pass...\n}\n\n\nvoid\nRepeaterMapSource::setSearchRadius(const QGeoCoordinate &pos, unsigned int radius) {\n  // Store position\n  QGeoCoordinate oldPos = _searchPosition;\n  _searchRadius = radius;\n  _searchPosition = pos;\n  // If position has moved by 10% of the search radius -> update\n  if (_searchRadius && _searchPosition.isValid() && _searchPosition.distanceTo(oldPos) > 100*_searchRadius)\n    QTimer::singleShot(0, this, &RepeaterMapSource::download);\n}\n\n\nbool\nRepeaterMapSource::prepareRequest(QNetworkRequest &request) {\n  // Set API token\n  request.setRawHeader(\"X-API-Token\", _apiToken.toHex().toLower());\n\n  // If position & radius is set -> add to request\n  if (_searchPosition.isValid() && _searchRadius) {\n    QUrl url(request.url());\n    QUrlQuery query(url);\n    logDebug() << \"Get repeater near \" << _searchPosition.toString()\n    << \" (radius \" << _searchRadius << \"km) from repeatermap.de\";\n    query.addQueryItem(\"lat\", QString::number(_searchPosition.latitude()));\n    query.addQueryItem(\"lon\", QString::number(_searchPosition.longitude()));\n    query.addQueryItem(\"radius\", QString::number(_searchRadius));\n    url.setQuery(query);\n    request.setUrl(url);\n  } else {\n    logInfo() << \"No search position/radius set, request list of german repeaters from repeatermap.de.\";\n  }\n\n  // Done.\n  return DownloadableRepeaterDatabaseSource::prepareRequest(request);\n}\n\n\nbool\nRepeaterMapSource::parse(const QByteArray &json) {\n  QJsonParseError err;\n  auto doc = QJsonDocument::fromJson(json, &err);\n  if (doc.isNull()) {\n    logError() << \"Cannot received JSON: \" << err.errorString() << \".\";\n    return false;\n  }\n  if ((! doc.isObject()) || (! doc.object().contains(\"repeaters\"))\n    || (! doc.object()[\"repeaters\"].isArray())) {\n    logError() << \"Malformed result.\";\n    return false;\n  }\n\n  QJsonArray list = doc.object()[\"repeaters\"].toArray();\n  for (auto val: list) {\n    if (! val.isObject())\n      continue;\n    auto repeater = val.toObject();\n\n    auto call = repeater[\"call\"].toString().simplified().toUpper();\n    auto mode = repeater[\"mode\"].toString();\n    auto tx = Frequency::fromMHz(repeater[\"tx\"].toDouble());\n    auto rx = Frequency::fromMHz(repeater[\"rx\"].toDouble());\n    auto position = QGeoCoordinate(repeater[\"lat\"].toDouble(), repeater[\"lon\"].toDouble());\n    auto qth = repeater[\"qth\"].toString();\n\n    if (\"FM\" == mode) {\n      cache(RepeaterDatabaseEntry::fm(call, rx, tx, position, qth));\n    } else if (\"DMR\" == mode) {\n      cache(RepeaterDatabaseEntry::dmr(call, rx, tx, position, qth));\n    }\n  }\n\n  logDebug() << \"Loaded \" << _cache.size() << \" elements from \" << _url.toDisplayString() << \".\";\n\n  return true;\n}\n"
  },
  {
    "path": "src/repeatermapsource.hh",
    "content": "#ifndef REPEATERMAPSOURCE_HH\n#define REPEATERMAPSOURCE_HH\n\n#include \"repeaterdatabase.hh\"\n\n\nclass RepeaterMapSource : public DownloadableRepeaterDatabaseSource\n{\n  Q_OBJECT\n\npublic:\n  explicit RepeaterMapSource(const QByteArray &apiToken, const QGeoCoordinate &position,\n    unsigned int radius, QObject *parent = nullptr);\n\n  void setSearchRadius(const QGeoCoordinate &position, unsigned int radius) override;\n\nprotected:\n  bool prepareRequest(QNetworkRequest &request) override;\n  bool parse(const QByteArray &doc) override;\n\nprotected:\n  QByteArray _apiToken;\n  QGeoCoordinate _searchPosition;\n  unsigned int _searchRadius;\n};\n\n#endif // REPEATERMAPSOURCE_HH\n"
  },
  {
    "path": "src/roamingchanneldialog.cc",
    "content": "#include \"roamingchanneldialog.hh\"\n#include \"ui_roamingchanneldialog.h\"\n#include \"roamingchannel.hh\"\n#include \"utils.hh\"\n\nRoamingChannelDialog::RoamingChannelDialog(Config *config, QWidget *parent)\n  : QDialog(parent), ui(new Ui::RoamingChannelDialog), _myChannel(new RoamingChannel(this)),\n    _channel(nullptr)\n{\n  Q_UNUSED(config)\n\n  ui->setupUi(this);\n\n  construct();\n}\n\nRoamingChannelDialog::RoamingChannelDialog(Config *config, RoamingChannel *channel, QWidget *parent)\n  : QDialog(parent), ui(new Ui::RoamingChannelDialog), _myChannel(new RoamingChannel(this)),\n    _channel(channel)\n{\n  Q_UNUSED(config)\n\n  ui->setupUi(this);\n  if (_channel)\n    _myChannel->copy(*_channel);\n\n  construct();\n}\n\nRoamingChannelDialog::~RoamingChannelDialog() {\n  delete ui;\n}\n\nvoid\nRoamingChannelDialog::construct() {\n  if (_channel)\n    setWindowTitle(tr(\"Edit roaming channel\"));\n  else\n    setWindowTitle(tr(\"Create roaming channel\"));\n\n  ui->name->setText(_myChannel->name());\n  ui->rxFrequency->setValidator(new QDoubleValidator(0,500,5));\n  ui->rxFrequency->setText(_myChannel->rxFrequency().format(Frequency::Unit::MHz));\n  ui->txFrequency->setValidator(new QDoubleValidator(0,500,5));\n  ui->txFrequency->setText(_myChannel->txFrequency().format(Frequency::Unit::MHz));\n  ui->timeSlot->addItem(tr(\"TS 1\"), QVariant::fromValue(DMRChannel::TimeSlot::TS1));\n  ui->timeSlot->addItem(tr(\"TS 2\"), QVariant::fromValue(DMRChannel::TimeSlot::TS2));\n  ui->timeSlot->setCurrentIndex(\n        (DMRChannel::TimeSlot::TS1 == _myChannel->timeSlot()) ? 0 : 1);\n  if (! _myChannel->timeSlotOverridden()) {\n    ui->overrideTimeSlot->setChecked(true);\n    ui->timeSlot->setEnabled(false);\n  }\n  ui->colorCode->setValue(_myChannel->colorCode());\n  if (! _myChannel->colorCodeOverridden()) {\n    ui->overrideColorCode->setChecked(true);\n    ui->colorCode->setEnabled(false);\n  }\n\n  connect(ui->overrideTimeSlot, SIGNAL(toggled(bool)),\n          this, SLOT(onOverrideTimeSlotToggled(bool)));\n  connect(ui->overrideColorCode, SIGNAL(toggled(bool)),\n          this, SLOT(onOverrideColorCodeToggled(bool)));\n}\n\n\nvoid\nRoamingChannelDialog::onOverrideTimeSlotToggled(bool override) {\n  ui->timeSlot->setEnabled(! override);\n}\n\nvoid\nRoamingChannelDialog::onOverrideColorCodeToggled(bool override) {\n  ui->colorCode->setEnabled(! override);\n}\n\nRoamingChannel *\nRoamingChannelDialog::channel() {\n  _myChannel->setName(ui->name->text().simplified());\n  _myChannel->setRXFrequency(Frequency::fromString(ui->rxFrequency->text()));\n  _myChannel->setTXFrequency(Frequency::fromString(ui->txFrequency->text()));\n  _myChannel->setTimeSlot(ui->timeSlot->currentData().value<DMRChannel::TimeSlot>());\n  _myChannel->overrideTimeSlot(! ui->overrideTimeSlot->isChecked());\n  _myChannel->setColorCode(ui->colorCode->value());\n  _myChannel->overrideColorCode(! ui->overrideColorCode->isChecked());\n\n  if (_channel) {\n    _channel->copy(*_myChannel);\n    return _channel;\n  }\n\n  _myChannel->setParent(nullptr);\n  return _myChannel;\n}\n"
  },
  {
    "path": "src/roamingchanneldialog.hh",
    "content": "#ifndef ROAMINGCHANNELDIALOG_HH\n#define ROAMINGCHANNELDIALOG_HH\n\n#include <QDialog>\n\n// Forward declarations\nnamespace Ui {\n  class RoamingChannelDialog;\n}\nclass Config;\nclass RoamingChannel;\n\n\nclass RoamingChannelDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  RoamingChannelDialog(Config *config, QWidget *parent = nullptr);\n  RoamingChannelDialog(Config *config, RoamingChannel *channel, QWidget *parent = nullptr);\n  ~RoamingChannelDialog();\n\n  RoamingChannel *channel();\n\nprotected slots:\n  void construct();\n  void onOverrideTimeSlotToggled(bool override);\n  void onOverrideColorCodeToggled(bool override);\n\nprivate:\n  Ui::RoamingChannelDialog *ui;\n  RoamingChannel *_myChannel;\n  RoamingChannel *_channel;\n};\n\n#endif // ROAMINGCHANNELDIALOG_HH\n"
  },
  {
    "path": "src/roamingchanneldialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RoamingChannelDialog</class>\n <widget class=\"QDialog\" name=\"RoamingChannelDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QFormLayout\" name=\"formLayout\">\n     <item row=\"0\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label\">\n       <property name=\"text\">\n        <string>Name</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"1\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_2\">\n       <property name=\"text\">\n        <string>RX Frequency [MHz]</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"2\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_3\">\n       <property name=\"text\">\n        <string>TX Frequency [MHz]</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"3\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_4\">\n       <property name=\"text\">\n        <string>Time Slot</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"4\" column=\"0\">\n      <widget class=\"QLabel\" name=\"label_5\">\n       <property name=\"text\">\n        <string>Color Code</string>\n       </property>\n      </widget>\n     </item>\n     <item row=\"0\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"name\"/>\n     </item>\n     <item row=\"1\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"rxFrequency\"/>\n     </item>\n     <item row=\"2\" column=\"1\">\n      <widget class=\"QLineEdit\" name=\"txFrequency\"/>\n     </item>\n     <item row=\"3\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n       <item>\n        <widget class=\"QCheckBox\" name=\"overrideTimeSlot\">\n         <property name=\"text\">\n          <string>Selected</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QComboBox\" name=\"timeSlot\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n     <item row=\"4\" column=\"1\">\n      <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n       <item>\n        <widget class=\"QCheckBox\" name=\"overrideColorCode\">\n         <property name=\"text\">\n          <string>Selected</string>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QSpinBox\" name=\"colorCode\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n      </layout>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RoamingChannelDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RoamingChannelDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/roamingchannellistview.cc",
    "content": "#include \"roamingchannellistview.hh\"\n#include \"ui_roamingchannellistview.h\"\n#include \"roamingchanneldialog.hh\"\n#include \"configitemwrapper.hh\"\n#include \"config.hh\"\n#include \"settings.hh\"\n\n#include <QMessageBox>\n\nRoamingChannelListView::RoamingChannelListView(Config *config, QWidget *parent) :\n  QWidget(parent), _config(config), ui(new Ui::RoamingChannelListView)\n{\n  Settings settings;\n\n  ui->setupUi(this);\n\n  ui->roamingChannelTableView->setModel(\n        new RoamingChannelListWrapper(_config->roamingChannels(), ui->roamingChannelTableView));\n\n  connect(ui->addRoamingChannel, SIGNAL(clicked()), this, SLOT(onAddChannel()));\n  connect(ui->remRoamingChannel, SIGNAL(clicked()), this, SLOT(onRemChannel()));\n  connect(ui->roamingChannelTableView, SIGNAL(doubleClicked(unsigned)),\n          this, SLOT(onEditChannel(unsigned)));\n\n  connect(ui->roamingChannelListHint, SIGNAL(linkActivated(QString)),\n          this, SLOT(onHideRoamingNote()));\n\n  if (settings.hideRoamingNote())\n    ui->roamingChannelListHint->setHidden(true);\n}\n\nRoamingChannelListView::~RoamingChannelListView() {\n  delete ui;\n}\n\nvoid\nRoamingChannelListView::onAddChannel() {\n  RoamingChannelDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->roamingChannelTableView->hasSelection())\n    row = ui->roamingChannelTableView->selection().second+1;\n  _config->roamingChannels()->add(dialog.channel(), row);\n\n}\n\nvoid\nRoamingChannelListView::onEditChannel(unsigned int idx) {\n  RoamingChannelDialog dialog(_config, _config->roamingChannels()->channel(idx));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  dialog.channel();\n}\n\nvoid\nRoamingChannelListView::onRemChannel() {\n  if (! ui->roamingChannelTableView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete roaming channel\"),\n          tr(\"Cannot delete roaming channel: You have to select a channel first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int, int> rows = ui->roamingChannelTableView->selection();\n  int rowcount = rows.second - rows.first + 1;\n  if (rows.first==rows.second) {\n    QString name = _config->roamingChannels()->channel(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete roaming channel?\"), tr(\"Delete roaming channel %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete roaming channel?\"), tr(\"Delete %1 roaming channel?\").arg(rowcount)))\n      return;\n  }\n  // collect all selected channels\n  // need to collect them first as rows change when deleting\n  QList<RoamingChannel *> lists; lists.reserve(rowcount);\n  for (int row=rows.first; row<=rows.second; row++)\n    lists.push_back(_config->roamingChannels()->channel(row));\n  // remove\n  foreach (RoamingChannel *channel, lists)\n    _config->roamingChannels()->del(channel);\n}\n\nvoid\nRoamingChannelListView::onHideRoamingNote() {\n  Settings settings; settings.setHideRoamingNote(true);\n  ui->roamingChannelListHint->setVisible(false);\n}\n"
  },
  {
    "path": "src/roamingchannellistview.hh",
    "content": "#ifndef ROAMINGCHANNELLISTVIEW_HH\n#define ROAMINGCHANNELLISTVIEW_HH\n\n#include <QWidget>\n\n// Forward declarations\nnamespace Ui {\n  class RoamingChannelListView;\n}\nclass Config;\n\n\nclass RoamingChannelListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit RoamingChannelListView(Config *config, QWidget *parent = nullptr);\n  ~RoamingChannelListView();\n\nprotected slots:\n  void onAddChannel();\n  void onEditChannel(unsigned int idx);\n  void onRemChannel();\n  void onHideRoamingNote();\n\nprotected:\n  /** Holds a weak reference to the abstract config. */\n  Config *_config;\n\nprivate:\n  Ui::RoamingChannelListView *ui;\n};\n\n#endif // ROAMINGCHANNELLISTVIEW_HH\n"
  },
  {
    "path": "src/roamingchannellistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RoamingChannelListView</class>\n <widget class=\"QWidget\" name=\"RoamingChannelListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n   <item>\n    <widget class=\"QLabel\" name=\"roamingChannelListHint\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">padding:10px;border: 2px solid black; border-radius: 10px;</string>\n     </property>\n     <property name=\"frameShape\">\n      <enum>QFrame::NoFrame</enum>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Hint:&lt;/span&gt; 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.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"ConfigObjectTableView\" name=\"roamingChannelTableView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addRoamingChannel\">\n       <property name=\"text\">\n        <string>Add Roaming Channel</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remRoamingChannel\">\n       <property name=\"text\">\n        <string>Delete Roaming Channel</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectTableView</class>\n   <extends>QWidget</extends>\n   <header>configobjecttableview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/roamingchannelselectiondialog.cc",
    "content": "#include \"roamingchannelselectiondialog.hh\"\n#include \"config.hh\"\n\n#include <QListWidget>\n#include <QDialogButtonBox>\n#include <QVBoxLayout>\n\nMultiRoamingChannelSelectionDialog::MultiRoamingChannelSelectionDialog(\n    Config *config, RoamingChannelRefList *exclude, QWidget *parent)\n  : QDialog(parent), _channels(nullptr)\n{\n  setWindowTitle(tr(\"Select roaming channels\"));\n  _channels = new QListWidget();\n  for (int i=0; i<config->roamingChannels()->count(); i++) {\n    auto channel = config->roamingChannels()->channel(i);\n    if (exclude && exclude->has(channel))\n      continue;\n    auto item = new QListWidgetItem(channel->name());\n    item->setFlags(Qt::ItemIsUserCheckable|Qt::ItemIsEnabled);\n    item->setData(Qt::UserRole, QVariant::fromValue(channel));\n    item->setCheckState(Qt::Unchecked);\n    _channels->addItem(item);\n  }\n\n  auto box = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);\n\n  auto layout = new QVBoxLayout();\n  layout->addWidget(_channels);\n  layout->addWidget(box);\n  setLayout(layout);\n\n  connect(box, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(box, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nQList<RoamingChannel *>\nMultiRoamingChannelSelectionDialog::channels() const {\n  QList<RoamingChannel *> channels;\n  for (int i=0; i<_channels->count(); i++) {\n    if (Qt::Checked ==_channels->item(i)->checkState())\n      channels.push_back(_channels->item(i)->data(Qt::UserRole).value<RoamingChannel*>());\n  }\n  return channels;\n}\n"
  },
  {
    "path": "src/roamingchannelselectiondialog.hh",
    "content": "#ifndef MULTIROAMINGCHANNELSELECTIONDIALOG_HH\n#define MULTIROAMINGCHANNELSELECTIONDIALOG_HH\n\n#include <QDialog>\n#include <QList>\n\n// Forward declarations\nclass Config;\nclass RoamingChannel;\nclass QListWidget;\nclass RoamingChannelRefList;\n\n\nclass MultiRoamingChannelSelectionDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  MultiRoamingChannelSelectionDialog(Config *config, RoamingChannelRefList *exclude=nullptr, QWidget *parent=nullptr);\n\n  QList<RoamingChannel *> channels() const;\n\nprotected:\n  QListWidget *_channels;\n};\n\n#endif // MULTIROAMINGCHANNELSELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/roamingzonedialog.cc",
    "content": "#include \"roamingzonedialog.hh\"\n#include \"settings.hh\"\n#include \"config.hh\"\n#include \"channelselectiondialog.hh\"\n#include \"roamingchannelselectiondialog.hh\"\n#include <QMessageBox>\n\n\n/* ****************************************************************************************** *\n * RoamingZoneDialog\n * ****************************************************************************************** */\nRoamingZoneDialog::RoamingZoneDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myZone(new RoamingZone(this)), _zone(nullptr)\n{\n  setWindowTitle(tr(\"Create Roaming Zone\"));\n  construct();\n}\n\nRoamingZoneDialog::RoamingZoneDialog(Config *config, RoamingZone *zone, QWidget *parent)\n  : QDialog(parent), _config(config), _myZone(new RoamingZone(this)), _zone(zone)\n{\n  setWindowTitle(tr(\"Set Roaming Zone\"));\n\n  if (_zone)\n    _myZone->copy(*_zone);\n\n  construct();\n}\n\n\nvoid\nRoamingZoneDialog::construct() {\n  setupUi(this);\n  Settings settings;\n\n  zoneName->setText(_myZone->name());\n  channelListView->setModel(new RoamingChannelRefListWrapper(_myZone->channels(), channelListView));\n\n  extensionView->setObjectName(\"roamingZoneExtension\");\n  extensionView->setObject(_myZone, _config);\n\n  connect(addRoam, SIGNAL(clicked(bool)), this, SLOT(onAddRoamingChannel()));\n  connect(addDMR, SIGNAL(clicked(bool)), this, SLOT(onAddDMRChannel()));\n  connect(rem, SIGNAL(clicked(bool)), this, SLOT(onRemChannel()));\n}\n\n\nvoid\nRoamingZoneDialog::onAddDMRChannel() {\n  MultiChannelSelectionDialog dia(_config->channelList(), false, true);\n  if (QDialog::Accepted != dia.exec())\n    return;\n\n  QList<Channel *> lst = dia.channel();\n  foreach (Channel *channel, lst) {\n    if (0 <= _myZone->channels()->indexOf(channel))\n      continue;\n    if (! channel->is<DMRChannel>())\n      continue;\n    RoamingChannel *rch = RoamingChannel::fromDMRChannel(channel->as<DMRChannel>());\n    _config->roamingChannels()->add(rch);\n    _myZone->addChannel(rch);\n  }\n}\n\nvoid\nRoamingZoneDialog::onAddRoamingChannel() {\n  MultiRoamingChannelSelectionDialog dia(_config, _myZone->channels());\n  if (QDialog::Accepted != dia.exec())\n    return;\n\n  QList<RoamingChannel *> lst = dia.channels();\n  foreach (RoamingChannel *channel, lst) {\n    if (0 <= _myZone->channels()->indexOf(channel))\n      continue;\n    _myZone->addChannel(channel);\n  }\n}\n\nvoid\nRoamingZoneDialog::onRemChannel() {\n  if (! channelListView->hasSelection()) {\n    QMessageBox::information(nullptr, tr(\"Cannot remove channels.\"),\n                             tr(\"Cannot remove channels. Select at least one channel first.\"),\n                             QMessageBox::Close, QMessageBox::Close);\n    return;\n  }\n\n  QPair<int, int> selection = channelListView->selection();\n  QList<RoamingChannel *> channels;\n  for (int i=selection.first; i<=selection.second; i++)\n    channels.push_back(_myZone->channels()->get(i)->as<RoamingChannel>());\n  foreach (RoamingChannel *channel, channels) {\n    _myZone->channels()->del(channel);\n  }\n}\n\nRoamingZone *\nRoamingZoneDialog::zone() {\n  _myZone->setName(zoneName->text().simplified());\n\n  RoamingZone *zone = _myZone;\n  if (_zone) {\n    _zone->copy(*_myZone);\n    zone = _zone;\n  } else {\n    _myZone->setParent(nullptr);\n  }\n\n  return zone;\n}\n\n\n\n/* ****************************************************************************************** *\n * RoamingZoneSelect\n * ****************************************************************************************** */\nRoamingZoneSelect::RoamingZoneSelect(Config *config, QWidget *parent)\n  : QComboBox(parent)\n{\n  addItem(tr(\"[None]\"), QVariant::fromValue((RoamingZone *)nullptr));\n  addItem(tr(\"[Default]\"), QVariant::fromValue(DefaultRoamingZone::get()));\n  for (int i=0; i<config->roamingZones()->count(); i++) {\n    auto zone = config->roamingZones()->zone(i);\n    addItem(zone->name(), QVariant::fromValue(zone));\n  }\n}\n\n\nvoid\nRoamingZoneSelect::setRoamingZone(RoamingZone *z) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<RoamingZone*>() == z) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nRoamingZone *\nRoamingZoneSelect::roamingZone() const {\n  return currentData().value<RoamingZone*>();\n}\n\n"
  },
  {
    "path": "src/roamingzonedialog.hh",
    "content": "#ifndef ROAMINGZONEDIALOG_HH\n#define ROAMINGZONEDIALOG_HH\n\n#include <QComboBox>\n#include <QDialog>\n#include \"roamingzone.hh\"\n#include \"ui_roamingzonedialog.h\"\n\nclass Config;\n\nclass RoamingZoneDialog : public QDialog, private Ui_RoamingZoneDialog\n{\n  Q_OBJECT\npublic:\n  explicit RoamingZoneDialog(Config *config, QWidget *parent = nullptr);\n  RoamingZoneDialog(Config *config, RoamingZone *zone, QWidget *parent=nullptr);\n\n  RoamingZone *zone();\n\nprotected slots:\n  void construct();\n\n  void onAddRoamingChannel();\n  void onAddDMRChannel();\n  void onRemChannel();\n\nprotected:\n  Config *_config;\n  RoamingZone *_myZone;\n  RoamingZone *_zone;\n};\n\n\nclass RoamingZoneSelect: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  RoamingZoneSelect(Config *config, QWidget *parent=nullptr);\n\n  void setRoamingZone(RoamingZone *z);\n  RoamingZone *roamingZone() const;\n};\n\n#endif // ROAMINGZONEDIALOG_HH\n"
  },
  {
    "path": "src/roamingzonedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RoamingZoneDialog</class>\n <widget class=\"QDialog\" name=\"RoamingZoneDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>485</width>\n    <height>413</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Dialog</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"basicTab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <item>\n          <widget class=\"QLabel\" name=\"zoneNameLabel\">\n           <property name=\"text\">\n            <string>Name:</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"zoneName\"/>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"ConfigObjectListView\" name=\"channelListView\" native=\"true\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <item>\n          <widget class=\"QPushButton\" name=\"addRoam\">\n           <property name=\"text\">\n            <string>Add Roaming Channel</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"addDMR\">\n           <property name=\"text\">\n            <string>Add DMR Channel</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt++</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"rem\">\n           <property name=\"text\">\n            <string>Remove Channel</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+-</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extension</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RoamingZoneDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RoamingZoneDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/roamingzonelistview.cc",
    "content": "#include \"roamingzonelistview.hh\"\n#include \"ui_roamingzonelistview.h\"\n\n#include \"settings.hh\"\n#include \"config.hh\"\n#include \"roamingzonedialog.hh\"\n#include \"contactselectiondialog.hh\"\n\n#include <QMessageBox>\n\n\nRoamingZoneListView::RoamingZoneListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::RoamingZoneListView), _config(config)\n{\n  Settings settings;\n  ui->setupUi(this);\n\n  ui->listView->setModel(new RoamingListWrapper(_config->roamingZones(), ui->listView));\n\n  connect(ui->addRoamingZone, SIGNAL(clicked()), this, SLOT(onAddRoamingZone()));\n  connect(ui->genRoamingZone, SIGNAL(clicked(bool)), this, SLOT(onGenRoamingZone()));\n  connect(ui->remRoamingZone, SIGNAL(clicked()), this, SLOT(onRemRoamingZone()));\n  connect(ui->listView, SIGNAL(doubleClicked(unsigned)),\n          this, SLOT(onEditRoamingZone(unsigned)));\n  connect(ui->roamingNote, SIGNAL(linkActivated(QString)), this, SLOT(onHideRoamingNote()));\n\n  if (settings.hideRoamingNote())\n    ui->roamingNote->setHidden(true);\n\n}\n\nRoamingZoneListView::~RoamingZoneListView() {\n  delete ui;\n}\n\nvoid\nRoamingZoneListView::onAddRoamingZone() {\n  RoamingZoneDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->listView->hasSelection())\n    row = ui->listView->selection().second+1;\n  _config->roamingZones()->add(dialog.zone(), row);\n}\n\nvoid\nRoamingZoneListView::onGenRoamingZone() {\n  MultiGroupCallSelectionDialog contSel(_config->contacts(), false);\n  contSel.setWindowTitle(tr(\"Generate roaming zone\"));\n  contSel.setLabel(tr(\"Create a roaming zone by collecting all channels with these group calls.\"));\n\n  if (QDialog::Accepted != contSel.exec())\n    return;\n\n  QList<DMRContact *> contacts = contSel.contacts();\n  RoamingZone *zone = new RoamingZone(\"Name\");\n  for (int i=0; i<_config->channelList()->count(); i++) {\n    DMRChannel *dch = _config->channelList()->channel(i)->as<DMRChannel>();\n    if (nullptr == dch)\n      continue;\n    if (contacts.contains(dch->contact())) {\n      RoamingChannel *rch = RoamingChannel::fromDMRChannel(dch);\n      _config->roamingChannels()->add(rch);\n      zone->addChannel(rch);\n    }\n  }\n\n  RoamingZoneDialog dialog(_config, zone);\n  if (QDialog::Accepted != dialog.exec()) {\n    zone->deleteLater();\n    return;\n  }\n\n  int row=-1;\n  if (ui->listView->hasSelection())\n    row = ui->listView->selection().second+1;\n  _config->roamingZones()->add(dialog.zone(), row);\n}\n\nvoid\nRoamingZoneListView::onRemRoamingZone() {\n  if (! ui->listView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete roaming zone\"),\n          tr(\"Cannot delete roaming zone: You have to select a zone first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int, int> rows = ui->listView->selection();\n  int rowcount = rows.second - rows.first + 1;\n  if (rows.first==rows.second) {\n    QString name = _config->roamingZones()->zone(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete roaming zone?\"), tr(\"Delete roaming zone %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete roaming zones?\"), tr(\"Delete %1 roaming zones?\").arg(rowcount)))\n      return;\n  }\n  // collect all selected zones\n  // need to collect them first as rows change when deleting\n  QList<RoamingZone *> lists; lists.reserve(rowcount);\n  for (int row=rows.first; row<=rows.second; row++)\n    lists.push_back(_config->roamingZones()->zone(row));\n  // remove\n  foreach (RoamingZone *zone, lists)\n    _config->roamingZones()->del(zone);\n}\n\nvoid\nRoamingZoneListView::onEditRoamingZone(unsigned row) {\n  RoamingZoneDialog dialog(_config, _config->roamingZones()->zone(row));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  dialog.zone();\n}\n\nvoid\nRoamingZoneListView::onHideRoamingNote() {\n  Settings setting; setting.setHideRoamingNote(true);\n  ui->roamingNote->setVisible(false);\n}\n\n"
  },
  {
    "path": "src/roamingzonelistview.hh",
    "content": "#ifndef ROAMINGZONELISTVIEW_HH\n#define ROAMINGZONELISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\n\nnamespace Ui {\n  class RoamingZoneListView;\n}\n\n\n\nclass RoamingZoneListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit RoamingZoneListView(Config *_config, QWidget *parent = nullptr);\n  ~RoamingZoneListView();\n\nprotected slots:\n  void onAddRoamingZone();\n  void onGenRoamingZone();\n  void onRemRoamingZone();\n  void onEditRoamingZone(unsigned row);\n  void onHideRoamingNote();\n\nprivate:\n  Ui::RoamingZoneListView *ui;\n  Config *_config;\n};\n\n#endif // ROAMINGZONELISTVIEW_HH\n"
  },
  {
    "path": "src/roamingzonelistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RoamingZoneListView</class>\n <widget class=\"QWidget\" name=\"RoamingZoneListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>497</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"roamingNote\">\n     <property name=\"styleSheet\">\n      <string notr=\"true\">padding:10px;border: 2px solid black; border-radius: 10px;</string>\n     </property>\n     <property name=\"text\">\n      <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n     </property>\n     <property name=\"textFormat\">\n      <enum>Qt::RichText</enum>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"ConfigObjectListView\" name=\"listView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_15\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addRoamingZone\">\n       <property name=\"text\">\n        <string>Add Roaming Zone</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt++</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"genRoamingZone\">\n       <property name=\"text\">\n        <string>Generate Roaming Zone</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remRoamingZone\">\n       <property name=\"text\">\n        <string>Delete Roaming Zone</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/rxgrouplistdialog.cc",
    "content": "#include \"rxgrouplistdialog.hh\"\n#include \"config.hh\"\n#include \"contact.hh\"\n#include \"contactselectiondialog.hh\"\n#include \"settings.hh\"\n\n#include <QInputDialog>\n#include <QMessageBox>\n\n\n/* ********************************************************************************************* *\n * Implementation of RXGroupListDialog\n * ********************************************************************************************* */\nRXGroupListDialog::RXGroupListDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myGroupList(new RXGroupList(this)), _list(nullptr)\n{\n  setWindowTitle(tr(\"Create Group List\"));\n  construct();\n}\n\nRXGroupListDialog::RXGroupListDialog(Config *config, RXGroupList *list, QWidget *parent)\n  : QDialog(parent), _config(config), _myGroupList(new RXGroupList(this)), _list(list)\n{\n  setWindowTitle(tr(\"Edit Group List\"));\n  if (_list)\n    _myGroupList->copy(*_list);\n\n  construct();\n}\n\nRXGroupList *\nRXGroupListDialog::groupList() {\n  _myGroupList->setName(groupListName->text().simplified());\n\n  RXGroupList *list = _myGroupList;\n  if (_list) {\n    _list->copy(*_myGroupList);\n    list = _myGroupList;\n  } else {\n    _myGroupList->setParent(nullptr);\n  }\n\n  // Return modified list.\n  return list;\n}\n\nvoid\nRXGroupListDialog::construct() {\n  setupUi(this);\n  Settings settings;\n\n  connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n  connect(addContact, SIGNAL(clicked()), this, SLOT(onAddGroup()));\n  connect(remContact, SIGNAL(clicked()), this, SLOT(onRemGroup()));\n\n  groupListName->setText(_myGroupList->name());\n  contactListView->setModel(new GroupListWrapper(_myGroupList, contactListView));\n\n  extensionView->setObjectName(\"groupListExtension\");\n  extensionView->setObject(_myGroupList, _config);\n}\n\n\nvoid\nRXGroupListDialog::onAddGroup() {\n  MultiGroupCallSelectionDialog dialog(_config->contacts(), false, _myGroupList->contacts());\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  QList<DMRContact *> contacts = dialog.contacts();\n  foreach (DMRContact *contact, contacts) {\n    if (_myGroupList->contacts()->has(contact))\n      continue;\n    _myGroupList->addContact(contact);\n  }\n}\n\nvoid\nRXGroupListDialog::onRemGroup() {\n  if (!contactListView->hasSelection()) {\n    QMessageBox::information(nullptr, tr(\"Cannot remove group call\"),\n                             tr(\"Cannot remove group call: You have to select at least one group call first.\"));\n    return;\n  }\n  QPair<int,int> selection = contactListView->selection();\n  // collect all selected group lists\n  // need to collect them first as rows change when deleting\n  QList<DMRContact *> lists;\n  for (int row=selection.first; row<=selection.second; row++)\n    lists.push_back(_myGroupList->contact(row));\n  // remove list\n  foreach (DMRContact *cont, lists)\n    _myGroupList->contacts()->del(cont);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of RXGroupListBox\n * ********************************************************************************************* */\nRXGroupListBox::RXGroupListBox(RXGroupLists *groups, QWidget *parent)\n  : QComboBox(parent)\n{\n  populateRXGroupListBox(this, groups);\n}\n\n\nvoid\nRXGroupListBox::setGroupList(RXGroupList *lst) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<RXGroupList*>() == lst) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nRXGroupList *\nRXGroupListBox::groupList() const {\n  return currentData().value<RXGroupList *>();\n}\n\n\nvoid\npopulateRXGroupListBox(QComboBox *box, RXGroupLists *groups, RXGroupList *list) {\n  box->addItem(QObject::tr(\"[None]\"), QVariant::fromValue(nullptr));\n  for (int i=0; i<groups->count(); i++) {\n    box->addItem(groups->list(i)->name(), QVariant::fromValue(groups->list(i)));\n    if (groups->list(i) == list)\n      box->setCurrentIndex(i+1);\n  }\n}\n"
  },
  {
    "path": "src/rxgrouplistdialog.hh",
    "content": "#ifndef RXGROUPLISTDIALOG_HH\n#define RXGROUPLISTDIALOG_HH\n\n#include <QListView>\n#include <QListWidget>\n#include <QDialog>\n#include <QComboBox>\n\n#include \"rxgrouplist.hh\"\n\n#include \"ui_rxgrouplistdialog.h\"\n\nclass Config;\n\n\nclass RXGroupListDialog: public QDialog, private Ui::RXGroupListDialog\n{\n\tQ_OBJECT\n\npublic:\n\texplicit RXGroupListDialog(Config *config, QWidget *parent=nullptr);\n\tRXGroupListDialog(Config *config, RXGroupList *list, QWidget *parent=nullptr);\n\n\tRXGroupList *groupList();\n\nprotected slots:\n\tvoid onAddGroup();\n\tvoid onRemGroup();\n\nprotected:\n\tvoid construct();\n\nprotected:\n\tConfig *_config;\n  RXGroupList *_myGroupList;\n\tRXGroupList *_list;\n};\n\n\nclass RXGroupListBox: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  RXGroupListBox(RXGroupLists *groups, QWidget *parent=0);\n\n  void setGroupList(RXGroupList *lst);\n  RXGroupList *groupList() const;\n};\n\nvoid populateRXGroupListBox(QComboBox *box, RXGroupLists *groups, RXGroupList *list=nullptr);\n\n\n#endif // RXGROUPLISTDIALOG_HH\n"
  },
  {
    "path": "src/rxgrouplistdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>RXGroupListDialog</class>\n <widget class=\"QDialog\" name=\"RXGroupListDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>425</width>\n    <height>357</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string/>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"tab_3\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n         <item>\n          <widget class=\"QLabel\" name=\"label_2\">\n           <property name=\"text\">\n            <string>Name</string>\n           </property>\n           <property name=\"buddy\">\n            <cstring>groupListName</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"groupListName\"/>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"ConfigObjectListView\" name=\"contactListView\" native=\"true\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_3\">\n         <item>\n          <widget class=\"QPushButton\" name=\"addContact\">\n           <property name=\"text\">\n            <string>Add Contact</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt++</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"remContact\">\n           <property name=\"text\">\n            <string>Remove Contact</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+-</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>RXGroupListDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>RXGroupListDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/satellitedatabasedialog.cc",
    "content": "#include \"satellitedatabasedialog.hh\"\n#include \"ui_satellitedatabasedialog.h\"\n#include \"satellitedatabase.hh\"\n#include \"satelliteselectiondialog.hh\"\n#include \"transponderfrequencydelegate.hh\"\n#include \"settings.hh\"\n#include \"selectivecallbox.hh\"\n#include \"satellitetransponderdialog.hh\"\n\n\n\nSatelliteDatabaseDialog::SatelliteDatabaseDialog(SatelliteDatabase *db, QWidget *parent) :\n  QDialog(parent), ui(new Ui::SatelliteDatabaseDialog), _database(db)\n{\n  ui->setupUi(this);\n  setWindowIcon(QIcon::fromTheme(\"edit-satellites\"));\n\n  connect(ui->addButton, &QPushButton::clicked,\n          this, &SatelliteDatabaseDialog::onAddSatellite);\n  connect(ui->delButton, &QPushButton::clicked,\n          this, &SatelliteDatabaseDialog::onDeleteSatellite);\n\n  ui->satellitesView->setModel(_database);\n  connect(ui->satellitesView, &QTableView::doubleClicked,\n          this, &SatelliteDatabaseDialog::onEditTransponder);\n\n  // FM downlink\n  //ui->satellitesView->setItemDelegateForColumn(2, new TransponderFrequencyDelegate(false, Transponder::Mode::FM));\n  // FM uplink\n  //ui->satellitesView->setItemDelegateForColumn(3, new TransponderFrequencyDelegate(true, Transponder::Mode::FM));\n  // FM downlink sub tone\n  //ui->satellitesView->setItemDelegateForColumn(4, new SelectiveCallDelegate());\n  // FM uplink sub tone\n  //ui->satellitesView->setItemDelegateForColumn(5, new SelectiveCallDelegate());\n  // APRS downlink\n  //ui->satellitesView->setItemDelegateForColumn(6, new TransponderFrequencyDelegate(false, Transponder::Mode::APRS));\n  // APRS uplink\n  //ui->satellitesView->setItemDelegateForColumn(7, new TransponderFrequencyDelegate(true, Transponder::Mode::APRS));\n  // APRS downlink sub tone\n  //ui->satellitesView->setItemDelegateForColumn(8, new SelectiveCallDelegate());\n  // APRS uplink sub tone\n  //ui->satellitesView->setItemDelegateForColumn(9, new SelectiveCallDelegate());\n  // Beacon\n  //ui->satellitesView->setItemDelegateForColumn(10, new TransponderFrequencyDelegate(false, Transponder::Mode::CW));\n\n  this->restoreGeometry(Settings().headerState(objectName()));\n  ui->satellitesView->horizontalHeader()->restoreState(\n        Settings().headerState(ui->satellitesView->objectName()));\n}\n\n\nSatelliteDatabaseDialog::~SatelliteDatabaseDialog()\n{\n  Settings settings;\n  settings.setHeaderState(this->objectName(), this->saveGeometry());\n  settings.setHeaderState(ui->satellitesView->objectName(),\n                          ui->satellitesView->horizontalHeader()->saveState());\n\n  delete ui;\n}\n\n\nvoid\nSatelliteDatabaseDialog::onEditTransponder(const QModelIndex &idx) {\n  if ((! idx.isValid()) || (idx.row() >= (int)_database->count()))\n    return;\n\n  SatelliteTransponderDialog dialog(_database->getAt(idx.row()), _database->transponders());\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  _database->setAt(dialog.satellite(), idx.row());\n}\n\n\nvoid\nSatelliteDatabaseDialog::onAddSatellite() {\n  SatelliteSelectionDialog dialog(&(_database->orbitalElements()));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  _database->add(_database->orbitalElements().getById(dialog.satellite()));\n}\n\n\nvoid\nSatelliteDatabaseDialog::onDeleteSatellite() {\n  QModelIndexList selected = ui->satellitesView->selectionModel()->selectedRows();\n  if (selected.isEmpty())\n    return;\n\n  _database->removeRow(selected.back().row());\n}\n\n\n"
  },
  {
    "path": "src/satellitedatabasedialog.hh",
    "content": "#ifndef SATELLITEDATABASEDIALOG_HH\n#define SATELLITEDATABASEDIALOG_HH\n\n#include <QDialog>\n\nnamespace Ui {\n  class SatelliteDatabaseDialog;\n}\n\nclass SatelliteDatabase;\n\n\nclass SatelliteDatabaseDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit SatelliteDatabaseDialog(SatelliteDatabase *db, QWidget *parent = nullptr);\n  ~SatelliteDatabaseDialog();\n\nprivate slots:\n  void onEditTransponder(const QModelIndex &idx);\n  void onAddSatellite();\n  void onDeleteSatellite();\n\nprivate:\n  Ui::SatelliteDatabaseDialog *ui;\n  SatelliteDatabase *_database;\n};\n\n#endif // SATELLITEDATABASEDIALOG_HH\n"
  },
  {
    "path": "src/satellitedatabasedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SatelliteDatabaseDialog</class>\n <widget class=\"QDialog\" name=\"SatelliteDatabaseDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit satellite database</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n     <item>\n      <widget class=\"QTableView\" name=\"satellitesView\">\n       <property name=\"editTriggers\">\n        <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>\n       </property>\n       <property name=\"selectionMode\">\n        <enum>QAbstractItemView::SelectionMode::SingleSelection</enum>\n       </property>\n       <property name=\"selectionBehavior\">\n        <enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>\n       </property>\n       <attribute name=\"verticalHeaderVisible\">\n        <bool>false</bool>\n       </attribute>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addButton\">\n       <property name=\"text\">\n        <string>Add</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"delButton\">\n       <property name=\"text\">\n        <string>Delete</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>SatelliteDatabaseDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>SatelliteDatabaseDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/satelliteselectiondialog.cc",
    "content": "#include \"satelliteselectiondialog.hh\"\n#include \"ui_satelliteselectiondialog.h\"\n#include \"orbitalelementsdatabase.hh\"\n#include \"logger.hh\"\n#include \"settings.hh\"\n#include \"searchpopup.hh\"\n\n\nSatelliteSelectionDialog::SatelliteSelectionDialog(OrbitalElementsDatabase *db, QWidget *parent)\n  : QDialog(parent), ui(new Ui::SatelliteSelectionDialog), _database(db)\n{\n  ui->setupUi(this);\n  setWindowIcon(QIcon::fromTheme(\"edit-satellites\"));\n  ui->satellites->setModel(_database);\n  ui->satellites->hideColumn(2);\n\n  SearchPopup::attach(ui->satellites);\n  this->restoreGeometry(Settings().headerState(objectName()));\n}\n\nSatelliteSelectionDialog::~SatelliteSelectionDialog() {\n  Settings().setHeaderState(this->objectName(), this->saveGeometry());\n  delete ui;\n}\n\n\nunsigned int\nSatelliteSelectionDialog::satellite() const {\n  QModelIndexList selection = ui->satellites->selectionModel()->selectedRows();\n  if (0 == selection.count())\n      return 0;\n  return _database->getAt(selection.back().row()).id();\n}\n"
  },
  {
    "path": "src/satelliteselectiondialog.hh",
    "content": "#ifndef SATELLITESELECTIONDIALOG_HH\n#define SATELLITESELECTIONDIALOG_HH\n\n#include <QDialog>\n\nnamespace Ui {\n  class SatelliteSelectionDialog;\n}\n\nclass OrbitalElementsDatabase;\n\n\nclass SatelliteSelectionDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit SatelliteSelectionDialog(OrbitalElementsDatabase *db, QWidget *parent = nullptr);\n  ~SatelliteSelectionDialog();\n\n  unsigned int satellite() const;\n\nprivate:\n  Ui::SatelliteSelectionDialog *ui;\n  OrbitalElementsDatabase *_database;\n};\n\n#endif // SATELLITESELECTIONDIALOG_HH\n"
  },
  {
    "path": "src/satelliteselectiondialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SatelliteSelectionDialog</class>\n <widget class=\"QDialog\" name=\"SatelliteSelectionDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Select a satellite</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"hintText\">\n     <property name=\"text\">\n      <string>Select a satellite</string>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTreeView\" name=\"satellites\">\n     <property name=\"selectionBehavior\">\n      <enum>QAbstractItemView::SelectRows</enum>\n     </property>\n     <property name=\"rootIsDecorated\">\n      <bool>false</bool>\n     </property>\n     <property name=\"itemsExpandable\">\n      <bool>false</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>SatelliteSelectionDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>SatelliteSelectionDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/satellitetransponderdialog.cc",
    "content": "#include \"satellitetransponderdialog.hh\"\n#include \"ui_satellitetransponderdialog.h\"\n\nSatelliteTransponderDialog::SatelliteTransponderDialog(const Satellite &sat, TransponderDatabase &transponder, QWidget *parent)\n  : QDialog(parent) , ui(new Ui::SatelliteTransponderDialog), _satellite(sat)\n{\n  ui->setupUi(this);\n  setWindowIcon(QIcon::fromTheme(\"edit-satellites\"));\n\n  ui->fmDownlinkFrequency->populate(_satellite.id(), false, Transponder::Mode::FM, transponder);\n  ui->fmDownlinkFrequency->setFrequency(_satellite.fmDownlink());\n  ui->fmDownlinkTone->setSelectiveCall(_satellite.fmDownlinkTone());\n  ui->fmUplinkFrequency->populate(_satellite.id(), true, Transponder::Mode::FM, transponder);\n  ui->fmUplinkFrequency->setFrequency(_satellite.fmUplink());\n  ui->fmUplinkTone->setSelectiveCall(_satellite.fmUplinkTone());\n\n  ui->aprsDownlinkFrequency->populate(_satellite.id(), false, Transponder::Mode::APRS, transponder);\n  ui->aprsDownlinkFrequency->setFrequency(_satellite.aprsDownlink());\n  ui->aprsDownlinkTone->setSelectiveCall(sat.aprsDownlinkTone());\n  ui->aprsUplinkFrequency->populate(_satellite.id(), true, Transponder::Mode::APRS, transponder);\n  ui->aprsUplinkFrequency->setFrequency(_satellite.aprsUplink());\n  ui->aprsUplinkTone->setSelectiveCall(_satellite.aprsUplinkTone());\n\n  ui->beaconFrequency->populate(_satellite.id(), false, Transponder::Mode::CW, transponder);\n  ui->beaconFrequency->setFrequency(_satellite.beacon());\n}\n\nSatelliteTransponderDialog::~SatelliteTransponderDialog() {\n  delete ui;\n}\n\n\nconst Satellite &\nSatelliteTransponderDialog::satellite() const {\n  return _satellite;\n}\n\nvoid\nSatelliteTransponderDialog::accept() {\n  _satellite.setFMUplink(ui->fmUplinkFrequency->frequency());\n  _satellite.setFMUplinkTone(ui->fmUplinkTone->selectiveCall());\n  _satellite.setFMDownlink(ui->fmDownlinkFrequency->frequency());\n  _satellite.setFMDownlinkTone(ui->fmDownlinkTone->selectiveCall());\n\n  _satellite.setAPRSUplink(ui->aprsUplinkFrequency->frequency());\n  _satellite.setAPRSUplinkTone(ui->aprsUplinkTone->selectiveCall());\n  _satellite.setAPRSDownlink(ui->aprsDownlinkFrequency->frequency());\n  _satellite.setAPRSDownlinkTone(ui->aprsDownlinkTone->selectiveCall());\n\n  _satellite.setBeacon(ui->beaconFrequency->frequency());\n\n  QDialog::accept();\n}\n\n"
  },
  {
    "path": "src/satellitetransponderdialog.hh",
    "content": "#ifndef SATELLITETRANSPONDERDIALOG_HH\n#define SATELLITETRANSPONDERDIALOG_HH\n\n#include <QDialog>\n#include \"satellitedatabase.hh\"\n\n\nnamespace Ui {\nclass SatelliteTransponderDialog;\n}\n\nclass SatelliteTransponderDialog : public QDialog\n{\n  Q_OBJECT\n\npublic:\n  SatelliteTransponderDialog(const Satellite &sat, TransponderDatabase &transponder, QWidget *parent = nullptr);\n  ~SatelliteTransponderDialog();\n\n  const Satellite &satellite() const;\n\npublic slots:\n  void accept() override;\n\nprivate:\n  Ui::SatelliteTransponderDialog *ui;\n  Satellite _satellite;\n};\n\n#endif // SATELLITETRANSPONDERDIALOG_HH\n"
  },
  {
    "path": "src/satellitetransponderdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SatelliteTransponderDialog</class>\n <widget class=\"QDialog\" name=\"SatelliteTransponderDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>496</width>\n    <height>495</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Satellite Transponder</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QGroupBox\" name=\"fmBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"title\">\n      <string>FM Voice Transponder</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout\">\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QLabel\" name=\"fmUplinkFrequencyLabel\">\n        <property name=\"text\">\n         <string>Uplink Frequency</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>fmUplinkFrequency</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QLabel\" name=\"fmUplinkToneLabel\">\n        <property name=\"text\">\n         <string>Uplink Tone</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>fmUplinkTone</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"TransponderFrequencyEditor\" name=\"fmUplinkFrequency\"/>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"SelectiveCallBox\" name=\"fmUplinkTone\" native=\"true\"/>\n      </item>\n      <item row=\"2\" column=\"0\">\n       <widget class=\"QLabel\" name=\"fmDownlinkFrequencyLabel\">\n        <property name=\"text\">\n         <string>Downlink Frequency</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>fmDownlinkFrequency</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"1\">\n       <widget class=\"QLabel\" name=\"fmDownlinkToneLabel\">\n        <property name=\"text\">\n         <string>Downlink Tone</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>fmDownlinkTone</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"0\">\n       <widget class=\"TransponderFrequencyEditor\" name=\"fmDownlinkFrequency\"/>\n      </item>\n      <item row=\"3\" column=\"1\">\n       <widget class=\"SelectiveCallBox\" name=\"fmDownlinkTone\" native=\"true\"/>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"aprsBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"title\">\n      <string>APRS Transponder</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout_3\">\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QLabel\" name=\"aprsUplinkFrequencyLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Uplink Frequency</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>aprsUplinkFrequency</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"0\" column=\"1\">\n       <widget class=\"QLabel\" name=\"aprsUplinkToneLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Uplink Tone</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>aprsUplinkTone</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"TransponderFrequencyEditor\" name=\"aprsUplinkFrequency\"/>\n      </item>\n      <item row=\"1\" column=\"1\">\n       <widget class=\"SelectiveCallBox\" name=\"aprsUplinkTone\" native=\"true\"/>\n      </item>\n      <item row=\"2\" column=\"0\">\n       <widget class=\"QLabel\" name=\"aprsDownlinkFrequencyLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Downlink Frequency</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>aprsDownlinkFrequency</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"2\" column=\"1\">\n       <widget class=\"QLabel\" name=\"aprsDownlinkToneLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Downlink Tone</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>aprsDownlinkTone</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"3\" column=\"0\">\n       <widget class=\"TransponderFrequencyEditor\" name=\"aprsDownlinkFrequency\"/>\n      </item>\n      <item row=\"3\" column=\"1\">\n       <widget class=\"SelectiveCallBox\" name=\"aprsDownlinkTone\" native=\"true\"/>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QGroupBox\" name=\"beaconBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"title\">\n      <string>Beacon</string>\n     </property>\n     <layout class=\"QGridLayout\" name=\"gridLayout_2\">\n      <item row=\"0\" column=\"0\">\n       <widget class=\"QLabel\" name=\"beaconFrequencyLabel\">\n        <property name=\"sizePolicy\">\n         <sizepolicy hsizetype=\"Preferred\" vsizetype=\"Minimum\">\n          <horstretch>0</horstretch>\n          <verstretch>0</verstretch>\n         </sizepolicy>\n        </property>\n        <property name=\"text\">\n         <string>Beacon Frequency</string>\n        </property>\n        <property name=\"buddy\">\n         <cstring>beaconFrequency</cstring>\n        </property>\n       </widget>\n      </item>\n      <item row=\"1\" column=\"0\">\n       <widget class=\"TransponderFrequencyEditor\" name=\"beaconFrequency\"/>\n      </item>\n     </layout>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>SelectiveCallBox</class>\n   <extends>QWidget</extends>\n   <header>selectivecallbox.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>TransponderFrequencyEditor</class>\n   <extends>QComboBox</extends>\n   <header>transponderfrequencydelegate.hh</header>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>SatelliteTransponderDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>257</x>\n     <y>631</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>SatelliteTransponderDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>325</x>\n     <y>631</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/scanlistdialog.cc",
    "content": "#include \"scanlistdialog.hh\"\n#include \"config.hh\"\n#include \"channelselectiondialog.hh\"\n#include \"settings.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of ScanListDialog\n * ********************************************************************************************* */\nScanListDialog::ScanListDialog(Config *config, ScanList *scanlist, QWidget *parent)\n  : QDialog(parent), _config(config), _myScanList(new ScanList(this)), _scanlist(scanlist)\n{\n  setWindowTitle(tr(\"Edit Scan List\"));\n\n  if (_scanlist)\n    _myScanList->copy(*_scanlist);\n\n  construct();\n}\n\nScanListDialog::ScanListDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myScanList(new ScanList(this)), _scanlist(nullptr)\n{\n  setWindowTitle(tr(\"Create Scan List\"));\n\n  construct();\n}\n\nvoid\nScanListDialog::construct() {\n  setupUi(this);\n  Settings settings;\n\n  priorityChannel1->addItem(tr(\"[None]\"), QVariant::fromValue(nullptr));\n  priorityChannel1->addItem(tr(\"[Selected]\"), QVariant::fromValue(SelectedChannel::get()));\n  priorityChannel2->addItem(tr(\"[None]\"), QVariant::fromValue(nullptr));\n  priorityChannel2->addItem(tr(\"[Selected]\"), QVariant::fromValue(SelectedChannel::get()));\n  transmitChannel->addItem(tr(\"[Last]\"), QVariant::fromValue(nullptr));\n  transmitChannel->addItem(tr(\"[Selected]\"), QVariant::fromValue(SelectedChannel::get()));\n\n  for (int i=0; i<_config->channelList()->count(); i++) {\n    Channel *channel = _config->channelList()->channel(i);\n    priorityChannel1->addItem(channel->name(), QVariant::fromValue(channel));\n    priorityChannel2->addItem(channel->name(), QVariant::fromValue(channel));\n    transmitChannel->addItem(channel->name(), QVariant::fromValue(channel));\n  }\n\n  scanListName->setText(_myScanList->name());\n  // set priority channel\n  if (_myScanList->primaryChannel())\n    priorityChannel1->setCurrentIndex(_config->channelList()->indexOf(_myScanList->primaryChannel())+2);\n  // set secondary priority channel\n  if (_myScanList->secondaryChannel())\n    priorityChannel2->setCurrentIndex(_config->channelList()->indexOf(_myScanList->secondaryChannel())+2);\n  if (_myScanList->revertChannel())\n    transmitChannel->setCurrentIndex(_config->channelList()->indexOf(_myScanList->revertChannel())+2);\n\n  channelListView->setModel(new ChannelRefListWrapper(_myScanList->channels(), channelListView));\n\n  extensionView->setObjectName(\"scanListExtension\");\n  extensionView->setObject(_myScanList, _config);\n\n  connect(addChannel, SIGNAL(clicked()), this, SLOT(onAddChannel()));\n  connect(remChannel, SIGNAL(clicked()), this, SLOT(onRemChannel()));\n  connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n}\n\nvoid\nScanListDialog::onAddChannel() {\n  MultiChannelSelectionDialog dia(_config->channelList(), true);\n  if (QDialog::Accepted != dia.exec())\n    return;\n\n  QList<Channel *> channels = dia.channel();\n  foreach (Channel *channel, channels) {\n    if (0 <= _myScanList->channels()->indexOf(channel))\n      continue;\n    _myScanList->channels()->add(channel);\n  }\n}\n\nvoid\nScanListDialog::onRemChannel() {\n  if (! channelListView->hasSelection())\n    return;\n\n  QPair<int,int> selection = channelListView->selection();\n  QList<Channel *> channels;\n  for (int i=selection.first; i<=selection.second; i++)\n    channels.push_back(_myScanList->channels()->get(i)->as<Channel>());\n  foreach (Channel *channel, channels)\n    _myScanList->channels()->del(channel);\n}\n\n\nScanList *\nScanListDialog::scanlist() {\n  _myScanList->setName(scanListName->text().simplified());\n  // Set priority and transmit channels\n  _myScanList->setPrimaryChannel(priorityChannel1->currentData(Qt::UserRole).value<Channel *>());\n  _myScanList->setSecondaryChannel(priorityChannel2->currentData(Qt::UserRole).value<Channel *>());\n  _myScanList->setRevertChannel(transmitChannel->currentData(Qt::UserRole).value<Channel *>());\n\n  ScanList *scanlist = _myScanList;\n  if (_scanlist) {\n    _scanlist->copy(*_myScanList);\n    scanlist = _scanlist;\n  } else {\n    _myScanList->setParent(nullptr);\n  }\n\n  return scanlist;\n}\n"
  },
  {
    "path": "src/scanlistdialog.hh",
    "content": "#ifndef SCANLISTDIALOG_HH\n#define SCANLISTDIALOG_HH\n\n#include <QDialog>\n\n#include \"ui_scanlistdialog.h\"\n\nclass Config;\nclass ScanList;\n\n\nclass ScanListDialog: public QDialog, private Ui::ScanListDialog\n{\n\tQ_OBJECT\n\npublic:\n\tScanListDialog(Config *config, QWidget *parent=nullptr);\n\tScanListDialog(Config *config, ScanList *list, QWidget *parent=nullptr);\n\n\tScanList *scanlist();\n\nprotected slots:\n\tvoid onAddChannel();\n\tvoid onRemChannel();\n\nprotected:\n\tvoid construct();\n\nprotected:\n\tConfig *_config;\n  ScanList *_myScanList;\n  ScanList *_scanlist;\n};\n\n\n#endif // SCANLISTDIALOG_HH\n"
  },
  {
    "path": "src/scanlistdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ScanListDialog</class>\n <widget class=\"QDialog\" name=\"ScanListDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>437</width>\n    <height>536</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Scan List</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"tab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <layout class=\"QFormLayout\" name=\"formLayout\">\n         <item row=\"0\" column=\"0\">\n          <widget class=\"QLabel\" name=\"nameLabel\">\n           <property name=\"text\">\n            <string>Name</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"0\" column=\"1\">\n          <widget class=\"QLineEdit\" name=\"scanListName\"/>\n         </item>\n         <item row=\"1\" column=\"0\">\n          <widget class=\"QLabel\" name=\"priorityChannel50Label\">\n           <property name=\"text\">\n            <string>Primary Channel (50%)</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"1\" column=\"1\">\n          <widget class=\"QComboBox\" name=\"priorityChannel1\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"0\">\n          <widget class=\"QLabel\" name=\"priorityChannel25Label\">\n           <property name=\"text\">\n            <string>Secondary Channel (25%)</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"2\" column=\"1\">\n          <widget class=\"QComboBox\" name=\"priorityChannel2\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"0\">\n          <widget class=\"QLabel\" name=\"transmitChannelLabel\">\n           <property name=\"text\">\n            <string>Transmit Channel</string>\n           </property>\n          </widget>\n         </item>\n         <item row=\"3\" column=\"1\">\n          <widget class=\"QComboBox\" name=\"transmitChannel\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <widget class=\"ConfigObjectListView\" name=\"channelListView\" native=\"true\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QPushButton\" name=\"addChannel\">\n           <property name=\"text\">\n            <string>Add Channel</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt++</string>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QPushButton\" name=\"remChannel\">\n           <property name=\"text\">\n            <string>Remove Channel</string>\n           </property>\n           <property name=\"shortcut\">\n            <string>Alt+-</string>\n           </property>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extensions</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ScanListDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ScanListDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/scanlistsview.cc",
    "content": "#include \"scanlistsview.hh\"\n#include \"ui_scanlistsview.h\"\n\n#include \"scanlistdialog.hh\"\n\n#include <QMessageBox>\n\n\n\nScanListsView::ScanListsView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::ScanListsView), _config(config)\n{\n  ui->setupUi(this);\n\n  ui->listView->setModel(new ScanListsWrapper(_config->scanlists(), ui->listView));\n  connect(ui->addScanList, SIGNAL(clicked()), this, SLOT(onAddScanList()));\n  connect(ui->remScanList, SIGNAL(clicked()), this, SLOT(onRemScanList()));\n  connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditScanList(unsigned)));\n}\n\nScanListsView::~ScanListsView() {\n  delete ui;\n}\n\nvoid\nScanListsView::onAddScanList() {\n  ScanListDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->listView->hasSelection())\n    row = ui->listView->selection().second+1;\n  _config->scanlists()->add(dialog.scanlist(), row);\n}\n\nvoid\nScanListsView::onRemScanList() {\n  if (! ui->listView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete scanlist\"),\n          tr(\"Cannot delete scanlist: You have to select a scanlist first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->listView->selection();\n  int rowcount = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->scanlists()->scanlist(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete scan list?\"), tr(\"Delete scan list %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete scan lists?\"), tr(\"Delete %1 scan lists?\").arg(rowcount)))\n      return;\n  }\n\n  // collect all selected scan lists\n  // need to collect them first as rows change when deleting\n  QList<ScanList *> lists; lists.reserve(rowcount);\n  for (int row=rows.first; row<=rows.second; row++)\n    lists.push_back(_config->scanlists()->scanlist(row));\n  // remove\n  foreach (ScanList *list, lists)\n    _config->scanlists()->del(list);\n}\n\nvoid\nScanListsView::onEditScanList(unsigned row) {\n  ScanListDialog dialog(_config, _config->scanlists()->scanlist(row));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  dialog.scanlist();\n}\n\n\n"
  },
  {
    "path": "src/scanlistsview.hh",
    "content": "#ifndef SCANLISTSVIEW_HH\n#define SCANLISTSVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nnamespace Ui {\n  class ScanListsView;\n}\n\nclass ScanListsView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ScanListsView(Config *config, QWidget *parent = nullptr);\n  ~ScanListsView();\n\nprotected slots:\n  void onAddScanList();\n  void onRemScanList();\n  void onEditScanList(unsigned row);\n\nprivate:\n  Ui::ScanListsView *ui;\n  Config *_config;\n};\n\n#endif // SCANLISTSVIEW_HH\n"
  },
  {
    "path": "src/scanlistsview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ScanListsView</class>\n <widget class=\"QWidget\" name=\"ScanListsView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"ConfigObjectListView\" name=\"listView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_10\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addScanList\">\n       <property name=\"toolTip\">\n        <string/>\n       </property>\n       <property name=\"text\">\n        <string>Add Scan List</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt++</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remScanList\">\n       <property name=\"text\">\n        <string>Delete Scan List</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/searchpopup.cc",
    "content": "#include \"searchpopup.hh\"\n#include <QAction>\n#include <QHBoxLayout>\n#include <QLineEdit>\n#include <QToolButton>\n#include <QLabel>\n#include \"logger.hh\"\n\n\nSearchPopup::SearchPopup(QAbstractItemView *parent)\n  : QFrame(parent)\n{\n  setWindowFlags(Qt::Popup | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);\n  setFrameStyle(QFrame::Panel);\n  setWindowModality(Qt::ApplicationModal);\n\n  QAction *search = new QAction(this);\n  search->setShortcut(QKeySequence(tr(\"Ctrl+F\")));\n  parent->addAction(search);\n  connect(search, SIGNAL(triggered(bool)), this, SLOT(showPopup()));\n\n  _search = new QLineEdit();\n  connect(_search, SIGNAL(textChanged(QString)), this, SLOT(onSearchChanged(QString)));\n\n  QAction *hide = new QAction(_search);\n  hide->setShortcut(QKeySequence(Qt::Key_Escape));\n  _search->addAction(hide, QLineEdit::TrailingPosition);\n  connect(hide, SIGNAL(triggered(bool)), this, SLOT(hide()));\n\n  _label = new QLabel();\n  QToolButton *up = new QToolButton(this);\n  up->setIcon(QIcon::fromTheme(\"edit-move-up\"));\n  connect(up, SIGNAL(clicked(bool)), this, SLOT(onPrevious()));\n  QToolButton *down = new QToolButton(this);\n  down->setIcon(QIcon::fromTheme(\"edit-move-down\"));\n  connect(down, SIGNAL(clicked(bool)), this, SLOT(onNext()));\n  QToolButton *close = new QToolButton(this);\n  close->setIcon(QIcon::fromTheme(\"application-exit\"));\n  connect(close, SIGNAL(clicked(bool)), this, SLOT(hide()));\n\n  QHBoxLayout *layout = new QHBoxLayout();\n  layout->addWidget(_search);\n  layout->addWidget(_label);\n  layout->addWidget(up);\n  layout->addWidget(down);\n  layout->addWidget(close);\n  setLayout(layout);\n\n  move(parent->mapToGlobal(QPoint(parent->width()-width()-5, parent->height()-height()+5)));\n  this->hide();\n}\n\nvoid\nSearchPopup::showPopup() {\n  show();\n  auto itemView = qobject_cast<QAbstractItemView *>(parent());\n  move(itemView->mapToGlobal(QPoint(itemView->width()-width()-5, itemView->height()-height()+5)));\n  _search->clear();\n  _search->setFocus();\n}\n\nvoid\nSearchPopup::onSearchChanged(const QString &text) {\n  QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(parent());\n  QAbstractItemModel *model = itemView->model();\n\n  if (text.isEmpty()) {\n    _currentMatch = 0;\n    _matches.clear();\n    itemView->selectionModel()->clear();\n    _label->setText(\"\");\n    return;\n  }\n\n  _currentMatch = 0;\n  itemView->selectionModel()->clear();\n  _matches.clear();\n  for (int i=0; i<model->columnCount(); i++)\n    _matches.append(model->match(model->index(0,i), Qt::DisplayRole, text, -1,\n                                 Qt::MatchContains|Qt::MatchWrap));\n  std::sort(\n        _matches.begin(), _matches.end(),\n        [](const QModelIndex &a, const QModelIndex &b) {\n    if (a.row() < b.row())\n      return true;\n    if (a.row() > b.row())\n      return false;\n    return a.column() < b.column();\n  });\n\n  if (_matches.count()) {\n    _label->setText(tr(\"%1/%2\").arg(_currentMatch+1).arg(_matches.count()));\n    itemView->setCurrentIndex(_matches.at(_currentMatch));\n  } else {\n    _label->setText(\"\");\n  }\n}\n\nvoid\nSearchPopup::onNext() {\n  if (0 == _matches.count())\n    return;\n\n  QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(parent());\n  if ((++_currentMatch) >= _matches.count())\n    _currentMatch = 0;\n  _label->setText(tr(\"%1/%2\").arg(_currentMatch+1).arg(_matches.count()));\n  itemView->setCurrentIndex(_matches.at(_currentMatch));\n}\n\nvoid\nSearchPopup::onPrevious() {\n  if (0 == _matches.count())\n    return;\n\n  QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(parent());\n  if (0 == _currentMatch)\n    _currentMatch = _matches.count()-1;\n  _label->setText(QString(\"%1/%2\").arg(_currentMatch+1).arg(_matches.count()));\n  itemView->setCurrentIndex(_matches.at(_currentMatch));\n}\n\n\nvoid\nSearchPopup::attach(QAbstractItemView *itemview) {\n  if (nullptr == itemview)\n    return;\n\n  new SearchPopup(itemview);\n}\n"
  },
  {
    "path": "src/searchpopup.hh",
    "content": "#ifndef SEARCHPOPUP_HH\n#define SEARCHPOPUP_HH\n\n#include <QFrame>\n#include <QAbstractItemView>\n\nclass QLabel;\n\n\nclass SearchPopup : public QFrame\n{\n  Q_OBJECT\n\nprotected:\n  explicit SearchPopup(QAbstractItemView *parent);\n\npublic:\n  static void attach(QAbstractItemView *itemview);\n\npublic slots:\n  void showPopup();\n\nprotected slots:\n  void onSearchChanged(const QString &text);\n  void onNext();\n  void onPrevious();\n\nprotected:\n  QLineEdit *_search;\n  QLabel *_label;\n  int _currentMatch;\n  QModelIndexList _matches;\n};\n\n#endif // SEARCHPOPUP_HH\n"
  },
  {
    "path": "src/selectivecallbox.cc",
    "content": "#include \"selectivecallbox.hh\"\n#include <QComboBox>\n#include <QLineEdit>\n#include <QHBoxLayout>\n#include <QLabel>\n\n\n/* ********************************************************************************************* *\n * SelectiveCallBox\n * ********************************************************************************************* */\nSelectiveCallBox::SelectiveCallBox(QWidget *parent)\n  : QWidget{parent}, _typeSelection(nullptr), _stack(nullptr), _ctcss(nullptr), _dcs(nullptr),\n    _inverted(nullptr)\n{\n  _typeSelection = new QComboBox();\n  _typeSelection->addItem(tr(\"None\"));\n  _typeSelection->addItem(tr(\"CTCSS\"));\n  _typeSelection->addItem(tr(\"DCS\"));\n\n  _stack = new QStackedWidget();\n  _stack->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  _stack->setContentsMargins(0,0,0,0);\n  auto emptyLayout = new QHBoxLayout();\n  emptyLayout->setContentsMargins(0,0,0,0);\n  auto emptyLabel = new QLabel(\"None\");\n  emptyLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  emptyLayout->addWidget(emptyLabel);\n  auto emptyWidget = new QWidget();\n  emptyWidget->setLayout(emptyLayout);\n  emptyWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  emptyWidget->setContentsMargins(0,0,0,0);\n  _stack->addWidget(emptyWidget);\n\n  // CTCSS settings\n  _ctcss = new QComboBox();\n  _ctcss->setEditable(true);\n  auto ctcssLayout = new QHBoxLayout();\n  ctcssLayout->setContentsMargins(0,0,0,0);\n  ctcssLayout->addWidget(_ctcss);\n  ctcssLayout->addWidget(new QLabel(tr(\"Hz\")));\n  auto ctcssWidget = new QWidget();\n  ctcssWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  ctcssWidget->setContentsMargins(0,0,0,0);\n  ctcssWidget->setLayout(ctcssLayout);\n  _stack->addWidget(ctcssWidget);\n\n  // DCS settings\n  _dcs = new QComboBox();\n  _dcs->setEditable(true);\n  _inverted = new QCheckBox(tr(\"Inverted\"));\n  auto dscLayout = new QHBoxLayout();\n  dscLayout->setContentsMargins(0,0,0,0);\n  dscLayout->addWidget(_dcs);\n  dscLayout->addWidget(_inverted);\n  auto dcsWidget = new QWidget();\n  dcsWidget->setLayout(dscLayout);\n  dcsWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  dcsWidget->setContentsMargins(0,0,0,0);\n  _stack->addWidget(dcsWidget);\n\n  connect(_typeSelection, &QComboBox::currentTextChanged, [=]() {\n    _stack->setCurrentIndex(_typeSelection->currentIndex());\n  });\n\n  foreach (const SelectiveCall &call, SelectiveCall::standard()) {\n    if (call.isInvalid())\n      continue;\n    if (call.isCTCSS())\n      _ctcss->addItem(QString(\"%1.%2\").arg(call.mHz()/1000).arg((call.mHz()/100)%10),\n                      QVariant::fromValue(call));\n    else if (call.isDCS() && (! call.isInverted()))\n      _dcs->addItem(QString(\"%1\").arg(call.binCode(), 3, 8, QChar('0')),\n                    QVariant(call.binCode()));\n  }\n\n  auto layout = new QHBoxLayout();\n  layout->addWidget(_typeSelection,0);\n  layout->addWidget(_stack, 1);\n  layout->setContentsMargins(0,0,0,0);\n  setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);\n  setContentsMargins(0,0,0,0);\n  setLayout(layout);\n}\n\n\nvoid\nSelectiveCallBox::setSelectiveCall(const SelectiveCall &call) {\n  if (call.isInvalid()) {\n    _typeSelection->setCurrentIndex(0);\n  } else if (call.isCTCSS()) {\n    _typeSelection->setCurrentIndex(1);\n    int idx = _ctcss->findData(QVariant::fromValue(call));\n    if (idx >= 0)\n      _ctcss->setCurrentIndex(idx);\n    else\n      _ctcss->setEditText(call.format());\n  } else if (call.isDCS()) {\n    _typeSelection->setCurrentIndex(2);\n    int idx = _dcs->findData(QVariant(call.binCode()));\n    if (idx >= 0)\n      _dcs->setCurrentIndex(idx);\n    else\n      _dcs->setEditText(QString(\"%1\").arg(call.binCode(), 3, 8, QChar('0')));\n    _inverted->setChecked(call.isInverted());\n  }\n}\n\nSelectiveCall\nSelectiveCallBox::selectiveCall() const {\n  if (0 == _typeSelection->currentIndex()) {\n    return SelectiveCall();\n  } else if (1 == _typeSelection->currentIndex()) {\n    int idx = _ctcss->currentIndex();\n    if (0 >= idx)\n      return _ctcss->currentData().value<SelectiveCall>();\n    return SelectiveCall::parseCTCSS(_ctcss->currentText());\n  } else if (2 == _typeSelection->currentIndex()) {\n    int idx = _dcs->currentIndex();\n    if (0 >= idx)\n      return SelectiveCall(_dcs->currentData().toUInt(), _inverted->isChecked());\n    return SelectiveCall(_dcs->currentText().toUInt(), _inverted->isChecked());\n  }\n\n  return SelectiveCall();\n}\n\n\n/* ********************************************************************************************* *\n * SelectiveCallDelegate\n * ********************************************************************************************* */\nSelectiveCallDelegate::SelectiveCallDelegate(QObject *parent)\n  : QStyledItemDelegate(parent)\n{\n  // pass...\n}\n\n\nQWidget *\nSelectiveCallDelegate::createEditor(\n    QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n  Q_UNUSED(option); Q_UNUSED(index);\n  auto seditor = new SelectiveCallBox(parent);\n  seditor->setSelectiveCall(index.data(Qt::EditRole).value<SelectiveCall>());\n  return seditor;\n}\n\n\nvoid\nSelectiveCallDelegate::setEditorData(QWidget *editor, const QModelIndex index) {\n  auto seditor = qobject_cast<SelectiveCallBox*>(editor);\n  seditor->setSelectiveCall(index.data(Qt::EditRole).value<SelectiveCall>());\n}\n\n\nvoid\nSelectiveCallDelegate::setModelData(\n    QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const\n{\n  auto seditor = qobject_cast<SelectiveCallBox*>(editor);\n  model->setData(index, QVariant::fromValue(seditor->selectiveCall()));\n}\n\n"
  },
  {
    "path": "src/selectivecallbox.hh",
    "content": "#ifndef SELECTIVECALLBOX_H\n#define SELECTIVECALLBOX_H\n\n#include <QWidget>\n#include \"signaling.hh\"\n#include <QComboBox>\n#include <QCheckBox>\n#include <QStackedWidget>\n#include <QStyledItemDelegate>\n\n\nclass SelectiveCallBox : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit SelectiveCallBox(QWidget *parent = nullptr);\n\n  void setSelectiveCall(const SelectiveCall &call);\n  SelectiveCall selectiveCall() const;\n\nsignals:\n  void selected(const SelectiveCall &call);\n\nprivate:\n  QComboBox *_typeSelection;\n  QStackedWidget *_stack;\n  QComboBox *_ctcss;\n  QComboBox *_dcs;\n  QCheckBox *_inverted;\n};\n\n\nclass SelectiveCallDelegate: public QStyledItemDelegate\n{\n  Q_OBJECT\n\npublic:\n  explicit SelectiveCallDelegate(QObject *parent=nullptr);\n\n  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;\n  void setEditorData(QWidget *editor, const QModelIndex index);\n  void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;\n};\n\n\n#endif // SELECTIVECALLBOX_H\n"
  },
  {
    "path": "src/settings.cc",
    "content": "#include \"settings.hh\"\n#include \"logger.hh\"\n#include \"config.h\"\n#include \"utils.hh\"\n#include <QStandardPaths>\n#include <QDir>\n\n\n/* ********************************************************************************************* *\n * Implementation of SettingsDialog\n * ********************************************************************************************* */\nSettings::Settings(QObject *parent)\n  : QSettings(parent)\n{\n  // pass...\n}\n\nQDateTime\nSettings::lastRepeaterUpdate() const {\n  if (! contains(\"lastRepeaterUpdate\"))\n    return QDateTime();\n  return value(\"lastRepeaterUpdate\").toDateTime();\n}\n\nvoid\nSettings::repeaterUpdated() {\n  setValue(\"lastRepeaterUpdate\", QDateTime::currentDateTime());\n}\n\nbool\nSettings::repeaterUpdateNeeded(unsigned period) const {\n  QDateTime last = lastRepeaterUpdate();\n  if (! last.isValid())\n    return true;\n  QDateTime now = QDateTime::currentDateTime();\n  return last.daysTo(now) >= period;\n}\n\n\nbool\nSettings::queryPosition() const {\n  return value(\"queryPosition\", false).toBool();\n}\n\nvoid\nSettings::setQueryPosition(bool enable) {\n  setValue(\"queryPosition\", enable);\n}\n\nQString\nSettings::locator() const {\n  return value(\"locator\",\"\").toString();\n}\n\nvoid\nSettings::setLocator(const QString &locator) {\n  setValue(\"locator\", locator);\n}\n\nQGeoCoordinate\nSettings::position() const {\n  return loc2deg(locator());\n}\n\nint\nSettings::repeaterSearchRadius() const {\n  return value(\"repeaterSource/repeaterSearchRadius\", 50).toInt();\n}\nvoid\nSettings::setRepeaterSearchRadius(int radius) {\n  radius = std::min(150, std::max(20, radius));\n  setValue(\"repeaterSource/repeaterSearchRadius\", radius);\n}\n\n\nbool\nSettings::repeaterBookSourceEnabled() const {\n  return value(\"repeaterSource/repeaterBook\", true).toBool();\n}\nvoid\nSettings::enableRepeaterBookSource(bool enabled) {\n  return setValue(\"repeaterSource/repeaterBook\", enabled);\n}\n\nbool\nSettings::repeaterMapSourceEnabled() const {\n  return value(\"repeaterSource/repeaterMap\", true).toBool();\n}\nvoid\nSettings::enableRepeaterMapSource(bool enabled) {\n  return setValue(\"repeaterSource/repeaterMap\", enabled);\n}\n\nQByteArray\nSettings::repeaterMapAPIToken() const {\n  return value(\"repeaterSource/repeaterMapAPIToken\", QByteArray()).toByteArray();\n}\nvoid\nSettings::setRepeaterMapAPIToken(const QByteArray &token) {\n  setValue(\"repeaterSource/repeaterMapAPIToken\", token);\n}\n\nbool\nSettings::hearhamSourceEnabled() const {\n  return value(\"repeaterSource/hearham\", false).toBool();\n}\nvoid\nSettings::enableHearhamSource(bool enabled) {\n  return setValue(\"repeaterSource/hearham\", enabled);\n}\n\nbool\nSettings::radioIdRepeaterSourceEnabled() const {\n  return value(\"repeaterSource/radioId\", true).toBool();\n}\nvoid\nSettings::enableRadioIdRepeaterSource(bool enabled) {\n  return setValue(\"repeaterSource/radioId\", enabled);\n}\n\n\nRepeaterBookSource::Region\nSettings::repeaterBookRegion() const {\n  return value(\"repeaterBookRegion\", RepeaterBookSource::Region::World).value<RepeaterBookSource::Region>();\n}\nvoid\nSettings::setRepeaterBookRegion(RepeaterBookSource::Region region) {\n  setValue(\"repeaterBookRegion\", region);\n}\n\nbool\nSettings::disableAutoDetect() const {\n  return value(\"disableAutoDetect\", false).toBool();\n}\nvoid\nSettings::setDisableAutoDetect(bool disable) {\n  setValue(\"disableAutoDetect\", disable);\n}\n\nbool\nSettings::updateCodeplug() const {\n  return value(\"updateCodeplug\", true).toBool();\n}\nvoid\nSettings::setUpdateCodeplug(bool update) {\n  setValue(\"updateCodeplug\", update);\n}\n\nbool\nSettings::updateDeviceClock() const {\n  return value(\"updateDeviceClock\", false).toBool();\n}\nvoid\nSettings::setUpdateDeviceClock(bool update) {\n  setValue(\"updateDeviceClock\", update);\n}\n\nbool\nSettings::autoEnableGPS() const {\n  return value(\"autoEnableGPS\", false).toBool();\n}\nvoid\nSettings::setAutoEnableGPS(bool update) {\n  setValue(\"autoEnableGPS\", update);\n}\n\nbool\nSettings::autoEnableRoaming() const {\n  return value(\"autoEnableRoaming\", false).toBool();\n}\nvoid\nSettings::setAutoEnableRoaming(bool update) {\n  setValue(\"autoEnableRoaming\", update);\n}\n\nQDir\nSettings::lastDirectory() const {\n  return QDir(value(\"lastDir\", QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first()).toString());\n}\nvoid\nSettings::setLastDirectoryDir(const QDir &dir) {\n  setValue(\"lastDir\", dir.absolutePath());\n}\n\nCodeplug::Flags\nSettings::codePlugFlags() const {\n  Codeplug::Flags flags;\n  flags.setUpdateDeviceClock(updateDeviceClock());\n  flags.setUpdateCodeplug(updateCodeplug());\n  flags.setAutoEnableGPS(autoEnableGPS());\n  flags.setAutoEnableRoaming(autoEnableRoaming());\n  return flags;\n}\n\nbool\nSettings::limitCallSignDBEntries() const {\n  return value(\"limitCallSignDBEntries\", false).toBool();\n}\nvoid\nSettings::setLimitCallSignDBEnties(bool enable) {\n  setValue(\"limitCallSignDBEntries\", enable);\n}\n\nunsigned\nSettings::maxCallSignDBEntries() const {\n  return value(\"maxCallSignDBEntries\", 1).toInt();\n}\nvoid\nSettings::setMaxCallSignDBEntries(unsigned max) {\n  setValue(\"maxCallSignDBEntries\", max);\n}\n\nbool\nSettings::selectUsingUserDMRID() {\n  int num = beginReadArray(\"callSignDBPrefixes\"); endArray();\n  return value(\"selectCallSignDBUsingUserDMRID\", true).toBool() || (0 == num);\n}\nvoid\nSettings::setSelectUsingUserDMRID(bool enable) {\n  setValue(\"selectCallSignDBUsingUserDMRID\", enable);\n}\n\nQSet<unsigned>\nSettings::callSignDBPrefixes() {\n  QSet<unsigned> prefixes;\n  int num = beginReadArray(\"callSignDBPrefixes\");\n  for (int i=0; i<num; i++) {\n    setArrayIndex(i);\n    bool ok=true; unsigned prefix = value(\"prefix\").toInt(&ok);\n    if (ok)\n      prefixes.insert(prefix);\n  }\n  endArray();\n  return prefixes;\n}\nvoid\nSettings::setCallSignDBPrefixes(const QSet<unsigned> &prefixes) {\n  beginWriteArray(\"callSignDBPrefixes\");\n  unsigned i = 0;\n  foreach (unsigned prefix, prefixes) {\n    setArrayIndex(i);\n    setValue(\"prefix\", prefix);\n    i++;\n  }\n  endArray();\n}\n\nbool\nSettings::ignoreVerificationWarning() const {\n  return value(\"ignoreVerificationWarning\", true).toBool();\n}\nvoid\nSettings::setIgnoreVerificationWarning(bool ignore) {\n  setValue(\"ignoreVerificationWarning\", ignore);\n}\n\nbool\nSettings::ignoreFrequencyLimits() const {\n  return value(\"ignoreFrequencyLimits\", false).toBool();\n}\nvoid\nSettings::setIgnoreFrequencyLimits(bool ignore) {\n  setValue(\"ignoreFrequencyLimits\", ignore);\n}\n\nbool\nSettings::hideChannelNote() const {\n  return value(\"hideChannelNote\", false).toBool();\n}\nvoid\nSettings::setHideChannelNote(bool hide) {\n  setValue(\"hideChannelNote\", hide);\n}\n\nbool\nSettings::hideGSPNote() const {\n  return value(\"hideGPSNote\", false).toBool();\n}\n\nvoid\nSettings::setHideGPSNote(bool hide) {\n  setValue(\"hideGPSNote\", hide);\n}\n\nbool\nSettings::hideRoamingChannelNote() const {\n  return value(\"hideRoamingChannelNote\", false).toBool();\n}\n\nvoid\nSettings::setHideRoamingChannelNote(bool hide) {\n  setValue(\"hideRoamingChannelNote\", hide);\n}\n\nbool\nSettings::hideRoamingNote() const {\n  return value(\"hideRoamingNote\", false).toBool();\n}\n\nvoid\nSettings::setHideRoamingNote(bool hide) {\n  setValue(\"hideRoamingNote\", hide);\n}\n\nbool\nSettings::hideZoneNote() const {\n  return value(\"hideZoneNote\", false).toBool();\n}\n\nvoid\nSettings::setHideZoneNote(bool hide) {\n  setValue(\"hideZoneNote\", hide);\n}\n\nbool\nSettings::showDisclaimer() const {\n  return value(\"showDisclaimer\", true).toBool();\n}\nvoid\nSettings::setShowDisclaimer(bool show) {\n  setValue(\"showDisclaimer\", show);\n}\n\nConfigMergeVisitor::ItemStrategy\nSettings::configMergeItemStrategy() const {\n  return (ConfigMergeVisitor::ItemStrategy)value(\n        \"configMergeItemStrategy\", (uint) ConfigMergeVisitor::ItemStrategy::Duplicate).toUInt();\n}\nvoid\nSettings::setConfigMergeItemStrategy(ConfigMergeVisitor::ItemStrategy strategy) {\n  setValue(\"configMergeItemStrategy\", (uint)strategy);\n}\n\nConfigMergeVisitor::SetStrategy\nSettings::configMergeSetStrategy() const {\n  return (ConfigMergeVisitor::SetStrategy)value(\n        \"configMergeSetStrategy\", (uint) ConfigMergeVisitor::SetStrategy::Merge).toUInt();\n}\nvoid\nSettings::setConfigMergeSetStrategy(ConfigMergeVisitor::SetStrategy strategy) {\n  setValue(\"configMergeSetStrategy\", (uint)strategy);\n}\n\nQByteArray\nSettings::mainWindowState() const {\n  return value(\"mainWindowState\", QByteArray()).toByteArray();\n}\nvoid\nSettings::setMainWindowState(const QByteArray &state) {\n  setValue(\"mainWindowState\", state);\n}\n\nQByteArray\nSettings::headerState(const QString &objName) const {\n  if (objName.isEmpty())\n    return QByteArray();\n  QString key = QString(\"headerState/%1\").arg(objName);\n  return value(key, QByteArray()).toByteArray();\n}\nvoid\nSettings::setHeaderState(const QString &objName, const QByteArray &state) {\n  if (objName.isEmpty())\n    return;\n  QString key = QString(\"headerState/%1\").arg(objName);\n  setValue(key, state);\n}\n\n\nbool\nSettings::sortFilterEnabled(const QString &objName) const {\n  if (objName.isEmpty())\n    return false;\n  QString key = QString(\"sortFilter/%1\").arg(objName);\n  return value(key, false).toBool();\n}\n\nvoid\nSettings::enableSortFilter(const QString &objName, bool enable) {\n  if (objName.isEmpty())\n    return;\n  QString key = QString(\"sortFilter/%1\").arg(objName);\n  setValue(key, enable);\n}\n\nbool\nSettings::isUpdated() const {\n  if (! contains(\"version\"))\n    return false;\n  return VERSION_STRING == value(\"version\").toString();\n}\nvoid\nSettings::markUpdated() {\n  setValue(\"version\", VERSION_STRING);\n}\n\n\n/* ********************************************************************************************* *\n * Implementation of SettingsDialog\n * ********************************************************************************************* */\nSettingsDialog::SettingsDialog(QWidget *parent)\n  : QDialog(parent)\n{\n  setupUi(this);\n\n  Settings settings;\n\n  _source = QGeoPositionInfoSource::createDefaultSource(this);\n  if (_source) {\n    connect(_source, SIGNAL(positionUpdated(QGeoPositionInfo)),\n            this, SLOT(positionUpdated(QGeoPositionInfo)));\n    if (settings.queryPosition())\n      _source->startUpdates();\n  }\n\n  queryLocation->setChecked(settings.queryPosition());\n  locatorEntry->setText(settings.locator());\n  if (queryLocation->isChecked())\n    locatorEntry->setEnabled(false);\n  Ui::SettingsDialog::searchRadius->setValue(settings.repeaterSearchRadius());\n\n  Ui::SettingsDialog::repeaterBookEnable->setChecked(settings.repeaterBookSourceEnabled());\n  switch (settings.repeaterBookRegion()) {\n  case RepeaterBookSource::World: Ui::SettingsDialog::repeaterBookRegion->setCurrentIndex(0); break;\n  case RepeaterBookSource::NorthAmerica: Ui::SettingsDialog::repeaterBookRegion->setCurrentIndex(1); break;\n  }\n\n  Ui::SettingsDialog::repeaterMapEnable->setChecked(settings.repeaterMapSourceEnabled());\n  Ui::SettingsDialog::repeatermapAPIToken->setText(settings.repeaterMapAPIToken().toHex());\n  Ui::SettingsDialog::hearhamEnable->setChecked(settings.hearhamSourceEnabled());\n  Ui::SettingsDialog::radioIdEnable->setChecked(settings.radioIdRepeaterSourceEnabled());\n\n  connect(Ui::SettingsDialog::ignoreFrequencyLimits, SIGNAL(toggled(bool)),\n          this, SLOT(onIgnoreFrequencyLimitsSet(bool)));\n  connect(queryLocation, SIGNAL(toggled(bool)), this, SLOT(onSystemLocationToggled(bool)));\n\n  Ui::SettingsDialog::disableAutoDetect->setChecked(settings.disableAutoDetect());\n  Ui::SettingsDialog::updateCodeplug->setChecked(settings.updateCodeplug());\n  Ui::SettingsDialog::updateDeviceClock->setChecked(settings.updateDeviceClock());\n  Ui::SettingsDialog::autoEnableGPS->setChecked(settings.autoEnableGPS());\n  Ui::SettingsDialog::autoEnableRoaming->setChecked(settings.autoEnableRoaming());\n  Ui::SettingsDialog::ignoreVerificationWarnings->setChecked(settings.ignoreVerificationWarning());\n  Ui::SettingsDialog::ignoreFrequencyLimits->setChecked(settings.ignoreFrequencyLimits());\n\n  Ui::SettingsDialog::dbLimitEnable->setChecked(settings.limitCallSignDBEntries());\n  if (! settings.limitCallSignDBEntries())\n    Ui::SettingsDialog::dbLimit->setEnabled(false);\n  Ui::SettingsDialog::dbLimit->setValue(settings.maxCallSignDBEntries());\n  Ui::SettingsDialog::useUserId->setChecked(settings.selectUsingUserDMRID());\n  if (settings.selectUsingUserDMRID())\n    Ui::SettingsDialog::prefixes->setEnabled(false);\n  QSet<unsigned> prefs = settings.callSignDBPrefixes();\n  QStringList prefs_text;\n  foreach (unsigned prefix, prefs) {\n    prefs_text.append(QString::number(prefix));\n  }\n  Ui::SettingsDialog::prefixes->setText(prefs_text.join(\", \"));\n\n  connect(Ui::SettingsDialog::dbLimitEnable, SIGNAL(toggled(bool)), this, SLOT(onDBLimitToggled(bool)));\n  connect(Ui::SettingsDialog::useUserId, SIGNAL(toggled(bool)), this, SLOT(onUseUserDMRIdToggled(bool)));\n}\n\nbool\nSettingsDialog::systemLocationEnabled() const {\n  return queryLocation->isChecked();\n}\n\nQString\nSettingsDialog::locator() const {\n  return locatorEntry->text().simplified();\n}\n\nvoid\nSettingsDialog::onSystemLocationToggled(bool enabled) {\n  locatorEntry->setEnabled(! enabled);\n  if (enabled && _source)\n    _source->startUpdates();\n  else if (_source)\n    _source->stopUpdates();\n}\n\nvoid\nSettingsDialog::onIgnoreFrequencyLimitsSet(bool enabled) {\n  if (enabled) {\n    Ui::SettingsDialog::ignoreFrequencyLimits->setText(tr(\"Warning!\"));\n  } else {\n    Ui::SettingsDialog::ignoreFrequencyLimits->setText(\"\");\n  }\n}\n\nvoid\nSettingsDialog::positionUpdated(const QGeoPositionInfo &info) {\n  logDebug() << \"Application: Current position: \" << info.coordinate().toString();\n  if (info.isValid() && queryLocation->isChecked()) {\n    locatorEntry->setText(deg2loc(info.coordinate()));\n  }\n}\n\nvoid\nSettingsDialog::onDBLimitToggled(bool enable) {\n  Ui::SettingsDialog::dbLimit->setEnabled(enable);\n}\n\nvoid\nSettingsDialog::onUseUserDMRIdToggled(bool enable) {\n  Ui::SettingsDialog::prefixes->setEnabled(! enable);\n}\n\n\nvoid\nSettingsDialog::accept() {\n  Settings settings;\n  settings.setQueryPosition(queryLocation->isChecked());\n  settings.setLocator(locatorEntry->text().simplified());\n  settings.setRepeaterSearchRadius(Ui::SettingsDialog::searchRadius->value());\n  settings.setDisableAutoDetect(disableAutoDetect->isChecked());\n  settings.enableRepeaterBookSource(Ui::SettingsDialog::repeaterBookEnable->isChecked());\n  if (0 == Ui::SettingsDialog::repeaterBookRegion->currentIndex())\n    settings.setRepeaterBookRegion(RepeaterBookSource::World);\n  else\n    settings.setRepeaterBookRegion(RepeaterBookSource::NorthAmerica);\n  settings.enableRepeaterMapSource(Ui::SettingsDialog::repeaterMapEnable->isChecked());\n  settings.setRepeaterMapAPIToken(\n    QByteArray::fromHex(Ui::SettingsDialog::repeatermapAPIToken->text().simplified().toLatin1()));\n  settings.enableHearhamSource(Ui::SettingsDialog::hearhamEnable->isChecked());\n  settings.enableRadioIdRepeaterSource(Ui::SettingsDialog::radioIdEnable->isChecked());\n\n  settings.setUpdateCodeplug(updateCodeplug->isChecked());\n  settings.setUpdateDeviceClock(updateDeviceClock->isChecked());\n  settings.setAutoEnableGPS(autoEnableGPS->isChecked());\n  settings.setAutoEnableRoaming(autoEnableRoaming->isChecked());\n  settings.setIgnoreVerificationWarning(ignoreVerificationWarnings->isChecked());\n  settings.setIgnoreFrequencyLimits(ignoreFrequencyLimits->isChecked());\n  settings.setLimitCallSignDBEnties(dbLimitEnable->isChecked());\n  settings.setMaxCallSignDBEntries(dbLimit->value());\n  settings.setSelectUsingUserDMRID(useUserId->isChecked());\n\n  QStringList prefs_text = prefixes->text().split(\",\");\n  QSet<unsigned> prefs;\n  foreach (QString pref, prefs_text) {\n    bool ok=true; unsigned prefix = pref.toUInt(&ok);\n    if (ok)\n      prefs.insert(prefix);\n  }\n  settings.setCallSignDBPrefixes(prefs);\n\n  QDialog::accept();\n}\n\n\n\n"
  },
  {
    "path": "src/settings.hh",
    "content": "#ifndef SETTINGS_HH\n#define SETTINGS_HH\n\n#include <QDir>\n#include <QSettings>\n#include <QDateTime>\n#include <QGeoPositionInfoSource>\n#include <QGeoCoordinate>\n\n#include \"ui_settingsdialog.h\"\n\n#include \"codeplug.hh\"\n#include \"configmergevisitor.hh\"\n#include \"repeaterbooksource.hh\"\n\n\nclass Settings : public QSettings\n{\n\tQ_OBJECT\n\npublic:\n\texplicit Settings(QObject *parent=nullptr);\n\n\tQDateTime lastRepeaterUpdate() const;\n\tbool repeaterUpdateNeeded(unsigned period=7) const;\n\tvoid repeaterUpdated();\n\n  bool queryPosition() const;\n  void setQueryPosition(bool enable);\n\n  QString locator() const;\n  void setLocator(const QString &locator);\n  QGeoCoordinate position() const;\n  int repeaterSearchRadius() const;\n  void setRepeaterSearchRadius(int radius);\n\n  bool repeaterBookSourceEnabled() const;\n  void enableRepeaterBookSource(bool enable);\n\n  bool repeaterMapSourceEnabled() const;\n  void enableRepeaterMapSource(bool enable);\n  QByteArray repeaterMapAPIToken() const;\n  void setRepeaterMapAPIToken(const QByteArray &token);\n\n  bool hearhamSourceEnabled() const;\n  void enableHearhamSource(bool enable);\n\n  bool radioIdRepeaterSourceEnabled() const;\n  void enableRadioIdRepeaterSource(bool enable);\n\n  RepeaterBookSource::Region repeaterBookRegion() const;\n  void setRepeaterBookRegion(RepeaterBookSource::Region region);\n\n  bool disableAutoDetect() const;\n  void setDisableAutoDetect(bool disable);\n\n  bool updateCodeplug() const;\n  void setUpdateCodeplug(bool update);\n\n  bool updateDeviceClock() const;\n  void setUpdateDeviceClock(bool update);\n\n  bool autoEnableGPS() const;\n  void setAutoEnableGPS(bool enable);\n\n  bool autoEnableRoaming() const;\n  void setAutoEnableRoaming(bool enable);\n\n  QDir lastDirectory() const;\n  void setLastDirectoryDir(const QDir &dir);\n\n  Codeplug::Flags codePlugFlags() const;\n\n  bool limitCallSignDBEntries() const;\n  void setLimitCallSignDBEnties(bool enable);\n  unsigned maxCallSignDBEntries() const;\n  void  setMaxCallSignDBEntries(unsigned max);\n  bool selectUsingUserDMRID();\n  void setSelectUsingUserDMRID(bool enable);\n  QSet<unsigned> callSignDBPrefixes();\n  void setCallSignDBPrefixes(const QSet<unsigned> &prefixes);\n\n  bool ignoreVerificationWarning() const;\n  void setIgnoreVerificationWarning(bool ignore);\n\n  bool ignoreFrequencyLimits() const;\n  void setIgnoreFrequencyLimits(bool ignore);\n\n  bool hideChannelNote() const;\n  void setHideChannelNote(bool hide);\n\n  bool hideGSPNote() const;\n  void setHideGPSNote(bool hide);\n\n  bool hideRoamingChannelNote() const;\n  void setHideRoamingChannelNote(bool hide);\n\n  bool hideRoamingNote() const;\n  void setHideRoamingNote(bool hide);\n\n  bool hideZoneNote() const;\n  void setHideZoneNote(bool hide);\n\n  bool showDisclaimer() const;\n  void setShowDisclaimer(bool show);\n\n  ConfigMergeVisitor::ItemStrategy configMergeItemStrategy() const;\n  void setConfigMergeItemStrategy(ConfigMergeVisitor::ItemStrategy strategy);\n\n  ConfigMergeVisitor::SetStrategy configMergeSetStrategy() const;\n  void setConfigMergeSetStrategy(ConfigMergeVisitor::SetStrategy strategy);\n\n  QByteArray mainWindowState() const;\n  void setMainWindowState(const QByteArray &state);\n\n  QByteArray headerState(const QString &objName) const;\n  void setHeaderState(const QString &objName, const QByteArray &state);\n\n  bool sortFilterEnabled(const QString &objName) const;\n  void enableSortFilter(const QString &objName, bool enable);\n\n  bool isUpdated() const;\n  void markUpdated();\n};\n\n\nclass SettingsDialog: public QDialog, private Ui::SettingsDialog\n{\n  Q_OBJECT\n\npublic:\n  explicit SettingsDialog(QWidget *parent=nullptr);\n\n  bool systemLocationEnabled() const;\n  QString locator() const;\n\npublic slots:\n  void accept();\n\nprotected slots:\n  void onSystemLocationToggled(bool enable);\n  void positionUpdated(const QGeoPositionInfo &info);\n  void onIgnoreFrequencyLimitsSet(bool enabled);\n  void onDBLimitToggled(bool enable);\n  void onUseUserDMRIdToggled(bool enable);\n\nprotected:\n  QGeoPositionInfoSource *_source;\n};\n\n#endif // SETTINGS_HH\n"
  },
  {
    "path": "src/settingsdialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SettingsDialog</class>\n <widget class=\"QDialog\" name=\"SettingsDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>599</width>\n    <height>826</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Settings</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"dataSourcesTab\">\n      <attribute name=\"title\">\n       <string>Data Sources</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <item>\n        <widget class=\"QGroupBox\" name=\"groupBox\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"title\">\n          <string>Location</string>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label\">\n            <property name=\"text\">\n             <string>System location</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"queryLocation\">\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_2\">\n            <property name=\"text\">\n             <string>Locator</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"locatorEntry\"/>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_17\">\n            <property name=\"text\">\n             <string>Search radius</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"searchRadius\">\n            <property name=\"toolTip\">\n             <string>Some data sources require a search area specified by the location and this radius.</string>\n            </property>\n            <property name=\"suffix\">\n             <string notr=\"true\">km</string>\n            </property>\n            <property name=\"minimum\">\n             <number>20</number>\n            </property>\n            <property name=\"maximum\">\n             <number>150</number>\n            </property>\n            <property name=\"singleStep\">\n             <number>10</number>\n            </property>\n            <property name=\"stepType\">\n             <enum>QAbstractSpinBox::StepType::AdaptiveDecimalStepType</enum>\n            </property>\n            <property name=\"value\">\n             <number>50</number>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"groupBox_5\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"title\">\n          <string>Repeater Info Sources</string>\n         </property>\n         <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n          <item>\n           <widget class=\"QTabWidget\" name=\"sourceTab\">\n            <property name=\"currentIndex\">\n             <number>0</number>\n            </property>\n            <widget class=\"QWidget\" name=\"repeaterbookTab\">\n             <attribute name=\"title\">\n              <string notr=\"true\">repeaterbook.com</string>\n             </attribute>\n             <layout class=\"QFormLayout\" name=\"formLayout_3\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_13\">\n                <property name=\"text\">\n                 <string>Enable</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"repeaterBookEnable\">\n                <property name=\"text\">\n                 <string/>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_12\">\n                <property name=\"text\">\n                 <string>Area</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QComboBox\" name=\"repeaterBookRegion\">\n                <property name=\"sizePolicy\">\n                 <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n                  <horstretch>0</horstretch>\n                  <verstretch>0</verstretch>\n                 </sizepolicy>\n                </property>\n                <item>\n                 <property name=\"text\">\n                  <string>World</string>\n                 </property>\n                </item>\n                <item>\n                 <property name=\"text\">\n                  <string>North America</string>\n                 </property>\n                </item>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_15\">\n                <property name=\"text\">\n                 <string>API Token</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"2\" column=\"1\">\n               <widget class=\"QLineEdit\" name=\"repeaterBookAPIToken\">\n                <property name=\"enabled\">\n                 <bool>false</bool>\n                </property>\n                <property name=\"echoMode\">\n                 <enum>QLineEdit::EchoMode::PasswordEchoOnEdit</enum>\n                </property>\n                <property name=\"placeholderText\">\n                 <string>paste token here</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n            <widget class=\"QWidget\" name=\"repeaterMapTab\">\n             <attribute name=\"title\">\n              <string notr=\"true\">repeatermap.de</string>\n             </attribute>\n             <layout class=\"QFormLayout\" name=\"formLayout_6\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_20\">\n                <property name=\"text\">\n                 <string>Enable</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"repeaterMapEnable\">\n                <property name=\"text\">\n                 <string/>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_21\">\n                <property name=\"text\">\n                 <string>API Token</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"1\" column=\"1\">\n               <widget class=\"QLineEdit\" name=\"repeatermapAPIToken\">\n                <property name=\"echoMode\">\n                 <enum>QLineEdit::EchoMode::PasswordEchoOnEdit</enum>\n                </property>\n                <property name=\"placeholderText\">\n                 <string>paste token here</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n            <widget class=\"QWidget\" name=\"hearhamTab\">\n             <attribute name=\"title\">\n              <string notr=\"true\">hearham.com</string>\n             </attribute>\n             <layout class=\"QFormLayout\" name=\"formLayout_7\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_22\">\n                <property name=\"text\">\n                 <string>Enable</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"hearhamEnable\">\n                <property name=\"text\">\n                 <string/>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n            <widget class=\"QWidget\" name=\"radioidnetTab\">\n             <attribute name=\"title\">\n              <string notr=\"true\">radioid.net</string>\n             </attribute>\n             <layout class=\"QFormLayout\" name=\"formLayout_8\">\n              <item row=\"0\" column=\"0\">\n               <widget class=\"QLabel\" name=\"label_16\">\n                <property name=\"text\">\n                 <string>Enable</string>\n                </property>\n               </widget>\n              </item>\n              <item row=\"0\" column=\"1\">\n               <widget class=\"QCheckBox\" name=\"radioIdEnable\">\n                <property name=\"text\">\n                 <string/>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </widget>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"QWidget\" name=\"programmingTab\">\n      <attribute name=\"title\">\n       <string>Programming</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n       <item>\n        <widget class=\"QGroupBox\" name=\"groupBox_5\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"title\">\n          <string>Radio Interfaces</string>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout_5\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_14\">\n            <property name=\"text\">\n             <string>disable auto-detect</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"disableAutoDetect\">\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"groupBox_2\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"title\">\n          <string>Radio Programming</string>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout_2\">\n          <property name=\"fieldGrowthPolicy\">\n           <enum>QFormLayout::FieldGrowthPolicy::AllNonFixedFieldsGrow</enum>\n          </property>\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_3\">\n            <property name=\"text\">\n             <string>Update codeplug</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"updateCodeplug\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Update the codeplug on the radio. If not selected, the codeplug on the radio gets overridden with possibly incomplete default values.&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;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).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_4\">\n            <property name=\"text\">\n             <string>Auto-enable GPS</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"autoEnableGPS\">\n            <property name=\"toolTip\">\n             <string>When a GPS or APRS system is defined and used for any channel, the GPS module gets enabled automatically.</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_5\">\n            <property name=\"text\">\n             <string>Auto-enable roaming</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"autoEnableRoaming\">\n            <property name=\"toolTip\">\n             <string>When a roaming zone is defined and used by any channel, the automatic roaming gets enabled.</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_6\">\n            <property name=\"text\">\n             <string>Ignore verification warnings</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"4\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"ignoreVerificationWarnings\">\n            <property name=\"toolTip\">\n             <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;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.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_7\">\n            <property name=\"text\">\n             <string>Ignore frequency limits</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"5\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"ignoreFrequencyLimits\">\n            <property name=\"toolTip\">\n             <string>Do not set this option unless you know what you are doing.</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_19\">\n            <property name=\"text\">\n             <string>Update Device Clock</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"updateDeviceClock\">\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n       <item>\n        <widget class=\"QGroupBox\" name=\"groupBox_3\">\n         <property name=\"sizePolicy\">\n          <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n           <horstretch>0</horstretch>\n           <verstretch>0</verstretch>\n          </sizepolicy>\n         </property>\n         <property name=\"title\">\n          <string>Call-Sign DB</string>\n         </property>\n         <layout class=\"QFormLayout\" name=\"formLayout_4\">\n          <item row=\"0\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_8\">\n            <property name=\"text\">\n             <string>Limit number of DB entries</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"0\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"dbLimitEnable\">\n            <property name=\"toolTip\">\n             <string>When enabled, the number of DB entries will be limited. Otherwise the maximum number of entries are generated (device dependent).</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_9\">\n            <property name=\"text\">\n             <string>Number of DB entries</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"1\" column=\"1\">\n           <widget class=\"QSpinBox\" name=\"dbLimit\">\n            <property name=\"sizePolicy\">\n             <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n              <horstretch>0</horstretch>\n              <verstretch>0</verstretch>\n             </sizepolicy>\n            </property>\n            <property name=\"toolTip\">\n             <string>Specifies the number of DB entries (if enabled above).</string>\n            </property>\n            <property name=\"minimum\">\n             <number>1</number>\n            </property>\n            <property name=\"maximum\">\n             <number>999999</number>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_10\">\n            <property name=\"text\">\n             <string>Select using my DMR ID</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"2\" column=\"1\">\n           <widget class=\"QCheckBox\" name=\"useUserId\">\n            <property name=\"toolTip\">\n             <string>If enabled, the entries are selected using the users DMR ID.</string>\n            </property>\n            <property name=\"text\">\n             <string/>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"0\">\n           <widget class=\"QLabel\" name=\"label_11\">\n            <property name=\"text\">\n             <string>Select using prefixes</string>\n            </property>\n           </widget>\n          </item>\n          <item row=\"3\" column=\"1\">\n           <widget class=\"QLineEdit\" name=\"prefixes\">\n            <property name=\"toolTip\">\n             <string>If enabled, these comma separated DMR ID prefixes are used to select the call-sign DB entries.</string>\n            </property>\n           </widget>\n          </item>\n         </layout>\n        </widget>\n       </item>\n      </layout>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>SettingsDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>SettingsDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/squelchedit.cc",
    "content": "#include \"squelchedit.hh\"\n#include \"ui_squelchedit.h\"\n#include \"channel.hh\"\n\n\n\nChannelSquelchEdit::ChannelSquelchEdit(Level globalDefault, QWidget *parent)\n  : QWidget(parent) , ui(new Ui::SquelchEdit), _globalDefault(globalDefault)\n{\n  ui->setupUi(this);\n  ui->defaultBox->setChecked(true);\n  ui->valueBox->setEnabled(false);\n  ui->valueBox->setValue(_globalDefault.value());\n  connect(ui->defaultBox, &QCheckBox::toggled, [this](bool checked) { this->ui->valueBox->setEnabled(!checked); });\n}\n\nChannelSquelchEdit::~ChannelSquelchEdit() {\n  delete ui;\n}\n\n\nvoid\nChannelSquelchEdit::setChannel(AnalogChannel *ch) {\n  _channel = ch;\n  if (ch->defaultSquelch()) {\n    ui->defaultBox->setChecked(true);\n    ui->valueBox->setValue(_globalDefault.value());\n  } else\n    ui->valueBox->setValue(ch->squelch().value());\n}\n\n\nvoid\nChannelSquelchEdit::accept() {\n  if (ui->defaultBox->isChecked()) {\n    _channel->setSquelchDefault();\n  } else {\n    _channel->setSquelch(Level::fromValue(ui->valueBox->value()));\n  }\n}\n\n\n"
  },
  {
    "path": "src/squelchedit.hh",
    "content": "#ifndef SQUELCHEDIT_HH\n#define SQUELCHEDIT_HH\n\n#include \"level.hh\"\n#include <QWidget>\n#include <QPointer>\n\nnamespace Ui {\nclass SquelchEdit;\n}\n\nclass AnalogChannel;\n\nclass ChannelSquelchEdit : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ChannelSquelchEdit(Level globalDefault=Level::fromValue(1), QWidget *parent = nullptr);\n  ~ChannelSquelchEdit();\n\n  void setChannel(AnalogChannel *ch);\n\npublic slots:\n  virtual void accept();\n\nprivate:\n  Ui::SquelchEdit *ui;\n  QPointer<AnalogChannel> _channel;\n  Level _globalDefault;\n};\n\n#endif // SQUELCHEDIT_HH\n"
  },
  {
    "path": "src/squelchedit.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>SquelchEdit</class>\n <widget class=\"QWidget\" name=\"SquelchEdit\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>267</width>\n    <height>45</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Minimum\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\"/>\n  </property>\n  <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n   <item>\n    <widget class=\"QSpinBox\" name=\"valueBox\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Fixed\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n     <property name=\"specialValueText\">\n      <string>Open</string>\n     </property>\n     <property name=\"maximum\">\n      <number>10</number>\n     </property>\n     <property name=\"value\">\n      <number>0</number>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QCheckBox\" name=\"defaultBox\">\n     <property name=\"toolTip\">\n      <string>Uses the global squelch setting if enabled.</string>\n     </property>\n     <property name=\"text\">\n      <string>Default</string>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "src/timeslotselect.cc",
    "content": "#include \"timeslotselect.hh\"\n\nTimeslotSelect::TimeslotSelect(QWidget *parent)\n: QComboBox(parent)\n{\n  addItem(tr(\"Slot 1\"), QVariant::fromValue(DMRChannel::TimeSlot::TS1));\n  addItem(tr(\"Slot 2\"), QVariant::fromValue(DMRChannel::TimeSlot::TS2));\n}\n\n\nvoid\nTimeslotSelect::setSlot(DMRChannel::TimeSlot slot) {\n  setCurrentIndex((slot == DMRChannel::TimeSlot::TS1) ? 0 : 1);\n}\n\nDMRChannel::TimeSlot\nTimeslotSelect::slot() const {\n  return currentData().value<DMRChannel::TimeSlot>();\n}\n"
  },
  {
    "path": "src/timeslotselect.hh",
    "content": "#ifndef TIMESLOTSELECT_HH\n#define TIMESLOTSELECT_HH\n\n#include <QComboBox>\n#include \"channel.hh\"\n\n\nclass TimeslotSelect : public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  explicit TimeslotSelect(QWidget *parent=nullptr);\n\n  void setSlot(DMRChannel::TimeSlot slot);\n  DMRChannel::TimeSlot slot() const;\n};\n\n#endif // TIMESLOTSELECT_HH\n"
  },
  {
    "path": "src/transponderfrequencydelegate.cc",
    "content": "#include \"transponderfrequencydelegate.hh\"\n#include \"satellitedatabase.hh\"\n\n\n/* ********************************************************************************************* *\n * Implementation of TransponderFrequencyEditor\n * ********************************************************************************************* */\nTransponderFrequencyEditor::TransponderFrequencyEditor(QWidget *parent)\n  : QComboBox(parent)\n{\n  setEditable(true);\n}\n\n\nTransponderFrequencyEditor::TransponderFrequencyEditor(unsigned int satId, bool uplink, Transponder::Mode mode,\n                                                       const TransponderDatabase &transponder, QWidget *parent)\n  : QComboBox(parent)\n{\n  setEditable(true);\n  populate(satId, uplink, mode, transponder);\n}\n\n\nvoid\nTransponderFrequencyEditor::populate(unsigned int satId, bool uplink, Transponder::Mode mode,\n                                     const TransponderDatabase &transponder) {\n  clear();\n  addItem(tr(\"None\"), QVariant::fromValue(Frequency()));\n  for (const Transponder &tp: transponder) {\n    if (tp.satellite() != satId)\n      continue;\n    if (tp.mode() != mode)\n      continue;\n\n    Frequency f = uplink ? tp.uplink() : tp.downlink();\n    if (0 == f.inHz())\n      continue;\n\n    addItem(QString(\"%1 (%2)\").arg(tp.name(), f.format()),\n            QVariant::fromValue(f));\n  }\n}\n\n\nvoid\nTransponderFrequencyEditor::setFrequency(const Frequency &current) {\n  for (int i=0; i<count(); i++) {\n    if (itemData(i).value<Frequency>() == current) {\n      setCurrentIndex(i);\n      break;\n    }\n  }\n}\n\nFrequency\nTransponderFrequencyEditor::frequency() const {\n  return currentData().value<Frequency>();\n}\n\n\n\n/* ********************************************************************************************* *\n * Implementation of TransponderFrequencyDelegate\n * ********************************************************************************************* */\nTransponderFrequencyDelegate::TransponderFrequencyDelegate(bool uplink, Transponder::Mode mode, QObject *parent)\n  : QStyledItemDelegate(parent), _uplink(uplink), _mode(mode)\n{\n  // pass...\n}\n\n\nQWidget *\nTransponderFrequencyDelegate::createEditor(\n    QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const\n{\n  Q_UNUSED(option);\n  auto model = qobject_cast<const SatelliteDatabase *>(index.model());\n  unsigned int satId = model->getAt(index.row()).id();\n  return new TransponderFrequencyEditor(satId, _uplink, _mode, model->transponders(), parent);\n}\n\n\nvoid\nTransponderFrequencyDelegate::setEditorData(QWidget *editor, const QModelIndex index) {\n  auto feditor = qobject_cast<TransponderFrequencyEditor*>(editor);\n  feditor->setFrequency(index.data(Qt::EditRole).value<Frequency>());\n}\n\n\nvoid\nTransponderFrequencyDelegate::setModelData(\n    QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const\n{\n  model->setData(\n        index, QVariant::fromValue(qobject_cast<TransponderFrequencyEditor*>(editor)->frequency()));\n}\n\n"
  },
  {
    "path": "src/transponderfrequencydelegate.hh",
    "content": "#ifndef TRANSPONDERFREQUENCYDELEGATE_HH\n#define TRANSPONDERFREQUENCYDELEGATE_HH\n\n#include <QComboBox>\n#include <QStyledItemDelegate>\n\n#include \"transponderdatabase.hh\"\n\n\n\nclass TransponderFrequencyEditor: public QComboBox\n{\n  Q_OBJECT\n\npublic:\n  explicit TransponderFrequencyEditor(QWidget *parent=nullptr);\n\n  TransponderFrequencyEditor(\n      unsigned int satId, bool uplink, Transponder::Mode mode,\n      const TransponderDatabase &transponder, QWidget *parent=nullptr);\n\n  void populate(unsigned int satId, bool uplink, Transponder::Mode mode,\n                const TransponderDatabase &transponder);\n\n  void setFrequency(const Frequency &current);\n  Frequency frequency() const;\n};\n\n\n\nclass TransponderFrequencyDelegate: public QStyledItemDelegate\n{\n  Q_OBJECT\n\npublic:\n  explicit TransponderFrequencyDelegate(bool uplink, Transponder::Mode mode, QObject *parent=nullptr);\n\n  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;\n  void setEditorData(QWidget *editor, const QModelIndex index);\n  void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;\n\nprotected:\n  bool _uplink;\n  Transponder::Mode _mode;\n};\n\n#endif // TRANSPONDERFREQUENCYDELEGATE_HH\n"
  },
  {
    "path": "src/verifydialog.cc",
    "content": "#include \"verifydialog.hh\"\n#include \"radiolimits.hh\"\n#include \"application.hh\"\n#include <QPushButton>\n#include <QLabel>\n\n\nVerifyDialog::VerifyDialog(const RadioLimitContext &issues, bool upload, QWidget *parent)\n    : QDialog(parent)\n{\n\tsetupUi(this);\n  uploadMessage->setVisible(false);\n\n  // Sort issues by severity\n  QList<RadioLimitIssue> issueList;\n  for (int i=0; i<issues.count(); i++)\n    issueList.append(issues.message(i));\n  std::sort(issueList.begin(), issueList.end(), [](const RadioLimitIssue &a, const RadioLimitIssue &b) {\n    return a.severity() > b.severity();\n  });\n\n  Application *app = qobject_cast<Application *>(QApplication::instance());\n  bool valid = true;\n  foreach(auto issue, issueList) {\n    auto item = new QTreeWidgetItem(treeWidget);\n    auto label = new QLabel(issue.message());\n    label->setMargin(5);\n    label->setWordWrap(true);\n    treeWidget->setItemWidget(item, 0, label);\n    // Dispatch by severity\n    switch (issue.severity()) {\n    case RadioLimitIssue::Silent:\n      break;\n    case RadioLimitIssue::Hint:\n      label->setStyleSheet(\"color: gray;\");\n      item->setIcon(0, QIcon::fromTheme(\"symbol-info\"));\n      break;\n    case RadioLimitIssue::Warning:\n      if (app->isDarkMode())\n        label->setStyleSheet(\"color: white;\");\n      else\n        label->setStyleSheet(\"color: black;\");\n      item->setIcon(0, QIcon::fromTheme(\"symbol-warning\"));\n      break;\n    case RadioLimitIssue::Critical:\n      if (app->isDarkMode())\n        label->setStyleSheet(\"color: red;\");\n      else\n        label->setStyleSheet(\"color: darkred;\");\n      valid = false;\n      item->setIcon(0, QIcon::fromTheme(\"symbol-error\"));\n      break;\n    }\n    // Add traceback (in reverse order)\n    foreach(auto step, issue.stack()) {\n      item->insertChild(0, new QTreeWidgetItem({step}));\n    }\n  }\n\n  if (upload) {\n    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);\n    QPushButton *ignore = buttonBox->button(QDialogButtonBox::Ok);\n    ignore->setEnabled(valid);\n    uploadMessage->setVisible(! valid);\n  }\n}\n"
  },
  {
    "path": "src/verifydialog.hh",
    "content": "#ifndef VERIFYDIALOG_HH\n#define VERIFYDIALOG_HH\n\n#include <QDialog>\n#include \"radio.hh\"\n#include \"ui_verifydialog.h\"\n\nclass RadioLimitContext;\n\nclass VerifyDialog : public QDialog, private Ui::VerifyDialog\n{\n  Q_OBJECT\npublic:\n\npublic:\n  explicit VerifyDialog(const RadioLimitContext &ctx, bool upload, QWidget *parent = nullptr);\n};\n\n#endif // VERIFYDIALOG_HH\n"
  },
  {
    "path": "src/verifydialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>VerifyDialog</class>\n <widget class=\"QDialog\" name=\"VerifyDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Verify Codeplug</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QLabel\" name=\"uploadMessage\">\n     <property name=\"text\">\n      <string>The codeplug cannot be uploaded, unless all critical issues (red) are resolved.</string>\n     </property>\n     <property name=\"wordWrap\">\n      <bool>true</bool>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QTreeWidget\" name=\"treeWidget\">\n     <property name=\"editTriggers\">\n      <set>QAbstractItemView::EditTrigger::NoEditTriggers</set>\n     </property>\n     <property name=\"showDropIndicator\" stdset=\"0\">\n      <bool>false</bool>\n     </property>\n     <property name=\"alternatingRowColors\">\n      <bool>true</bool>\n     </property>\n     <property name=\"iconSize\">\n      <size>\n       <width>32</width>\n       <height>32</height>\n      </size>\n     </property>\n     <property name=\"rootIsDecorated\">\n      <bool>true</bool>\n     </property>\n     <attribute name=\"headerVisible\">\n      <bool>false</bool>\n     </attribute>\n     <column>\n      <property name=\"text\">\n       <string notr=\"true\">1</string>\n      </property>\n     </column>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Orientation::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::StandardButton::Close</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>VerifyDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>VerifyDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/zonedialog.cc",
    "content": "#include \"zonedialog.hh\"\n\n#include \"settings.hh\"\n#include \"config.hh\"\n#include \"zone.hh\"\n#include \"channel.hh\"\n#include \"channelselectiondialog.hh\"\n#include \"settings.hh\"\n#include <QMessageBox>\n\n\n/* ********************************************************************************************* *\n * Implementation of ZoneDialog\n * ********************************************************************************************* */\nZoneDialog::ZoneDialog(Config *config, Zone *zone, QWidget *parent)\n  : QDialog(parent), _config(config), _myZone(new Zone(this)), _zone(zone)\n{\n  setWindowTitle(tr(\"Edit Zone\"));\n  if (_zone)\n    _myZone->copy(*_zone);\n\n  construct();\n}\n\nZoneDialog::ZoneDialog(Config *config, QWidget *parent)\n  : QDialog(parent), _config(config), _myZone(new Zone(this)), _zone(nullptr)\n{\n  setWindowTitle(tr(\"Create Zone\"));\n  construct();\n}\n\nvoid\nZoneDialog::construct() {\n  setupUi(this);\n  Settings settings;\n\n  if (settings.hideZoneNote())\n    zoneHint->setVisible(false);\n\n  zoneName->setText(_myZone->name());\n  listAView->setModel(new ChannelRefListWrapper(_myZone->A()));\n  listBView->setModel(new ChannelRefListWrapper(_myZone->B()));\n\n  extensionView->setObjectName(\"zoneExtension\");\n  extensionView->setObject(_myZone, _config);\n\n  connect(addChannelA, SIGNAL(clicked()), this, SLOT(onAddChannelA()));\n  connect(remChannelA, SIGNAL(clicked()), this, SLOT(onRemChannelA()));\n  connect(addChannelB, SIGNAL(clicked()), this, SLOT(onAddChannelB()));\n  connect(remChannelB, SIGNAL(clicked()), this, SLOT(onRemChannelB()));\n  connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));\n  connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));\n  connect(zoneHint, SIGNAL(linkActivated(QString)), this, SLOT(onHideZoneHint()));\n}\n\nvoid\nZoneDialog::onAddChannelA() {\n  MultiChannelSelectionDialog dia(_config->channelList(), false, false, _myZone->A());\n  if (QDialog::Accepted != dia.exec())\n    return;\n\n  QList<Channel *> channels = dia.channel();\n  foreach (Channel *channel, channels) {\n    if (0 <= _myZone->A()->indexOf(channel))\n      continue;\n    _myZone->A()->add(channel);\n  }\n}\n\nvoid\nZoneDialog::onRemChannelA() {\n  if (! listAView->hasSelection()) {\n    QMessageBox::information(nullptr, tr(\"Cannot remove channel\"),\n                             tr(\"Select at least one channel first.\"));\n    return;\n  }\n  QPair<int, int> selection = listAView->selection();\n  QList<Channel *> channels;\n  for (int i=selection.first; i<=selection.second; i++)\n    channels.push_back(_myZone->A()->get(i)->as<Channel>());\n  foreach (Channel *channel, channels) {\n    _myZone->A()->del(channel);\n  }\n}\n\n\nvoid\nZoneDialog::onAddChannelB() {\n  MultiChannelSelectionDialog dia(_config->channelList(), false, false, _myZone->B());\n  if (QDialog::Accepted != dia.exec())\n    return;\n\n  QList<Channel *> channels = dia.channel();\n  foreach (Channel *channel, channels) {\n    if (0 <= _myZone->B()->indexOf(channel))\n      continue;\n    _myZone->B()->add(channel);\n  }\n}\n\nvoid\nZoneDialog::onRemChannelB() {\n  if (! listBView->hasSelection()) {\n    QMessageBox::information(nullptr, tr(\"Cannot remove channel\"),\n                             tr(\"Select at least one channel first.\"));\n    return;\n  }\n  QPair<int, int> selection = listBView->selection();\n  QList<Channel *> channels;\n  for (int i=selection.first; i<=selection.second; i++)\n    channels.push_back(_myZone->B()->get(i)->as<Channel>());\n  foreach (Channel *channel, channels) {\n    _myZone->B()->del(channel);\n  }\n}\n\nvoid\nZoneDialog::onHideZoneHint() {\n  zoneHint->setVisible(false);\n  Settings settings;\n  settings.setHideZoneNote(true);\n}\n\nZone *\nZoneDialog::zone() {\n  _myZone->setName(zoneName->text());\n\n  Zone *zone = _myZone;\n  if (_zone) {\n    _zone->copy(*_myZone);\n    zone = _zone;\n  } else {\n    _myZone->setParent(nullptr);\n  }\n\n  return zone;\n}\n"
  },
  {
    "path": "src/zonedialog.hh",
    "content": "#ifndef ZONEDIALOG_HH\n#define ZONEDIALOG_HH\n\n#include <QDialog>\n\n#include \"ui_zonedialog.h\"\n\nclass Config;\nclass Zone;\n\n\nclass ZoneDialog: public QDialog, private Ui::ZoneDialog\n{\n\tQ_OBJECT\n\npublic:\n\tZoneDialog(Config *config, QWidget *parent=nullptr);\n\tZoneDialog(Config *config, Zone *zone, QWidget *parent=nullptr);\n\n\tZone *zone();\n\nprotected slots:\n\tvoid onAddChannelA();\n\tvoid onRemChannelA();\n\n  void onAddChannelB();\n\tvoid onRemChannelB();\n\n  void onHideZoneHint();\n\nprotected:\n\tvoid construct();\n\nprotected:\n\tConfig *_config;\n  Zone *_myZone;\n  Zone *_zone;\n};\n\n\n#endif // ZONEDIALOG_HH\n"
  },
  {
    "path": "src/zonedialog.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ZoneDialog</class>\n <widget class=\"QDialog\" name=\"ZoneDialog\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>550</width>\n    <height>475</height>\n   </rect>\n  </property>\n  <property name=\"sizePolicy\">\n   <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"MinimumExpanding\">\n    <horstretch>0</horstretch>\n    <verstretch>0</verstretch>\n   </sizepolicy>\n  </property>\n  <property name=\"windowTitle\">\n   <string>Edit Zone</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"QTabWidget\" name=\"tabWidget\">\n     <property name=\"currentIndex\">\n      <number>0</number>\n     </property>\n     <widget class=\"QWidget\" name=\"tab\">\n      <attribute name=\"title\">\n       <string>Basic</string>\n      </attribute>\n      <layout class=\"QVBoxLayout\" name=\"verticalLayout_3\">\n       <item>\n        <widget class=\"QLabel\" name=\"zoneHint\">\n         <property name=\"styleSheet\">\n          <string notr=\"true\">padding:10px;border: 2px solid black; border-radius: 10px;</string>\n         </property>\n         <property name=\"text\">\n          <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;justify&quot;&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;Note:&lt;/span&gt; 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. &lt;/p&gt;&lt;p align=&quot;justify&quot;&gt;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;amp; B) and program them individually into the radio.&lt;/p&gt;&lt;p align=&quot;right&quot;&gt;&lt;a href=&quot;#hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Hide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>\n         </property>\n         <property name=\"textFormat\">\n          <enum>Qt::RichText</enum>\n         </property>\n         <property name=\"wordWrap\">\n          <bool>true</bool>\n         </property>\n        </widget>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout\">\n         <item>\n          <widget class=\"QLabel\" name=\"label\">\n           <property name=\"text\">\n            <string>Name</string>\n           </property>\n           <property name=\"buddy\">\n            <cstring>zoneName</cstring>\n           </property>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QLineEdit\" name=\"zoneName\"/>\n         </item>\n        </layout>\n       </item>\n       <item>\n        <layout class=\"QHBoxLayout\" name=\"horizontalLayout_2\">\n         <item>\n          <widget class=\"QGroupBox\" name=\"groupBox\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"title\">\n            <string>Channels A</string>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_2\">\n            <item>\n             <widget class=\"ConfigObjectListView\" name=\"listAView\" native=\"true\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout_4\">\n              <item>\n               <widget class=\"QPushButton\" name=\"addChannelA\">\n                <property name=\"text\">\n                 <string>add</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"remChannelA\">\n                <property name=\"text\">\n                 <string>remove</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </widget>\n         </item>\n         <item>\n          <widget class=\"QGroupBox\" name=\"groupBox_2\">\n           <property name=\"sizePolicy\">\n            <sizepolicy hsizetype=\"MinimumExpanding\" vsizetype=\"Preferred\">\n             <horstretch>0</horstretch>\n             <verstretch>0</verstretch>\n            </sizepolicy>\n           </property>\n           <property name=\"title\">\n            <string>Channels B</string>\n           </property>\n           <layout class=\"QVBoxLayout\" name=\"verticalLayout_4\">\n            <item>\n             <widget class=\"ConfigObjectListView\" name=\"listBView\" native=\"true\">\n              <property name=\"sizePolicy\">\n               <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n                <horstretch>0</horstretch>\n                <verstretch>0</verstretch>\n               </sizepolicy>\n              </property>\n             </widget>\n            </item>\n            <item>\n             <layout class=\"QHBoxLayout\" name=\"horizontalLayout_5\">\n              <item>\n               <widget class=\"QPushButton\" name=\"addChannelB\">\n                <property name=\"text\">\n                 <string>add</string>\n                </property>\n               </widget>\n              </item>\n              <item>\n               <widget class=\"QPushButton\" name=\"remChannelB\">\n                <property name=\"text\">\n                 <string>remove</string>\n                </property>\n               </widget>\n              </item>\n             </layout>\n            </item>\n           </layout>\n          </widget>\n         </item>\n        </layout>\n       </item>\n      </layout>\n     </widget>\n     <widget class=\"ExtensionView\" name=\"extensionView\">\n      <attribute name=\"title\">\n       <string>Extension</string>\n      </attribute>\n     </widget>\n    </widget>\n   </item>\n   <item>\n    <widget class=\"QDialogButtonBox\" name=\"buttonBox\">\n     <property name=\"orientation\">\n      <enum>Qt::Horizontal</enum>\n     </property>\n     <property name=\"standardButtons\">\n      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>\n     </property>\n    </widget>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ExtensionView</class>\n   <extends>QWidget</extends>\n   <header>extensionview.hh</header>\n   <container>1</container>\n  </customwidget>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>accepted()</signal>\n   <receiver>ZoneDialog</receiver>\n   <slot>accept()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>248</x>\n     <y>254</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>157</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n  <connection>\n   <sender>buttonBox</sender>\n   <signal>rejected()</signal>\n   <receiver>ZoneDialog</receiver>\n   <slot>reject()</slot>\n   <hints>\n    <hint type=\"sourcelabel\">\n     <x>316</x>\n     <y>260</y>\n    </hint>\n    <hint type=\"destinationlabel\">\n     <x>286</x>\n     <y>274</y>\n    </hint>\n   </hints>\n  </connection>\n </connections>\n</ui>\n"
  },
  {
    "path": "src/zonelistview.cc",
    "content": "#include \"zonelistview.hh\"\n#include \"ui_zonelistview.h\"\n#include \"config.hh\"\n#include \"zonedialog.hh\"\n#include <QMessageBox>\n\n\nZoneListView::ZoneListView(Config *config, QWidget *parent)\n  : QWidget(parent), ui(new Ui::ZoneListView), _config(config)\n{\n  ui->setupUi(this);\n\n  ui->listView->setModel(new ZoneListWrapper(_config->zones(), ui->listView));\n  connect(ui->addZone, SIGNAL(clicked()), this, SLOT(onAddZone()));\n  connect(ui->remZone, SIGNAL(clicked()), this, SLOT(onRemZone()));\n  connect(ui->listView, SIGNAL(doubleClicked(unsigned)), this, SLOT(onEditZone(unsigned)));\n}\n\nZoneListView::~ZoneListView() {\n  delete ui;\n}\n\nvoid\nZoneListView::onAddZone() {\n  ZoneDialog dialog(_config);\n\n  if (QDialog::Accepted != dialog.exec())\n    return;\n\n  int row=-1;\n  if (ui->listView->hasSelection())\n    row = ui->listView->selection().second+1;\n  _config->zones()->add(dialog.zone(), row);\n}\n\nvoid\nZoneListView::onRemZone() {\n  // Check if there is any zones selected\n  if (! ui->listView->hasSelection()) {\n    QMessageBox::information(\n          nullptr, tr(\"Cannot delete zone\"),\n          tr(\"Cannot delete zone: You have to select a zone first.\"));\n    return;\n  }\n\n  // Get selection and ask for deletion\n  QPair<int,int> rows = ui->listView->selection();\n  int rowcount = rows.second-rows.first+1;\n  if (rows.first == rows.second) {\n    QString name = _config->zones()->zone(rows.first)->name();\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete zone?\"), tr(\"Delete zone %1?\").arg(name)))\n      return;\n  } else {\n    if (QMessageBox::No == QMessageBox::question(\n          nullptr, tr(\"Delete zones?\"), tr(\"Delete %1 zones?\").arg(rowcount)))\n      return;\n  }\n\n  // collect all selected zones\n  // need to collect them first as rows change when deleting\n  QList<Zone *> lists; lists.reserve(rowcount);\n  for(int row=rows.first; row<=rows.second; row++)\n    lists.push_back(_config->zones()->zone(row));\n  // remove\n  foreach (Zone *zone, lists)\n    _config->zones()->del(zone);\n}\n\nvoid\nZoneListView::onEditZone(unsigned row) {\n  ZoneDialog dialog(_config, _config->zones()->zone(row));\n  if (QDialog::Accepted != dialog.exec())\n    return;\n  dialog.zone();\n}\n"
  },
  {
    "path": "src/zonelistview.hh",
    "content": "#ifndef ZONELISTVIEW_HH\n#define ZONELISTVIEW_HH\n\n#include <QWidget>\n\nclass Config;\nnamespace Ui {\n  class ZoneListView;\n}\n\nclass ZoneListView : public QWidget\n{\n  Q_OBJECT\n\npublic:\n  explicit ZoneListView(Config *config, QWidget *parent = nullptr);\n  ~ZoneListView();\n\nprotected slots:\n  void onAddZone();\n  void onRemZone();\n  void onEditZone(unsigned row);\n\nprivate:\n  Ui::ZoneListView *ui;\n  Config *_config;\n};\n\n#endif // ZONELISTVIEW_HH\n"
  },
  {
    "path": "src/zonelistview.ui",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ui version=\"4.0\">\n <class>ZoneListView</class>\n <widget class=\"QWidget\" name=\"ZoneListView\">\n  <property name=\"geometry\">\n   <rect>\n    <x>0</x>\n    <y>0</y>\n    <width>400</width>\n    <height>300</height>\n   </rect>\n  </property>\n  <property name=\"windowTitle\">\n   <string notr=\"true\">Form</string>\n  </property>\n  <layout class=\"QVBoxLayout\" name=\"verticalLayout\">\n   <item>\n    <widget class=\"ConfigObjectListView\" name=\"listView\" native=\"true\">\n     <property name=\"sizePolicy\">\n      <sizepolicy hsizetype=\"Preferred\" vsizetype=\"MinimumExpanding\">\n       <horstretch>0</horstretch>\n       <verstretch>0</verstretch>\n      </sizepolicy>\n     </property>\n    </widget>\n   </item>\n   <item>\n    <layout class=\"QHBoxLayout\" name=\"horizontalLayout_8\">\n     <item>\n      <widget class=\"QPushButton\" name=\"addZone\">\n       <property name=\"text\">\n        <string>Add Zone</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt++</string>\n       </property>\n      </widget>\n     </item>\n     <item>\n      <widget class=\"QPushButton\" name=\"remZone\">\n       <property name=\"text\">\n        <string>Delete Zone</string>\n       </property>\n       <property name=\"shortcut\">\n        <string>Alt+-</string>\n       </property>\n      </widget>\n     </item>\n    </layout>\n   </item>\n  </layout>\n </widget>\n <customwidgets>\n  <customwidget>\n   <class>ConfigObjectListView</class>\n   <extends>QWidget</extends>\n   <header>configobjectlistview.hh</header>\n   <container>1</container>\n  </customwidget>\n </customwidgets>\n <resources/>\n <connections/>\n</ui>\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "\nqt_add_resources(TESTDATA resources.qrc)\n\nqt_add_library(libdmrconfigtest STATIC libdmrconfigtest.cc libdmrconfigtest.hh ${TESTDATA})\ntarget_link_libraries(libdmrconfigtest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf ${ADDITIONAL_LIBS})\n\n\nqt_add_executable(configtest configtest.cc configtest.hh ${TESTDATA})\ntarget_link_libraries(configtest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(tableformattest tableformattest.cc tableformattest.hh ${TESTDATA})\ntarget_link_libraries(tableformattest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(copytest copytest.cc copytest.hh ${TESTDATA})\ntarget_link_libraries(copytest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(labeltest labeltest.cc labeltest.hh ${TESTDATA})\ntarget_link_libraries(labeltest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(trafotest trafotest.cc trafotest.hh ${TESTDATA})\ntarget_link_libraries(trafotest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(crc32test crc32test.cc crc32test.hh ${TESTDATA})\ntarget_link_libraries(crc32test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(utilstest utilstest.cc utilstest.hh ${TESTDATA})\ntarget_link_libraries(utilstest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(chirptest chirptest.cc chirptest.hh ${TESTDATA})\ntarget_link_libraries(chirptest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(mergetest mergetest.cc mergetest.hh ${TESTDATA})\ntarget_link_libraries(mergetest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(smstemplatetest smstemplatetest.cc smstemplatetest.hh ${TESTDATA})\ntarget_link_libraries(smstemplatetest PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\n# Unit tests for Radioddity devices\nqt_add_executable(rd5r_test rd5r_test.cc rd5r_test.hh ${TESTDATA})\ntarget_link_libraries(rd5r_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(gd73_test gd73_test.cc gd73_test.hh ${TESTDATA})\ntarget_link_libraries(gd73_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(gd77_test gd77_test.cc gd77_test.hh ${TESTDATA})\ntarget_link_libraries(gd77_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\n# Unit tests for OpenGD77 firmware\nqt_add_executable(opengd77_test opengd77_test.cc opengd77_test.hh ${TESTDATA})\ntarget_link_libraries(opengd77_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n# Unit tests for OpenGD77 firmware\nqt_add_executable(openuv380_test openuv380_test.cc openuv380_test.hh ${TESTDATA})\ntarget_link_libraries(openuv380_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\n# Unit tests for TyT devices\nqt_add_executable(md390_test md390_test.cc md390_test.hh ${TESTDATA})\ntarget_link_libraries(md390_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(uv390_test uv390_test.cc uv390_test.hh ${TESTDATA})\ntarget_link_libraries(uv390_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(md2017_test md2017_test.cc md2017_test.hh ${TESTDATA})\ntarget_link_libraries(md2017_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\n# Unit tests for AnyTone devices\nqt_add_executable(d868uve_test d868uve_test.cc d868uve_test.hh ${TESTDATA})\ntarget_link_libraries(d868uve_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(d878uv_test d878uv_test.cc d878uv_test.hh ${TESTDATA})\ntarget_link_libraries(d878uv_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(d878uv2_test d878uv2_test.cc d878uv2_test.hh ${TESTDATA})\ntarget_link_libraries(d878uv2_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(d578uv_test d578uv_test.cc d578uv_test.hh ${TESTDATA})\ntarget_link_libraries(d578uv_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(d168uv_test d168uv_test.cc d168uv_test.hh ${TESTDATA})\ntarget_link_libraries(d168uv_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\n# Unit tests for Baofeng devices\nqt_add_executable(dmr6x2uv_test dmr6x2uv_test.cc dmr6x2uv_test.hh ${TESTDATA})\ntarget_link_libraries(dmr6x2uv_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(dmr6x2uv2_test dmr6x2uv2_test.cc dmr6x2uv2_test.hh ${TESTDATA})\ntarget_link_libraries(dmr6x2uv2_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(dm1701_test dm1701_test.cc dm1701_test.hh ${TESTDATA})\ntarget_link_libraries(dm1701_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(dr1801_test dr1801_test.cc dr1801_test.hh ${TESTDATA})\ntarget_link_libraries(dr1801_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\nqt_add_executable(dm32uv_test dm32uv_test.cc dm32uv_test.hh ${TESTDATA})\ntarget_link_libraries(dm32uv_test PRIVATE Qt6::Core Qt6::Network Qt6::Positioning Qt6::SerialPort Qt6::Test ${YAMLCPP_LIBRARIES} libdmrconf libdmrconfigtest ${ADDITIONAL_LIBS})\n\n\nadd_test(NAME Config    COMMAND configtest)\nadd_test(NAME TableFormat COMMAND tableformattest)\nadd_test(NAME Copy      COMMAND copytest)\nadd_test(NAME Label     COMMAND labeltest)\nadd_test(NAME Transformations COMMAND trafotest)\nadd_test(NAME CRC32     COMMAND crc32test)\nadd_test(NAME Utils     COMMAND utilstest)\nadd_test(NAME CHIRP     COMMAND chirptest)\nadd_test(NAME Merge     COMMAND mergetest)\nadd_test(NAME SMSTemplates COMMAND smstemplatetest)\n\nadd_test(NAME RD5R      COMMAND rd5r_test)\nadd_test(NAME GD73      COMMAND gd73_test)\nadd_test(NAME GD77      COMMAND gd77_test)\n\nadd_test(NAME OpenGD77  COMMAND opengd77_test)\nadd_test(NAME OpenUV380 COMMAND openuv380_test)\n\nadd_test(NAME MD390     COMMAND md390_test)\nadd_test(NAME UV390     COMMAND uv390_test)\nadd_test(NAME MD2017    COMMAND md2017_test)\n\nadd_test(NAME D868UVE   COMMAND d868uve_test)\nadd_test(NAME D878UV    COMMAND d878uv_test)\nadd_test(NAME D878UV2   COMMAND d878uv2_test)\nadd_test(NAME D578UV    COMMAND d578uv_test)\n\nadd_test(NAME DMR6X2UV  COMMAND dmr6x2uv_test)\nadd_test(NAME DMR6X2UV2 COMMAND dmr6x2uv2_test)\nadd_test(NAME DM1701    COMMAND dm1701_test)\nadd_test(NAME DR1801    COMMAND dr1801_test)\nadd_test(NAME DM32UV    COMMAND dm32uv_test)\n\n"
  },
  {
    "path": "test/chirptest.cc",
    "content": "#include \"chirptest.hh\"\n\n#include <QTest>\n#include \"chirpformat.hh\"\n#include \"config.hh\"\n\n\nChirpTest::ChirpTest(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\n\nvoid\nChirpTest::testReaderBasic() {\n\n  QFile file(\":/data/chirp_simple.csv\");\n  if (! file.open(QIODevice::ReadOnly)) {\n    QFAIL(\"Cannot open CHIRP file.\");\n  }\n\n  QTextStream stream(&file);\n  Config config;\n  ErrorStack err;\n\n  if (! ChirpReader::read(stream, &config, err)) {\n    QFAIL(QString(\"Cannot read codeplug file:\\n%1\").arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 3);\n  QCOMPARE(config.channelList()->channel(0)->name(), \"KD8BMI\");\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency().inMHz(), 147.075000);\n  QCOMPARE(config.channelList()->channel(0)->txFrequency().inMHz(), 147.675000);\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->txTone(), SelectiveCall(103.5));\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->rxTone(), SelectiveCall());\n  QCOMPARE(config.channelList()->channel(1)->rxFrequency().inMHz(), 146.760000);\n  QCOMPARE(config.channelList()->channel(1)->txFrequency().inMHz(), 146.160000);\n}\n\n\nvoid\nChirpTest::testReaderCTCSS() {\n\n  QFile file(\":/data/chirp_ctcss.csv\");\n  if (! file.open(QIODevice::ReadOnly)) {\n    QFAIL(\"Cannot open CHIRP file.\");\n  }\n\n  QTextStream stream(&file);\n  Config config;\n  ErrorStack err;\n\n  if (! ChirpReader::read(stream, &config, err)) {\n    QFAIL(QString(\"Cannot read codeplug file:\\n%1\").arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 3);\n\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->txTone().format(), SelectiveCall().format());\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->rxTone(), SelectiveCall());\n\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->txTone(), SelectiveCall(67.0));\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->rxTone(), SelectiveCall());\n\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->txTone(), SelectiveCall(77.0));\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->rxTone(), SelectiveCall(77.0));\n}\n\n\nvoid\nChirpTest::testReaderDCS() {\n\n  QFile file(\":/data/chirp_dcs.csv\");\n  if (! file.open(QIODevice::ReadOnly)) {\n    QFAIL(\"Cannot open CHIRP file.\");\n  }\n\n  QTextStream stream(&file);\n  Config config;\n  ErrorStack err;\n\n  if (! ChirpReader::read(stream, &config, err)) {\n    QFAIL(QString(\"Cannot read codeplug file:\\n%1\").arg(err.format()).toStdString().c_str());\n  }\n  SelectiveCall cmp(23, false);\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->txTone().format(), cmp.format());\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->rxTone(), SelectiveCall(23, false));\n\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->txTone(), SelectiveCall(23, false));\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->rxTone(), SelectiveCall(23, true));\n\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->txTone(), SelectiveCall(23, true));\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->rxTone(), SelectiveCall(23, false));\n\n  QCOMPARE(config.channelList()->channel(3)->as<FMChannel>()->txTone(), SelectiveCall(23, true));\n  QCOMPARE(config.channelList()->channel(3)->as<FMChannel>()->rxTone(), SelectiveCall(23, true));\n}\n\n\nvoid\nChirpTest::testReaderCross()\n{\n  QFile file(\":/data/chirp_cross.csv\");\n  if (! file.open(QIODevice::ReadOnly)) {\n    QFAIL(\"Cannot open CHIRP file.\");\n  }\n\n  QTextStream stream(&file);\n  Config config;\n  ErrorStack err;\n\n  if (! ChirpReader::read(stream, &config, err)) {\n    QFAIL(QString(\"Cannot read codeplug file:\\n%1\").arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 8);\n\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->txTone(), SelectiveCall());\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->rxTone(), SelectiveCall(67.0));\n\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->txTone(), SelectiveCall());\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->rxTone().format(), SelectiveCall(23, false).format());\n\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->txTone(), SelectiveCall(67.0));\n  QCOMPARE(config.channelList()->channel(2)->as<FMChannel>()->rxTone(), SelectiveCall());\n\n  QCOMPARE(config.channelList()->channel(3)->as<FMChannel>()->txTone(), SelectiveCall(67.0));\n  QCOMPARE(config.channelList()->channel(3)->as<FMChannel>()->rxTone(), SelectiveCall(77.0));\n\n  QCOMPARE(config.channelList()->channel(4)->as<FMChannel>()->txTone(), SelectiveCall(67.0));\n  QCOMPARE(config.channelList()->channel(4)->as<FMChannel>()->rxTone(), SelectiveCall(23, false));\n\n  QCOMPARE(config.channelList()->channel(5)->as<FMChannel>()->txTone(), SelectiveCall(23, false));\n  QCOMPARE(config.channelList()->channel(5)->as<FMChannel>()->rxTone(), SelectiveCall());\n\n  QCOMPARE(config.channelList()->channel(6)->as<FMChannel>()->txTone(), SelectiveCall(23, false));\n  QCOMPARE(config.channelList()->channel(6)->as<FMChannel>()->rxTone(), SelectiveCall(67.0));\n\n  QCOMPARE(config.channelList()->channel(7)->as<FMChannel>()->txTone(), SelectiveCall(23, false));\n  QCOMPARE(config.channelList()->channel(7)->as<FMChannel>()->rxTone(), SelectiveCall(32, false));\n}\n\n\nvoid\nChirpTest::testReaderBandwidth() {\n\n  QFile file(\":/data/chirp_bandwidth.csv\");\n  if (! file.open(QIODevice::ReadOnly)) {\n    QFAIL(\"Cannot open CHIRP file.\");\n  }\n\n  QTextStream stream(&file);\n  Config config;\n  ErrorStack err;\n\n  if (! ChirpReader::read(stream, &config, err)) {\n    QFAIL(QString(\"Cannot read codeplug file:\\n%1\").arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 2);\n  QVERIFY(config.channelList()->channel(0)->is<FMChannel>());\n  QCOMPARE(config.channelList()->channel(0)->as<FMChannel>()->bandwidth(), FMChannel::Bandwidth::Wide);\n  QCOMPARE(config.channelList()->channel(1)->as<FMChannel>()->bandwidth(), FMChannel::Bandwidth::Narrow);\n}\n\n\nvoid\nChirpTest::testWriterBasic() {\n  Config orig;\n\n  FMChannel *fm0 = new FMChannel();\n  fm0->setName(\"DB0SP\"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0));\n  orig.channelList()->add(fm0);\n\n  FMChannel *fm1 = new FMChannel();\n  fm1->setName(\"DB0SP\"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(144.4));\n  orig.channelList()->add(fm1);\n\n  QString csv;\n  QTextStream stream(&csv);\n  ErrorStack err;\n  if (! ChirpWriter::write(stream, &orig, err))\n    QFAIL(QString(\"Cannot serialize codeplug:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  Config parsed;\n  if (! ChirpReader::read(stream, &parsed, err))\n    QFAIL(QString(\"Cannot parse CHIRP CSV:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  QCOMPARE(parsed.channelList()->count(), orig.channelList()->count());\n  QCOMPARE(parsed.channelList()->channel(0)->name(), orig.channelList()->channel(0)->name());\n  QCOMPARE(parsed.channelList()->channel(0)->rxFrequency(), orig.channelList()->channel(0)->rxFrequency());\n  QCOMPARE(parsed.channelList()->channel(0)->txFrequency(), orig.channelList()->channel(0)->txFrequency());\n\n  QCOMPARE(parsed.channelList()->channel(1)->rxFrequency(), orig.channelList()->channel(1)->rxFrequency());\n  QCOMPARE(parsed.channelList()->channel(1)->txFrequency(), orig.channelList()->channel(1)->txFrequency());\n}\n\n\nvoid\nChirpTest::testWriterCTCSS() {\n  Config orig;\n\n  FMChannel *fm0 = new FMChannel();\n  fm0->setName(\"DB0SP\"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0));\n  fm0->setTXTone(SelectiveCall(67.0));\n  orig.channelList()->add(fm0);\n\n  FMChannel *fm1 = new FMChannel();\n  fm1->setName(\"DB0SP\"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0));\n  fm1->setTXTone(SelectiveCall(67.0));\n  fm1->setRXTone(SelectiveCall(67.0));\n  orig.channelList()->add(fm1);\n\n  FMChannel *fm2 = new FMChannel();\n  fm2->setName(\"DB0SP\"); fm2->setRXFrequency(Frequency::fromMHz(145.6)); fm2->setTXFrequency(Frequency::fromMHz(145.0));\n  fm2->setTXTone(SelectiveCall(67.0));\n  fm2->setRXTone(SelectiveCall(77.0));\n  orig.channelList()->add(fm2);\n\n  QString csv;\n  QTextStream stream(&csv);\n  ErrorStack err;\n  if (! ChirpWriter::write(stream, &orig, err))\n    QFAIL(QString(\"Cannot serialize codeplug:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  Config parsed;\n  if (! ChirpReader::read(stream, &parsed, err))\n    QFAIL(QString(\"Cannot parse CHIRP CSV:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  FMChannel *pfm1 = parsed.channelList()->channel(0)->as<FMChannel>();\n  QCOMPARE(pfm1->txTone().format(), fm0->txTone().format());\n\n  FMChannel *pfm2 = parsed.channelList()->channel(1)->as<FMChannel>();\n  QCOMPARE(pfm2->txTone(), fm1->txTone());\n  QCOMPARE(pfm2->rxTone(), fm1->rxTone());\n\n  FMChannel *pfm3 = parsed.channelList()->channel(2)->as<FMChannel>();\n  QCOMPARE(pfm3->txTone(), fm2->txTone());\n  QCOMPARE(pfm3->rxTone(), fm2->rxTone());\n}\n\n\nvoid\nChirpTest::testWriterDCS() {\n  Config orig;\n\n  FMChannel *fm0 = new FMChannel();\n  fm0->setName(\"DB0SP\"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0));\n  fm0->setTXTone(SelectiveCall(23, false));\n  fm0->setRXTone(SelectiveCall(23, false));\n  orig.channelList()->add(fm0);\n\n  FMChannel *fm1 = new FMChannel();\n  fm1->setName(\"DB0SP\"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0));\n  fm1->setTXTone(SelectiveCall(23, false));\n  fm1->setRXTone(SelectiveCall(23, true));\n  orig.channelList()->add(fm1);\n\n  FMChannel *fm2 = new FMChannel();\n  fm2->setName(\"DB0SP\"); fm2->setRXFrequency(Frequency::fromMHz(145.6)); fm2->setTXFrequency(Frequency::fromMHz(145.0));\n  fm2->setTXTone(SelectiveCall(23, false));\n  fm2->setRXTone(SelectiveCall(77.0));\n  orig.channelList()->add(fm2);\n\n  FMChannel *fm3 = new FMChannel();\n  fm3->setName(\"DB0SP\"); fm3->setRXFrequency(Frequency::fromMHz(145.6)); fm3->setTXFrequency(Frequency::fromMHz(145.0));\n  fm3->setTXTone(SelectiveCall(23, false));\n  fm3->setRXTone(SelectiveCall(32, false));\n  orig.channelList()->add(fm3);\n\n  QString csv;\n  QTextStream stream(&csv);\n  ErrorStack err;\n  if (! ChirpWriter::write(stream, &orig, err))\n    QFAIL(QString(\"Cannot serialize codeplug:\\n%1\").arg(err.format()).toStdString().c_str());\n  qDebug() << csv;\n\n  Config parsed;\n  if (! ChirpReader::read(stream, &parsed, err))\n    QFAIL(QString(\"Cannot parse CHIRP CSV:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  FMChannel *pfm0 = parsed.channelList()->channel(0)->as<FMChannel>();\n  QCOMPARE(pfm0->txTone(), fm0->txTone());\n  QCOMPARE(pfm0->rxTone(), fm0->rxTone());\n\n  FMChannel *pfm1 = parsed.channelList()->channel(1)->as<FMChannel>();\n  QCOMPARE(pfm1->txTone(), fm1->txTone());\n  QCOMPARE(pfm1->rxTone(), fm1->rxTone());\n\n  FMChannel *pfm2 = parsed.channelList()->channel(2)->as<FMChannel>();\n  QCOMPARE(pfm2->txTone(), fm2->txTone());\n  QCOMPARE(pfm2->rxTone(), fm2->rxTone());\n\n  FMChannel *pfm3 = parsed.channelList()->channel(3)->as<FMChannel>();\n  QCOMPARE(pfm3->txTone(), fm3->txTone());\n  QCOMPARE(pfm3->rxTone(), fm3->rxTone());\n}\n\n\nvoid\nChirpTest::testWriterCross() {\n\n}\n\n\nvoid\nChirpTest::testWriterBandwidth() {\n  Config orig;\n\n  FMChannel *fm0 = new FMChannel();\n  fm0->setName(\"DB0SP\"); fm0->setRXFrequency(Frequency::fromMHz(145.6)); fm0->setTXFrequency(Frequency::fromMHz(145.0));\n  fm0->setBandwidth(FMChannel::Bandwidth::Wide);\n  orig.channelList()->add(fm0);\n\n  FMChannel *fm1 = new FMChannel();\n  fm1->setName(\"DB0SP\"); fm1->setRXFrequency(Frequency::fromMHz(145.6)); fm1->setTXFrequency(Frequency::fromMHz(145.0));\n  fm1->setBandwidth(FMChannel::Bandwidth::Narrow);\n  orig.channelList()->add(fm1);\n\n  QString csv;\n  QTextStream stream(&csv);\n  ErrorStack err;\n  if (! ChirpWriter::write(stream, &orig, err))\n    QFAIL(QString(\"Cannot serialize codeplug:\\n%1\").arg(err.format()).toStdString().c_str());\n  qDebug() << csv;\n\n  Config parsed;\n  if (! ChirpReader::read(stream, &parsed, err))\n    QFAIL(QString(\"Cannot parse CHIRP CSV:\\n%1\").arg(err.format()).toStdString().c_str());\n\n  FMChannel *pfm0 = parsed.channelList()->channel(0)->as<FMChannel>();\n  QCOMPARE(pfm0->bandwidth(), fm0->bandwidth());\n\n  FMChannel *pfm1 = parsed.channelList()->channel(1)->as<FMChannel>();\n  QCOMPARE(pfm1->bandwidth(), fm1->bandwidth());\n}\n\n\nQTEST_GUILESS_MAIN(ChirpTest)\n"
  },
  {
    "path": "test/chirptest.hh",
    "content": "#ifndef CHIRPTEST_HH\n#define CHIRPTEST_HH\n\n#include <QObject>\n\nclass ChirpTest : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit ChirpTest(QObject *parent = nullptr);\n\nprivate slots:\n  void testReaderBasic();\n  void testReaderCTCSS();\n  void testReaderDCS();\n  void testReaderCross();\n  void testReaderBandwidth();\n\n  void testWriterBasic();\n  void testWriterCTCSS();\n  void testWriterDCS();\n  void testWriterCross();\n  void testWriterBandwidth();\n};\n\n#endif // CHIRPTEST_HH\n"
  },
  {
    "path": "test/configtest.cc",
    "content": "#include \"configtest.hh\"\n#include \"config.hh\"\n#include \"gnsssettings.hh\"\n#include \"errorstack.hh\"\n#include \"melody.hh\"\n#include <iostream>\n#include <QTest>\n#include \"logger.hh\"\n#include <iostream>\n#include \"gd73_limits.hh\"\n\n#include \"configcopyvisitor.hh\"\n\nConfigTest::ConfigTest(QObject *parent)\n  : UnitTestBase(parent), _stderr(stderr)\n{\n  Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG));\n}\n\n\nvoid\nConfigTest::initTestCase() {\n  UnitTestBase::initTestCase();\n\n  ErrorStack err;\n  if (! _ctcssCopyTest.readYAML(\":/data/ctcss_copy_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nConfigTest::testImmediateRefInvalidation() {\n  ErrorStack err;\n  Config *copy = ConfigCopy::copy(&_basicConfig, err)->as<Config>();\n  if (nullptr == copy)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(_basicConfig.posSystems()->count(), 1);\n  QVERIFY(_basicConfig.channelList()->get(1));\n  QVERIFY(_basicConfig.channelList()->get(1)->is<DMRChannel>());\n  QCOMPARE(_basicConfig.channelList()->get(1)->as<DMRChannel>()->aprs(),\n           _basicConfig.posSystems()->get(0)->as<DMRAPRSSystem>());\n\n  // Delete DMR APRS system and check if reference to it (channel 2) is removed as well.\n  QVERIFY(copy->posSystems()->del(copy->posSystems()->get(0)));\n  QCOMPARE(copy->posSystems()->count(), 0);\n  QCOMPARE(copy->channelList()->get(1)->as<DMRChannel>()->aprs(), nullptr);\n\n  QVERIFY(_basicConfig.channelList()->get(2));\n  QCOMPARE(_basicConfig.zones()->get(0)->as<Zone>()->B()->count(), 1);\n  QCOMPARE(_basicConfig.zones()->get(0)->as<Zone>()->B()->get(0),\n           _basicConfig.channelList()->get(2));\n\n  // Delete channel 3, check if zone 1, list B is empty\n  copy->channelList()->del(copy->channelList()->get(2));\n  QCOMPARE(copy->zones()->get(0)->as<Zone>()->B()->count(), 0);\n}\n\n\nvoid\nConfigTest::testCloneChannelBasic() {\n  // Check if a channel can be cloned\n  Channel *clone = _basicConfig.channelList()->channel(0)->clone()->as<Channel>();\n  // Check if channels are the same\n  QCOMPARE(clone->compare(*_basicConfig.channelList()->channel(0)), 0);\n}\n\nvoid\nConfigTest::testCloneChannelCTCSS() {\n  // Check if a channel can be cloned\n  QHash<ConfigObject*, ConfigObject*> table;\n  ConfigCloneVisitor visitor(table);\n  ErrorStack err;\n  if (! visitor.processItem(_ctcssCopyTest.channelList()->channel(0)))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  Channel *clone = visitor.takeResult(err)->as<Channel>();\n  if (nullptr == clone)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  // Check if channels are the same\n  QCOMPARE(clone->compare(*_ctcssCopyTest.channelList()->channel(0)), 0);\n  QCOMPARE(clone->as<FMChannel>()->txTone(),\n           _ctcssCopyTest.channelList()->channel(0)->as<FMChannel>()->txTone());\n  QCOMPARE(clone->as<FMChannel>()->rxTone(),\n           _ctcssCopyTest.channelList()->channel(0)->as<FMChannel>()->rxTone());\n}\n\nvoid\nConfigTest::testMultipleRadioIDs() {\n  ErrorStack err;\n  Config multipleRadioIds;\n  if (! multipleRadioIds.readYAML(\":/data/audio_settings_extension.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config::Context ctx;\n  if (! multipleRadioIds.label(ctx, err)) {\n    QFAIL(QString(\"Cannot label codeplug: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  YAML::Node node = multipleRadioIds.serialize(ctx, err);\n  if (node.IsNull()) {\n    QFAIL(QString(\"Cannot serialize codeplug: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nConfigTest::testMelodyLilypond() {\n  QString lilypond = \"a8 b e2 cis4 d\";\n  Melody melody; melody.fromLilypond(lilypond);\n  QString serialized = melody.toLilypond();\n  QCOMPARE(serialized, lilypond);\n\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  QCOMPARE(std::round(tones[0].first*100), 44000); QCOMPARE(tones[0].second, 300);\n  QCOMPARE(std::round(tones[1].first*100), 49388); QCOMPARE(tones[1].second, 300);\n  QCOMPARE(std::round(tones[2].first*100), 32963); QCOMPARE(tones[2].second,1200);\n  QCOMPARE(std::round(tones[3].first*100), 27718); QCOMPARE(tones[3].second, 600);\n  QCOMPARE(std::round(tones[4].first*100), 29366); QCOMPARE(tones[4].second, 600);\n}\n\nvoid\nConfigTest::testMelodyEncoding() {\n  QString lilypond = \"a8 b e2 cis4 d\";\n  Melody melody; melody.fromLilypond(lilypond);\n\n  QVector<QPair<double, unsigned int>> tones = melody.toTones();\n  QCOMPARE(std::round(tones[0].first*100), 44000); QCOMPARE(tones[0].second, 300);\n  QCOMPARE(std::round(tones[1].first*100), 49388); QCOMPARE(tones[1].second, 300);\n  QCOMPARE(std::round(tones[2].first*100), 32963); QCOMPARE(tones[2].second,1200);\n  QCOMPARE(std::round(tones[3].first*100), 27718); QCOMPARE(tones[3].second, 600);\n  QCOMPARE(std::round(tones[4].first*100), 29366); QCOMPARE(tones[4].second, 600);\n}\n\nvoid\nConfigTest::testMelodyDecoding() {\n  QString lilypond = \"a8 b e2 cis4 d\";\n  QVector<QPair<double, unsigned int>> tones = {\n    {440.00, 300}, {493.88, 300}, {329.63, 1200}, {277.18, 600}, {293.66, 600}\n  };\n\n  Melody melody; melody.infer(tones);\n  QCOMPARE(melody.toLilypond(), lilypond);\n  // infer() should also infer a BPM of 100\n  QCOMPARE(melody.bpm(), 100);\n}\n\n\nvoid\nConfigTest::testGNSSSettings() {\n  Config config; config.readYAML(\":/data/config_test.yaml\");\n  config.settings()->gnss()->setFixedPositionLocator(\"JO62jl45\");\n  config.settings()->gnss()->enableFixedPosition(true);\n  config.settings()->gnss()->setSystems(GNSSSettings::System::GPS | GNSSSettings::System::Beidou);\n\n  qDebug() << \"Before\" << config.settings()->gnss()->fixedPositionLocator();\n\n  QString buffer;\n  QTextStream stream(&buffer);\n  ErrorStack err;\n  if (! config.toYAML(stream, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  Config testConfig;\n  Config::Context ctx;\n  YAML::Node doc = YAML::Load(buffer.toStdString());\n  if (! testConfig.parse(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  if (! testConfig.link(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  qDebug() << \"After\" << testConfig.settings()->gnss()->fixedPositionLocator();\n\n  QCOMPARE(testConfig.settings()->gnss()->fixedPositionEnabled(), true);\n  QCOMPARE(testConfig.settings()->gnss()->fixedPositionLocator(), \"JO62jl45\");\n  QCOMPARE(testConfig.settings()->gnss()->systems(), GNSSSettings::System::GPS | GNSSSettings::System::Beidou);\n}\n\n\nvoid\nConfigTest::testAudioSettings() {\n  Config config; config.readYAML(\":/data/audio_settings_extension.yaml\");\n  QCOMPARE(config.settings()->audio()->micGain(), Level::fromValue(3));\n  QCOMPARE(config.settings()->audio()->fmMicGain(), Level::fromValue(6));\n\n  Config copyConfig;\n  QVERIFY(copyConfig.copy(config));\n  QCOMPARE(copyConfig.settings()->audio()->micGain(), config.settings()->audio()->micGain());\n  QCOMPARE(copyConfig.settings()->audio()->fmMicGain(), config.settings()->audio()->fmMicGain());\n}\n\n\nvoid\nConfigTest::testCTCSSNull() {\n  ErrorStack err;\n  Config ctcssConfig;\n  if (! ctcssConfig.readYAML(\":/data/ctcss_null_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Test backward compatibility\n  QVERIFY(ctcssConfig.channelList()->channel(0)->is<FMChannel>());\n  QCOMPARE(SelectiveCall() ,ctcssConfig.channelList()->channel(0)->as<FMChannel>()->rxTone());\n  QCOMPARE(SelectiveCall(67.0) ,ctcssConfig.channelList()->channel(0)->as<FMChannel>()->txTone());\n\n  // Test new format\n  QVERIFY(ctcssConfig.channelList()->channel(1)->is<FMChannel>());\n  QCOMPARE(SelectiveCall(37, true), ctcssConfig.channelList()->channel(1)->as<FMChannel>()->rxTone());\n  QCOMPARE(SelectiveCall(67.0) ,ctcssConfig.channelList()->channel(1)->as<FMChannel>()->txTone());\n}\n\n\nvoid\nConfigTest::testSelectiveCallDCS() {\n  ErrorStack err;\n\n  auto dcsFromOctal = SelectiveCall(0, false);\n  auto dcsFromBinary = SelectiveCall::fromBinaryDCS(0x0, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n000\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x0);\n  QCOMPARE(dcsFromOctal.octalCode(), 0);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(1, false);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x1, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n001\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x1);\n  QCOMPARE(dcsFromOctal.octalCode(), 1);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(7, false);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x7, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n007\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x7);\n  QCOMPARE(dcsFromOctal.octalCode(), 7);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(10, false);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x8, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n010\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x8);\n  QCOMPARE(dcsFromOctal.octalCode(), 10);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(42, false);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x22, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n042\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x22);\n  QCOMPARE(dcsFromOctal.octalCode(), 42);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(777, false);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x1ff, false);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"n777\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x1ff);\n  QCOMPARE(dcsFromOctal.octalCode(), 777);\n  QCOMPARE(dcsFromOctal.isInverted(), false);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(0, true);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x200 - 512, true);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"i000\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x0);\n  QCOMPARE(dcsFromOctal.octalCode(), 0);\n  QCOMPARE(dcsFromOctal.isInverted(), true);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(7, true);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x207 - 512, true);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"i007\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x7);\n  QCOMPARE(dcsFromOctal.octalCode(), 7);\n  QCOMPARE(dcsFromOctal.isInverted(), true);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(10, true);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x208 - 512, true);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"i010\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x8);\n  QCOMPARE(dcsFromOctal.octalCode(), 10);\n  QCOMPARE(dcsFromOctal.isInverted(), true);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(42, true);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x222 - 512, true);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"i042\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x22);\n  QCOMPARE(dcsFromOctal.octalCode(), 42);\n  QCOMPARE(dcsFromOctal.isInverted(), true);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n\n  dcsFromOctal = SelectiveCall(777, true);\n  dcsFromBinary = SelectiveCall::fromBinaryDCS(0x3ff - 512, true);\n  QVERIFY(dcsFromOctal.isDCS());\n  QCOMPARE(dcsFromOctal.format(), \"i777\");\n  QCOMPARE(dcsFromOctal.binCode(), 0x1ff);\n  QCOMPARE(dcsFromOctal.octalCode(), 777);\n  QCOMPARE(dcsFromOctal.isInverted(), true);\n  QCOMPARE(dcsFromOctal, dcsFromBinary);\n}\n\n\nvoid\nConfigTest::testDMRIdVerification() {\n  GD73Limits limits;\n  RadioLimitContext ctx;\n\n  ErrorStack err;\n  Config cfg;\n\n  if (! cfg.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  /*limits.verifyConfig(&cfg, ctx);\n  for (unsigned int i=0; i<ctx.count(); i++)\n    qDebug() << ctx.message(i).format();*/\n\n  QVERIFY(limits.verifyConfig(&cfg, ctx));\n  QVERIFY(ctx.maxSeverity() < RadioLimitIssue::Severity::Critical);\n\n  cfg.radioIDs()->get(0)->as<DMRRadioID>()->setNumber(0x12345678);\n\n  QVERIFY(!limits.verifyConfig(&cfg, ctx));\n  QVERIFY(ctx.maxSeverity() == RadioLimitIssue::Severity::Critical);\n}\n\n\nQTEST_GUILESS_MAIN(ConfigTest)\n\n"
  },
  {
    "path": "test/configtest.hh",
    "content": "#ifndef CONFIGTEST_HH\n#define CONFIGTEST_HH\n\n#include <QObject>\n#include \"libdmrconfigtest.hh\"\n#include \"config.hh\"\n\n\nclass ConfigTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit ConfigTest(QObject *parent = nullptr);\n\nprivate slots:\n  void initTestCase();\n\n  void testImmediateRefInvalidation();\n\n  void testCloneChannelBasic();\n  void testCloneChannelCTCSS();\n\n  /** Regression test for issue #388. */\n  void testMultipleRadioIDs();\n\n  void testMelodyLilypond();\n  void testMelodyEncoding();\n  void testMelodyDecoding(); \n\n  void testGNSSSettings();\n  void testAudioSettings();\n  \n  /** Regression test for #509. */\n  void testCTCSSNull();\n\n  void testSelectiveCallDCS();\n\n  /// Regression test #674.\n  void testDMRIdVerification();\n\nprotected:\n  QTextStream _stderr;\n  Config _ctcssCopyTest;\n};\n\n#endif // CONFIGTEST_HH\n"
  },
  {
    "path": "test/copytest.cc",
    "content": "#include \"copytest.hh\"\n#include \"configcopyvisitor.hh\"\n\n\nCopyTest::CopyTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nCopyTest::testChannelClone() {\n  QHash<ConfigObject *, ConfigObject *> map;\n\n  ErrorStack err;\n  ConfigCloneVisitor cloner(map);\n\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\"))\n    QFAIL(\"Cannot load config.\");\n  auto ch = config.channelList()->channel(0);\n\n  {\n    if (! cloner.processItem(ch, err))\n      QFAIL(err.format().toLocal8Bit().constData());\n\n    ConfigItem *item = cloner.takeResult(err);\n    QVERIFY(item);\n    QCOMPARE(ch->compare(*item), 0);\n    delete item;\n  }\n\n  {\n    ch->setPower(Channel::Power::Min);\n    ch->setTimeout(Interval::fromSeconds(60));\n    ch->setVOX(Level::fromValue(3));\n\n    if (! cloner.processItem(ch, err))\n      QFAIL(err.format().toLocal8Bit().constData());\n\n    ConfigItem *item = cloner.takeResult(err);\n    QVERIFY(item);\n    QVERIFY(item->is<Channel>());\n    QCOMPARE(item->as<Channel>()->power(), Channel::Power::Min);\n    QCOMPARE(item->as<Channel>()->timeout(), Interval::fromSeconds(60));\n    QCOMPARE(item->as<Channel>()->vox(), Level::fromValue(3));\n    delete item;\n  }\n\n  {\n    ch->setDefaultPower();\n    ch->setDefaultTimeout();\n    ch->setVOXDefault();\n\n    if (! cloner.processItem(ch, err))\n      QFAIL(err.format().toLocal8Bit().constData());\n\n    ConfigItem *item = cloner.takeResult(err);\n    QVERIFY(item);\n    QVERIFY(item->is<Channel>());\n    QVERIFY(item->as<Channel>()->defaultPower());\n    QVERIFY(item->as<Channel>()->defaultTimeout());\n    QVERIFY(item->as<Channel>()->defaultVOX());\n    delete item;\n  }\n}\n\n\nvoid\nCopyTest::testConfigClone() {\n  QHash<ConfigObject *, ConfigObject *> map;\n  ConfigCloneVisitor cloner(map);\n\n  ErrorStack err;\n  if (! cloner.process(&_basicConfig, err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n\n  ConfigItem *item = cloner.takeResult(err);\n  QVERIFY(item);\n\n  QCOMPARE(_basicConfig.compare(*item), 0);\n}\n\nvoid\nCopyTest::testConfigCopy() {\n  QHash<ConfigObject *, ConfigObject *> map;\n  ConfigCloneVisitor cloner(map);\n  FixReferencesVisistor fixer(map);\n\n  ErrorStack err;\n  if (! cloner.process(&_basicConfig, err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n\n  ConfigItem *item = cloner.takeResult(err);\n  QVERIFY(item);\n  QVERIFY(item->is<Config>());\n\n  if (! fixer.process(item->as<Config>(), err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n\n  QCOMPARE(_basicConfig.compare(*item), 0);\n}\n\nvoid\nCopyTest::testAPRSSystemCopy() {\n  // Regression test for issue #468.\n\n  QHash<ConfigObject *, ConfigObject *> map;\n  ConfigCloneVisitor cloner(map);\n  FixReferencesVisistor fixer(map);\n\n  Config config; ErrorStack err;\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\\n\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  if (! cloner.process(&config, err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n\n  ConfigItem *item = cloner.takeResult(err);\n  QVERIFY(item);\n  QVERIFY(item->is<Config>());\n\n  if (! fixer.process(item->as<Config>(), err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n  Config *comp_config = item->as<Config>();\n  QCOMPARE(config.compare(*item), 0);\n\n  QCOMPARE(config.posSystems()->count(), 1);\n  QCOMPARE(comp_config->posSystems()->count(), config.posSystems()->count());\n  QVERIFY(config.posSystems()->get(0)->is<FMAPRSSystem>());\n  QVERIFY(comp_config->posSystems()->get(0)->is<FMAPRSSystem>());\n\n  FMAPRSSystem *aprs = config.posSystems()->get(0)->as<FMAPRSSystem>(),\n      *comp_aprs = comp_config->posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(comp_aprs->name(), aprs->name());\n  QCOMPARE(comp_aprs->period(), aprs->period());\n  QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID());\n  QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID());\n  QCOMPARE(comp_aprs->path(), aprs->path());\n  QCOMPARE(comp_aprs->icon(), aprs->icon());\n  QCOMPARE(comp_aprs->message(), aprs->message());\n}\n\n\nQTEST_GUILESS_MAIN(CopyTest)\n"
  },
  {
    "path": "test/copytest.hh",
    "content": "#ifndef COPYTEST_HH\n#define COPYTEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\nclass CopyTest: public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit CopyTest(QObject *parent=nullptr);\n\nprivate slots:\n  void testChannelClone();\n  void testConfigClone();\n  void testConfigCopy();\n  void testAPRSSystemCopy();\n};\n\n#endif // COPYTEST_HH\n"
  },
  {
    "path": "test/crc32test.cc",
    "content": "#include \"crc32test.hh\"\n#include \"crc32.hh\"\n#include <QTest>\n\nCRC32Test::CRC32Test(QObject *parent) : QObject(parent)\n{\n  // pass...\n}\n\nvoid\nCRC32Test::testCRC32() {\n  QString txt(\"The quick brown fox jumps over the lazy dog\");\n  CRC32 crc;\n  crc.update(txt.toLocal8Bit());\n  QCOMPARE(crc.get(), 0x414FA339U^0xFFFFFFFF);\n}\n\nQTEST_GUILESS_MAIN(CRC32Test)\n"
  },
  {
    "path": "test/crc32test.hh",
    "content": "#ifndef CRC32TEST_H\n#define CRC32TEST_H\n\n#include <QObject>\n\nclass CRC32Test : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit CRC32Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testCRC32();\n};\n\n#endif // CRC32TEST_H\n"
  },
  {
    "path": "test/d168uv_test.cc",
    "content": "#include \"d168uv_test.hh\"\n#include \"logger.hh\"\n#include \"d168uv_codeplug.hh\"\n\n\nD168UVTest::D168UVTest(QObject *parent)\n  : UnitTestBase(parent), _stderr(stderr)\n{\n  Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG));\n}\n\nvoid\nD168UVTest::encodeDecode(Config &input, Config &output) {\n  ErrorStack err;\n  D168UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&input, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n  if (! codeplug.decode(&output, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nvoid\nD168UVTest::testFixedLocation() {\n  ErrorStack err;\n  Config decoded, config, reencoded; config.copy(_basicConfig);\n\n  config.settings()->gnss()->setFixedPositionLocator(\"JO62kk45\");\n  config.settings()->gnss()->enableFixedPosition(true);\n  encodeDecode(config, decoded);\n\n  QVERIFY(decoded.settings()->gnss()->fixedPositionEnabled());\n  QCOMPARE(decoded.settings()->gnss()->fixedPositionLocator(), QString(\"JO62kk45\"));\n\n  encodeDecode(decoded, reencoded);\n  QVERIFY(reencoded.settings()->gnss()->fixedPositionEnabled());\n  QCOMPARE(reencoded.settings()->gnss()->fixedPositionLocator(), QString(\"JO62kk45\"));\n}\n\n\nQTEST_GUILESS_MAIN(D168UVTest)\n\n"
  },
  {
    "path": "test/d168uv_test.hh",
    "content": "#ifndef D168UVTEST_HH\n#define D168UVTEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\n\nclass D168UVTest: public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit D168UVTest(QObject *parent=nullptr);\n\nprotected:\n  void encodeDecode(Config &input, Config &output);\n\nprivate slots:\n  void testFixedLocation();\n\nprotected:\n  QTextStream _stderr;\n};\n\n\n\n#endif // D168UVTEST_HH\n"
  },
  {
    "path": "test/d578uv_test.cc",
    "content": "#include \"d578uv_test.hh\"\n#include \"config.hh\"\n#include \"d578uv_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n\n\nD578UVTest::D578UVTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nD578UVTest::encodeDecode(Config &input, Config &output) {\n  ErrorStack err;\n  D578UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&input, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n  if (! codeplug.decode(&output, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nvoid\nD578UVTest::testBasicConfigEncoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D578UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD578UVTest::testBasicConfigDecoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D578UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D578UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD578UVTest::testChannelFrequency() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D578UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D578UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\nvoid\nD578UVTest::testChannelDataACK() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_channel_data_ack.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 2);\n  QVERIFY(config.channelList()->channel(0)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QVERIFY(config.channelList()->channel(1)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QCOMPARE(testConfig.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n}\n\n\nvoid\nD578UVTest::testSettingsDisplayVolumeChangePrompt() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_settings_display.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.settings()->anytoneExtension()->displaySettings()->volumeChangePromptEnabled(), false);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.settings()->anytoneExtension()->displaySettings()->volumeChangePromptEnabled(), false);\n}\n\nvoid\nD578UVTest::testMicGain() {\n  Config copy, config; config.copy(_basicConfig);\n  config.settings()->audio()->setMicGain(Level::fromValue(10));\n  encodeDecode(config, copy);\n  QCOMPARE(copy.settings()->audio()->micGain(), Level::fromValue(10));\n  QVERIFY(copy.settings()->anytoneExtension());\n  // FM mic gain enabled only if it differs from DMR gain\n  QVERIFY(! copy.settings()->audio()->fmMicGainEnabled());\n\n  QList<QPair<unsigned int,unsigned int>> pairs = {{1,1}, {2,1}, {3,1}, {4,3}, {5,3}, {6,5}, {7,5}, {8,7}, {9,7}, {10,10}};\n  for (auto pair: pairs) {\n    config.settings()->audio()->setMicGain(Level::fromValue(pair.first));\n    encodeDecode(config, copy);\n    QCOMPARE(copy.settings()->audio()->micGain(), Level::fromValue(pair.second));\n  }\n}\n\n\nvoid\nD578UVTest::testSettingsRoamingNotificationCount() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_settings_roaming.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.settings()->anytoneExtension()->roamingSettings()->notificationCount(), 1);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D578UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.settings()->anytoneExtension()->roamingSettings()->notificationCount(), 1);\n}\n\nvoid\nD578UVTest::testIdleToneEnable() {\n  ErrorStack err;\n  Config config, testConfig;\n\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  config.settings()->tone()->setChannelIdle(Channel::Type::FM);\n  encodeDecode(config, testConfig);\n  QCOMPARE(testConfig.settings()->tone()->channelIdle(), Channel::Type::FM);\n\n  config.settings()->tone()->setChannelIdle(Channel::Type::DMR);\n  encodeDecode(config, testConfig);\n  QCOMPARE(testConfig.settings()->tone()->channelIdle(), Channel::Type::DMR);\n\n  config.settings()->tone()->setChannelIdle(Channel::Type::FM|Channel::Type::DMR);\n  encodeDecode(config, testConfig);\n  QCOMPARE(testConfig.settings()->tone()->channelIdle(), Channel::Type::FM|Channel::Type::DMR);\n\n  config.settings()->tone()->setChannelIdle(Channel::Type::None);\n  encodeDecode(config, testConfig);\n  QCOMPARE(testConfig.settings()->tone()->channelIdle(), Channel::Type::None);\n}\n\nvoid\nD578UVTest::testARC4Encryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/arc4_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n            .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  D578UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"1122334455\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nD578UVTest::testAMChannel() {\n  // Load config from file\n  ErrorStack err;\n  Config config, testConfig;\n  if (! config.readYAML(\":/data/am_channel_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n            .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  encodeDecode(config, testConfig);\n\n  // There must be an AM and a FM channel. The order is not defined.\n  QCOMPARE(testConfig.channelList()->count(), 2);\n  auto am = testConfig.channelList()->channel(0)->is<AMChannel>() ?\n              testConfig.channelList()->channel(0)->as<AMChannel>() :\n              testConfig.channelList()->channel(1)->as<AMChannel>();\n  QCOMPARE(am->name(), config.channelList()->channel(0)->as<AMChannel>()->name());\n  QCOMPARE(am->rxFrequency(), config.channelList()->channel(0)->as<AMChannel>()->rxFrequency());\n}\n\n\nQTEST_GUILESS_MAIN(D578UVTest)\n\n"
  },
  {
    "path": "test/d578uv_test.hh",
    "content": "#ifndef D578UVTEST_HH\n#define D578UVTEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass D578UVTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit D578UVTest(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testARC4Encryption();\n  void testAMChannel();\n  void testMicGain(); // Retression test for #773\n  void testChannelDataACK(); // Regression test for #813\n  void testSettingsDisplayVolumeChangePrompt(); // Regression test for #813\n  void testSettingsRoamingNotificationCount(); // Regression test for #813\n  void testIdleToneEnable(); // Regression test for #911\n\nprotected:\n  void encodeDecode(Config &input, Config &ouput);\n};\n\n#endif // D578UVTEST_HH\n"
  },
  {
    "path": "test/d868uve_test.cc",
    "content": "#include \"d868uve_test.hh\"\n#include \"config.hh\"\n#include \"d868uv_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n\nD868UVETest::D868UVETest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nD868UVETest::encodeDecode(Config &input, Config &output) {\n  ErrorStack err;\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&input, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n  if (! codeplug.decode(&output, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD868UVETest::testBasicConfigEncoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD868UVETest::testBasicConfigDecoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD868UVETest::testChannelFrequency() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D868UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D868UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\nvoid\nD868UVETest::testAutoRepeaterOffset() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/anytone_auto_repeater_extension.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  AnytoneAutoRepeaterSettingsExtension *ext = config.settings()->anytoneExtension()->autoRepeaterSettings();\n  // There should be two offset frequencies\n  QCOMPARE(ext->offsets()->count(), 2);\n  QCOMPARE(ext->offsets()->get(0)->as<AnytoneAutoRepeaterOffset>()->offset().inHz(),  600000);\n  QCOMPARE(ext->offsets()->get(1)->as<AnytoneAutoRepeaterOffset>()->offset().inHz(), 7600000);\n  // check if VHF and UHF frequency offsets are correct\n  QVERIFY(! ext->vhfRef()->isNull()); QVERIFY(! ext->uhfRef()->isNull());\n  QCOMPARE(ext->vhfRef()->as<AnytoneAutoRepeaterOffset>(), ext->offsets()->get(0)->as<AnytoneAutoRepeaterOffset>());\n  QCOMPARE(ext->uhfRef()->as<AnytoneAutoRepeaterOffset>(), ext->offsets()->get(1)->as<AnytoneAutoRepeaterOffset>());\n\n  // Encode\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Compare\n  QVERIFY2(comp_config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  ext = comp_config.settings()->anytoneExtension()->autoRepeaterSettings();\n\n  // There should be two offset frequencies\n  QCOMPARE(ext->offsets()->count(), 2);\n  QCOMPARE(ext->offsets()->get(0)->as<AnytoneAutoRepeaterOffset>()->offset().inHz(),  600000);\n  QCOMPARE(ext->offsets()->get(1)->as<AnytoneAutoRepeaterOffset>()->offset().inHz(), 7600000);\n\n  // check if VHF and UHF frequency offsets are correct\n  QVERIFY(! ext->vhfRef()->isNull()); QVERIFY(! ext->uhfRef()->isNull());\n  QCOMPARE(ext->vhfRef()->as<AnytoneAutoRepeaterOffset>(), ext->offsets()->get(0)->as<AnytoneAutoRepeaterOffset>());\n  QCOMPARE(ext->uhfRef()->as<AnytoneAutoRepeaterOffset>(), ext->offsets()->get(1)->as<AnytoneAutoRepeaterOffset>());\n}\n\nvoid\nD868UVETest::testDTMFContacts() {\n  // Assemble config\n  Config base;\n\n  DTMFContact *contact0 = new DTMFContact();\n  contact0->setName(\"Contact 0\"); contact0->setNumber(\"0123456789ABCD#*\");\n  base.contacts()->add(contact0);\n\n  // Encode\n  D868UVCodeplug codeplug; ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&base, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check contacts\n  QCOMPARE(comp_config.contacts()->count(), 1);\n  QVERIFY(comp_config.contacts()->get(0)->is<DTMFContact>());\n  QCOMPARE(comp_config.contacts()->get(0)->as<DTMFContact>()->name(), \"Contact 0\");\n  // Contacts are limited to 14 digit numbers.\n  QCOMPARE(comp_config.contacts()->get(0)->as<DTMFContact>()->number(), \"0123456789ABCD\");\n}\n\n\nvoid\nD868UVETest::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // For now, messages are not encoded\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\n\nvoid\nD868UVETest::testBasicEncryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/basic_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n          .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"0123\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nD868UVETest::testRegressionSMSTemplateOffset() {\n  // Regression test for issue #469, message index offset error if more than one bank is used\n  // (i.e., more than 8 SMS).\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  for (int i=0; i<9; i++) {\n    SMSTemplate *sms = new SMSTemplate(); sms->setName(QString(\"SMS%1\").arg(i)); sms->setMessage(\"ABC\");\n    config.smsExtension()->smsTemplates()->add(sms);\n  }\n\n  ErrorStack err;\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // For now, messages are not encoded\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 9);\n}\n\n\nvoid\nD868UVETest::testRegressionSMSCount() {\n  // Regression test for issue #482 (tries to encode too many SMS)\n    Config config;\n    config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n    for (int i=0; i<101; i++) {\n        SMSTemplate *sms = new SMSTemplate(); sms->setName(QString(\"SMS%1\").arg(i)); sms->setMessage(\"ABC\");\n        config.smsExtension()->smsTemplates()->add(sms);\n    }\n\n    ErrorStack err;\n    D868UVCodeplug codeplug;\n    Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n    if (! codeplug.encode(&config, flags, err)) {\n        QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n                  .arg(err.format()).toStdString().c_str());\n    }\n}\n\n\nvoid\nD868UVETest::testRegressionDefaultChannel() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  config.settings()->setAnytoneExtension(new AnytoneSettingsExtension());\n  config.settings()->boot()->zoneA()->set(config.zones()->zone(0));\n  config.settings()->boot()->channelA()->set(config.zones()->zone(0)->A()->get(0));\n  config.settings()->boot()->zoneB()->set(config.zones()->zone(1));\n  config.settings()->boot()->channelB()->set(config.zones()->zone(1)->A()->get(0));\n  config.settings()->boot()->enableDefaultChannel(true);\n\n  D868UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n      QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D868UVE: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QVERIFY(decoded.settings()->anytoneExtension());\n  QVERIFY(decoded.settings()->boot()->defaultChannelEnabled());\n  QCOMPARE(decoded.settings()->boot()->zoneA()->as<Zone>(),\n           decoded.zones()->zone(0));\n  QCOMPARE(decoded.settings()->boot()->channelA()->as<Channel>()->name(),\n           decoded.zones()->zone(0)->A()->get(0)->name());\n  QCOMPARE(decoded.settings()->boot()->zoneB()->as<Zone>(),\n           decoded.zones()->zone(1));\n  QCOMPARE(decoded.settings()->boot()->channelB()->as<Channel>()->name(),\n           decoded.zones()->zone(1)->A()->get(0)->name());\n}\n\n\nvoid\nD868UVETest::testChannelDataACK() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_channel_data_ack.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 2);\n  QVERIFY(config.channelList()->channel(0)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QVERIFY(config.channelList()->channel(1)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D578UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QCOMPARE(testConfig.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n}\n\nvoid\nD868UVETest::testMicGain() {\n  ErrorStack err;\n  Config copy, config; config.copy(_basicConfig);\n  QList<QPair<unsigned int,unsigned int>> pairs = {{1,1}, {2,1}, {3,1}, {4,3}, {5,3}, {6,5}, {7,5}, {8,7}, {9,7}, {10,10}};\n  for (auto pair: pairs) {\n    config.settings()->audio()->setMicGain(Level::fromValue(pair.first));\n    encodeDecode(config, copy);\n    QCOMPARE(copy.settings()->audio()->micGain().value(), Level::fromValue(pair.second).value());\n  }\n}\n\n\n\nQTEST_GUILESS_MAIN(D868UVETest)\n\n"
  },
  {
    "path": "test/d868uve_test.hh",
    "content": "#ifndef D868UVETEST_HH\n#define D868UVETEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass D868UVETest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit D868UVETest(QObject *parent = nullptr);\n\nprotected:\n  void encodeDecode(Config &input, Config &output);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testAutoRepeaterOffset();\n  void testDTMFContacts();\n  void testSMSTemplates();\n  void testBasicEncryption();\n\n  void testRegressionSMSTemplateOffset();\n  void testRegressionSMSCount();\n  void testRegressionDefaultChannel();\n  void testChannelDataACK(); ///< Regression test for #813\n  void testMicGain();\n};\n\n#endif // D878UV2TEST_HH\n"
  },
  {
    "path": "test/d878uv2_test.cc",
    "content": "#include \"d878uv2_test.hh\"\n#include \"config.hh\"\n#include \"d878uv2.hh\"\n#include \"d878uv2_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nD878UV2Test::D878UV2Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nD878UV2Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UV2Codeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UV2Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UV2Codeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UV2Test::testChannelFrequency() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D878UV II: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D878UV II: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nD878UV2Test::testKeyFunctions() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/anytone_key_function.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  AnytoneKeySettingsExtension *ext = config.settings()->anytoneExtension()->keySettings();\n\n  QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel);\n  QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::GPS);\n  QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO);\n  QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType);\n  QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off);\n  QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n\n  // Encode\n  D878UV2Codeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(comp_config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  ext = comp_config.settings()->anytoneExtension()->keySettings();\n\n  QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel);\n  QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::GPS);\n  QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO);\n  QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType);\n  QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off);\n  QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n}\n\nvoid\nD878UV2Test::testAESEncryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/aes_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n            .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"11223344556677889900AABBCCDDEEFF\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nQTEST_GUILESS_MAIN(D878UV2Test)\n\n"
  },
  {
    "path": "test/d878uv2_test.hh",
    "content": "#ifndef D878UV2TEST_HH\n#define D878UV2TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass D878UV2Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit D878UV2Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testKeyFunctions();\n\n  void testAESEncryption();\n};\n\n#endif // D878UV2TEST_HH\n"
  },
  {
    "path": "test/d878uv_test.cc",
    "content": "#include \"d878uv_test.hh\"\n#include \"config.hh\"\n#include \"d878uv_codeplug.hh\"\n#include \"d878uv_limits.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n#include \"logger.hh\"\n\nD878UVTest::D878UVTest(QObject *parent)\n  : UnitTestBase(parent), _stderr(stderr)\n{\n  Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG));\n}\n\nvoid\nD878UVTest::encodeDecode(Config &input, Config &output) {\n  ErrorStack err;\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&input, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n  if (! codeplug.decode(&output, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UVTest::initTestCase() {\n  UnitTestBase::initTestCase();\n\n  ErrorStack err;\n  if (! _micGainConfig.readYAML(\":/data/audio_settings_extension.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UVTest::testBasicConfigEncoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UVTest::testBasicConfigDecoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nD878UVTest::testAnalogMicGain() {\n  Config config;\n  encodeDecode(_micGainConfig, config);\n  QVERIFY(config.settings()->audio()->fmMicGainEnabled());\n  QCOMPARE(config.settings()->audio()->fmMicGain(), Level::fromValue(5));\n}\n\nvoid\nD878UVTest::testChannelFrequency() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D878UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D878UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nD878UVTest::testRoaming() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UVCodeplug codeplug;\n  if (! codeplug.encode(&_roamingConfig, flags, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D878UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.roamingChannels()->count(), 3);\n  QCOMPARE(config.roamingZones()->count(), 2);\n  QCOMPARE(config.roamingZones()->count(), 2);\n\n  QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->count(), 2);\n  QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->channel(0),\n           config.roamingChannels()->get(0)->as<RoamingChannel>());\n  QCOMPARE(config.roamingZones()->get(0)->as<RoamingZone>()->channel(1),\n           config.roamingChannels()->get(1)->as<RoamingChannel>());\n\n  QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->count(), 2);\n  QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->channel(0),\n           config.roamingChannels()->get(0)->as<RoamingChannel>());\n  QCOMPARE(config.roamingZones()->get(1)->as<RoamingZone>()->channel(1),\n           config.roamingChannels()->get(2)->as<RoamingChannel>());\n}\n\nvoid\nD878UVTest::testHangTime() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/anytone_call_hangtime.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n\n  QCOMPARE(config.settings()->dmr()->privateCallHangTime().seconds(), 4ULL);\n  QCOMPARE(config.settings()->dmr()->groupCallHangTime().seconds(), 5ULL);\n\n  // Encode\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  Config *intermediate = codeplug.preprocess(&config, err);\n  if (nullptr == intermediate) {\n    QFAIL(QString(\"Cannot prepare codeplug for AnyTone AT-D878UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  if (! codeplug.encode(intermediate, flags, err)) {\n    delete intermediate;\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UVE: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  delete intermediate;\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UVII: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(comp_config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n\n  QCOMPARE(config.settings()->dmr()->privateCallHangTime().seconds(), 4ULL);\n  QCOMPARE(config.settings()->dmr()->groupCallHangTime().seconds(), 5ULL);\n}\n\nvoid\nD878UVTest::testKeyFunctions() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/anytone_key_function.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  AnytoneKeySettingsExtension *ext = config.settings()->anytoneExtension()->keySettings();\n\n  QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel);\n  QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::GPS);\n  QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO);\n  QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType);\n  QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off);\n  QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n\n  // Encode\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D868UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QVERIFY2(comp_config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n  ext = comp_config.settings()->anytoneExtension()->keySettings();\n\n  QCOMPARE(ext->funcKey1Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleMainChannel);\n  QCOMPARE(ext->funcKey1Long(), AnytoneKeySettingsExtension::KeyFunction::GPS);\n  QCOMPARE(ext->funcKey2Short(), AnytoneKeySettingsExtension::KeyFunction::ToggleVFO);\n  QCOMPARE(ext->funcKey2Long(), AnytoneKeySettingsExtension::KeyFunction::ChannelType);\n  QCOMPARE(ext->funcKeyAShort(), AnytoneKeySettingsExtension::KeyFunction::Off);\n  QCOMPARE(ext->funcKeyALong(), AnytoneKeySettingsExtension::KeyFunction::Encryption);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n  QCOMPARE(ext->funcKeyBShort(), AnytoneKeySettingsExtension::KeyFunction::Voltage);\n  QCOMPARE(ext->funcKeyBLong(), AnytoneKeySettingsExtension::KeyFunction::Call);\n}\n\n\nvoid\nD878UVTest::testFMAPRSSettings() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\\n\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QCOMPARE(config.posSystems()->count(), 1);\n  QVERIFY(config.posSystems()->get(0)->is<FMAPRSSystem>());\n\n  FMAPRSSystem *aprs = config.posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(aprs->source(), \"DM3MAT\"); QCOMPARE(aprs->srcSSID(), 7);\n  QCOMPARE(aprs->destination(), \"APAT81\"); QCOMPARE(aprs->destSSID(), 0);\n  QCOMPARE(aprs->path(), \"WIDE1-1,WIDE2-1\");\n  QCOMPARE(aprs->period(), Interval::fromMinutes(5));\n\n  // Encode\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  Config *intermediate = codeplug.preprocess(&config, err);\n  if (nullptr == intermediate) {\n    QFAIL(QString(\"Cannot prepare codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  if (! codeplug.encode(intermediate, flags, err)) {\n    delete intermediate;\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  delete intermediate;\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QCOMPARE(comp_config.posSystems()->count(), 1);\n  QVERIFY(comp_config.posSystems()->get(0)->is<FMAPRSSystem>());\n\n  FMAPRSSystem *comp_aprs = comp_config.posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID());\n  QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID());\n  QCOMPARE(comp_aprs->path(), aprs->path());\n  QCOMPARE(comp_aprs->period(), aprs->period());\n}\n\n\n\nvoid\nD878UVTest::testAESEncryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/aes_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n          .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"11223344556677889900AABBCCDDEEFF\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nD878UVTest::testARC4Encryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/arc4_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n          .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"1122334455\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nD878UVTest::testRegressionDefaultChannel() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  config.settings()->setAnytoneExtension(new AnytoneSettingsExtension());\n  config.settings()->boot()->zoneA()->set(config.zones()->zone(0));\n  config.settings()->boot()->channelA()->set(config.zones()->zone(0)->A()->get(0));\n  config.settings()->boot()->zoneB()->set(config.zones()->zone(1));\n  config.settings()->boot()->channelB()->set(config.zones()->zone(1)->A()->get(0));\n  config.settings()->boot()->enableDefaultChannel(true);\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n      QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QVERIFY(decoded.settings()->boot()->defaultChannelEnabled());\n  QCOMPARE(decoded.settings()->boot()->zoneA()->as<Zone>(),\n           decoded.zones()->zone(0));\n  QCOMPARE(decoded.settings()->boot()->channelA()->as<Channel>()->name(),\n           decoded.zones()->zone(0)->A()->get(0)->name());\n  QCOMPARE(decoded.settings()->boot()->zoneB()->as<Zone>(),\n           decoded.zones()->zone(1));\n  QCOMPARE(decoded.settings()->boot()->channelB()->as<Channel>()->name(),\n           decoded.zones()->zone(1)->A()->get(0)->name());\n}\n\n\n\nvoid\nD878UVTest::testRegressionAutoRepeater() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  config.settings()->setAnytoneExtension(new AnytoneSettingsExtension());\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n      QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QVERIFY(decoded.settings()->anytoneExtension());\n  QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->vhf2Min().inMHz(), 136.0);\n  QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->vhf2Max().inMHz(), 174.0);\n  QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->uhf2Min().inMHz(), 400.0);\n  QCOMPARE(decoded.settings()->anytoneExtension()->autoRepeaterSettings()->uhf2Max().inMHz(), 480.0);\n}\n\n\nvoid\nD878UVTest::testRegressionVFOStep() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  config.settings()->setAnytoneExtension(new AnytoneSettingsExtension());\n  config.settings()->anytoneExtension()->setVFOStep(Frequency::fromkHz(8.33));\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n      QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QVERIFY(decoded.settings()->anytoneExtension());\n  QCOMPARE(decoded.settings()->anytoneExtension()->vfoStep().inkHz(), 8.33);\n}\n\n\nvoid\nD878UVTest::testEmptyAESKey() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  // Empty encryption key\n  config.commercialExtension()->encryptionKeys()->add(new AESEncryptionKey());\n\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone AT-D878UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n      QFAIL(QString(\"Cannot decode codeplug for AnyTone AT-D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 0);\n}\n\n\nvoid\nD878UVTest::testChannelDataACK() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_channel_data_ack.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->count(), 2);\n  QVERIFY(config.channelList()->channel(0)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QVERIFY(config.channelList()->channel(1)->is<DMRChannel>());\n  QCOMPARE(config.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.channelList()->channel(0)->as<DMRChannel>()\n             ->extended()->dataConfirm(), false);\n  QCOMPARE(testConfig.channelList()->channel(1)->as<DMRChannel>()\n             ->extended()->dataConfirm(), true);\n}\n\n\nvoid\nD878UVTest::testSettingsDisplayVolumeChangePrompt() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/anytone_settings_display.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.settings()->anytoneExtension()->displaySettings()->volumeChangePromptEnabled(), false);\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D878UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for AnyTone D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for AnyTone D878UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.settings()->anytoneExtension()->displaySettings()->volumeChangePromptEnabled(), false);\n}\n\n\nvoid\nD878UVTest::testRadioLimits() {\n  D878UVLimits limits({{Frequency::fromMHz(137),Frequency::fromMHz(150)},\n                       {Frequency::fromMHz(400),Frequency::fromMHz(450)}},\n                      {{Frequency::fromMHz(137),Frequency::fromMHz(150)},\n                       {Frequency::fromMHz(400),Frequency::fromMHz(450)}}, \"V101\");\n  RadioLimitContext ctx;\n  if (! limits.verifyConfig(&_basicConfig, ctx)) {\n    QString issues;\n    for (int i=0; i<ctx.count(); i++) {\n      if (! issues.isEmpty())\n        issues.append(\", \");\n      issues.append(ctx.message(i).format());\n    }\n    QFAIL(issues.toLatin1().constData());\n  }\n}\n\n\nvoid\nD878UVTest::testMicGain() {\n  ErrorStack err;\n  Config copy, config; config.copy(_basicConfig);\n  QList<QPair<unsigned int,unsigned int>> pairs = {{1,1}, {2,1}, {3,1}, {4,3}, {5,3}, {6,5}, {7,5}, {8,7}, {9,7}, {10,10}};\n  for (auto pair: pairs) {\n    config.settings()->audio()->setMicGain(Level::fromValue(pair.first));\n    encodeDecode(config, copy);\n    QCOMPARE(copy.settings()->audio()->micGain(), Level::fromValue(pair.second));\n  }\n}\n\n\nvoid\nD878UVTest::testFixedLocation() {\n  ErrorStack err;\n  Config decoded, config; config.copy(_basicConfig);\n\n  config.settings()->gnss()->setFixedPositionLocator(\"JO62kk45\");\n  config.settings()->gnss()->enableFixedPosition(true);\n  encodeDecode(config, decoded);\n\n  QVERIFY(decoded.settings()->gnss()->fixedPositionEnabled());\n  QCOMPARE(decoded.settings()->gnss()->fixedPositionLocator(), QString(\"JO62kk45\"));\n}\n\n\nQTEST_GUILESS_MAIN(D878UVTest)\n\n"
  },
  {
    "path": "test/d878uv_test.hh",
    "content": "#ifndef D878UVTEST_HH\n#define D878UVTEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass D878UVTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit D878UVTest(QObject *parent = nullptr);\n\nprotected:\n  void encodeDecode(Config &input, Config &output);\n\nprivate slots:\n  void initTestCase();\n\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n\n  void testAnalogMicGain();\n  void testRoaming();\n  void testHangTime();\n  void testKeyFunctions();\n\n  void testFMAPRSSettings();\n\n  void testAESEncryption();\n  void testARC4Encryption();\n\n  void testRegressionDefaultChannel();\n  void testRegressionAutoRepeater();\n  void testRegressionVFOStep();\n\n  void testEmptyAESKey();  ///< Regression test for #711\n  void testChannelDataACK(); ///< Regression test for #813\n  void testSettingsDisplayVolumeChangePrompt(); ///< Regression test for #813\n  void testRadioLimits(); ///< Regression test for #816\n  void testMicGain();\n  void testFixedLocation();\n\nprotected:\n  Config _micGainConfig;\n  QTextStream _stderr;\n};\n\n#endif // D878UVTEST_HH\n"
  },
  {
    "path": "test/data/aes_encryption.yaml",
    "content": "---\nversion: 0.13.0\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: BB, ring: false, type: GroupCall, number: 2621}\n\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1]}\n\nchannels:\n - dmr:\n      id: ch1\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n      commercial:\n        encryptionKey: key1\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n\ncommercial:\n  encryptionKeys:\n    - aes:\n        id: key1\n        name: AES Key 1\n        key: 11223344556677889900AABBCCDDEEFF\n...\n"
  },
  {
    "path": "test/data/am_channel_test.yaml",
    "content": "---\nversion: 0.14.0\nsettings:\n  introLine1: \"\"\n  introLine2: \"\"\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 3\n  vox: off\n  tot: infinite\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: Test, number: 12345}\ncontacts:\n  - dmr: {id: cont1, name: Test Contact, ring: false, type: PrivateCall, number: 123456}\ngroupLists:\n  []\nchannels:\n  - am:\n      id: ch1\n      name: AM Channel\n      rxOnly: true\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      rxFrequency: 121.13 MHz\n      txFrequency: ~\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: ~\n      squelch: !<!default> \"\"\n  - fm:\n      id: ch2\n      name: 2m Mobil\n      rxOnly: false\n      admit: Always\n      rxTone: ~\n      txTone: ~\n      bandwidth: Narrow\n      rxFrequency: 145.5 MHz\n      txFrequency: 145.5 MHz\n      power: !<!default> \"\"\n      timeout: !<!default> \"\"\n      vox: ~\n      squelch: !<!default> \"\"\nzones:\n  - id: zone1\n    name: Local\n    A: [ch1, ch2]\n    B: []\ncommercial:\n  encryptionKeys:\n    []\nsms:\n  format: DMR\n  templates:\n    []\n...\n"
  },
  {
    "path": "test/data/anytone_auto_repeater_extension.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n  anytone:\n    autoRepeaterSettings:\n      directionA: Negative\n      directionB: Negative\n      vhf: off0\n      uhf: off1\n      offsets:\n        - {id: off0, name: VHF Offset, offset: 600 kHz}\n        - {id: off1, name: UHF Offset, offset: 7.6 MHz}\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/anytone_call_hangtime.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n  dmr:\n    privateCallHangTime: 4s\n    groupCallHangTime: 5s\n  anytone:\n    dmrSettings:\n      manualPrivateCallHangTime: 4s\n      manualGroupCallHangTime: 5s\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/anytone_channel_data_ack.yaml",
    "content": "radioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\nchannels:\n  - dmr:\n      id: ch1\n      name: \"Test Channel\"\n      rxFrequency: 123.456 Mhz\n      txFrequency: 123.456 Mhz\n      extended:\n        dataConfirm: false\n  - dmr:\n      id: ch2\n      name: \"Test Channel 2\"\n      rxFrequency: 123.456 Mhz\n      txFrequency: 123.456 Mhz\n      extended:\n        dataConfirm: true\n\nzones:\n  - id: zone1\n    name: Test Zone\n    A: [ch1, ch2]\n    B: []\n"
  },
  {
    "path": "test/data/anytone_key_function.yaml",
    "content": "---\nversion: 0.11.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n  anytone:\n    keySettings:\n      funcKey1Short: ToggleMainChannel\n      funcKey1Long: GPS\n      funcKey2Short: ToggleVFO\n      funcKey2Long: ChannelType\n      funcKeyAShort: Off\n      funcKeyALong: Encryption\n      funcKeyBShort: Voltage\n      funcKeyBLong: Call\n      funcKeyCShort: Power\n      funcKeyCLong: VOX\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/anytone_settings_display.yaml",
    "content": "settings:\n  anytone:\n    displaySettings:\n      volumeChangePrompt: false\n"
  },
  {
    "path": "test/data/anytone_settings_roaming.yaml",
    "content": "settings:\n  anytone:\n    roaming:\n      notificationCount: 1\n"
  },
  {
    "path": "test/data/arc4_encryption.yaml",
    "content": "---\nversion: 0.13.0\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: BB, ring: false, type: GroupCall, number: 2621}\n\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1]}\n\nchannels:\n - dmr:\n      id: ch1\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n      commercial:\n        encryptionKey: key1\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n\ncommercial:\n  encryptionKeys:\n    - rc4:\n        id: key1\n        name: ARC4 Key 1\n        key: 1122334455\n...\n"
  },
  {
    "path": "test/data/audio_settings_extension.yaml",
    "content": "---\nversion: 0.15.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n  audio:\n    fmMicGain: 6  # Must differ from settings->micLevel\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/basic_encryption.yaml",
    "content": "---\nversion: 0.13.0\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: BB, ring: false, type: GroupCall, number: 2621}\n\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1]}\n\nchannels:\n - dmr:\n      id: ch1\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n      commercial:\n        encryptionKey: key1\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n\ncommercial:\n  encryptionKeys:\n    - dmr:\n        id: key1\n        name: DMR Key 1\n        key: 0123\n...\n"
  },
  {
    "path": "test/data/channel_frequency_test.yaml",
    "content": "radioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\nchannels:\n  - dmr:\n      id: ch1\n      name: \"Test Channel\"\n      rxFrequency: 123.4567801 MHz # <- Up to 10Hz precision -> 123.45678 MHz\n      txFrequency: 999.99999 MHz   # <- Max frequency for 8-digit BCD in 10Hz\n"
  },
  {
    "path": "test/data/chirp_bandwidth.csv",
    "content": "Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE\r\n0,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n1,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,NFM,5.00,,50W,,,,,\r\n"
  },
  {
    "path": "test/data/chirp_cross.csv",
    "content": "Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE\r\n0,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,->Tone,FM,5.00,,50W,,,,,\r\n1,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,->DTCS,FM,5.00,,50W,,,,,\r\n2,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,Tone->,FM,5.00,,50W,,,,,\r\n3,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n4,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,Tone->DTCS,FM,5.00,,50W,,,,,\r\n5,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,023,DTCS->,FM,5.00,,50W,,,,,\r\n6,DB0SP,144.600000,-,0.600000,Cross,67.0,67.0,023,NN,023,DTCS->Tone,FM,5.00,,50W,,,,,\r\n7,DB0SP,144.600000,-,0.600000,Cross,67.0,77.0,023,NN,032,DTCS->DTCS,FM,5.00,,50W,,,,,\r\n"
  },
  {
    "path": "test/data/chirp_ctcss.csv",
    "content": "Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE\r\n0,DB0SP,144.600000,+,0.600000,,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n1,DB0SP,144.600000,+,0.600000,Tone,67.0,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n2,DB0SP,144.600000,+,0.600000,TSQL,67.0,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n"
  },
  {
    "path": "test/data/chirp_dcs.csv",
    "content": "Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE\r\n0,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n1,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,NR,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n2,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,RN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n3,DB0SP,144.600000,+,0.600000,DTCS,88.5,88.5,023,RR,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n"
  },
  {
    "path": "test/data/chirp_simple.csv",
    "content": "Location,Name,Frequency,Duplex,Offset,Tone,rToneFreq,cToneFreq,DtcsCode,DtcsPolarity,RxDtcsCode,CrossMode,Mode,TStep,Skip,Power,Comment,URCALL,RPT1CALL,RPT2CALL,DVCODE\r\n0,KD8BMI,147.075000,+,0.600000,Tone,103.5,77.0,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n1,W8CUL,146.760000,-,0.600000,Tone,103.5,103.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n2,W8MWA,145.430000,-,0.600000,Tone,103.5,103.5,023,NN,023,Tone->Tone,FM,5.00,,50W,,,,,\r\n"
  },
  {
    "path": "test/data/config_test.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1, ch2, ch3]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\n  - id: zone2\n    name: Zu Hause2\n    A: [ch3, ch2, ch4]\n    B: [ch1]\n\nroamingChannels:\n  - id: rc1\n    name: R DB0LDS\n    rxFrequency: 439.5625 MHz\n    txFrequency: 431.9625 MHz\n    colorCode: 1\n\nroamingZones:\n  - id: roam1\n    name: Berlin/Brand\n    channels:\n      - rc1\n\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/ctcss_copy_test.yaml",
    "content": "---\nversion: 0.12.0\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1]}\n\nchannels:\n  - fm:\n      id: ch1\n      name: DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      rxTone: {ctcss: 67}\n      txTone: {ctcss: 67}\n      power: High\n      timeout: 0\n      vox: 0\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n    B: []\n...\n"
  },
  {
    "path": "test/data/ctcss_null_test.yaml",
    "content": "---\nversion: 0.12.1\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1]}\n\nchannels:\n  - fm:\n      id: ch1\n      name: DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      rxTone: ~\n      txTone: {ctcss: 67}\n      power: High\n      timeout: 0\n      vox: 0\n\n  - fm:\n      id: ch2\n      name: DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      rxTone: {dcs: i037}\n      txTone: {ctcss: 67.0 Hz}\n      power: High\n      timeout: 0\n      vox: 0\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n    B: []\n...\n"
  },
  {
    "path": "test/data/dtmf_contact.yaml",
    "content": "---\nversion: 0.13.0\n\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dtmf: {id: cont1, name: DTMF, ring: false, number: \"*ABC123#\"}\n\nchannels:\n  - fm:\n      id: ch1\n      name: DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      rxTone: ~\n      txTone: {ctcss: 67}\n      power: High\n      timeout: 0\n      vox: 0\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1]\n    B: []\n...\n"
  },
  {
    "path": "test/data/fm_aprs_test.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\nchannels:\n  - fm:\n      id: aprs_ch\n      name: 2m APRS\n      rxFrequency: 145.8 MHz\n      txFrequency: 145.8 MHz\n      rxOnly: false\n      admit: Always\n      power: High\n      timeout: 0\n      vox: 0\n  - fm:\n      id: ch1\n      name: Some FM Channel\n      rxFrequency: 145.1 MHz\n      txFrequency: 145.1 MHz\n      rxOnly: false\n      admit: Always\n      power: High\n      timeout: 0\n      vox: 0\n      aprs: aprs\n\nzones:\n  - id: zone1\n    name: Test\n    A: [aprs_ch]\n    B: []\n\npositioning:\n  - aprs:\n      id: aprs\n      name: APRS APAT81\n      period: 300\n      revert: aprs_ch\n      icon: Jogger\n      message: test message\n      destination: APAT81-0\n      source: DM3MAT-7\n      path: [WIDE1-1, WIDE2-1]\n\n...\n"
  },
  {
    "path": "test/data/multiple_radio_ids.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n  - dmr: {id: id2, name: DN3MAT, number: 2621371}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\n\nscanLists:\n  - id: scan1\n    name: KW\n    channels: [ch1, ch2, ch3]\n\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\n\nroamingChannels:\n  - id: rc1\n    name: R DB0LDS\n    rxFrequency: 439.5625\n    txFrequency: 431.96249999999998\n    colorCode: 1\n\nroamingZones:\n  - id: roam1\n    name: Berlin/Brand\n    channels:\n      - rc1\n\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/data/opengd77_boot_melody.yaml",
    "content": "---\n# Minimal settings +boot tone\n\nsettings:\n  defaultID: id1\n  tone:\n    bootTone: true\n    bootMelody:\n      bpm: 100\n      melody: c4 e g c e g c e g c e g c4 d e g c1\n\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\n...\n"
  },
  {
    "path": "test/data/opengd77_simple_config.yaml",
    "content": "version: 0.14.0\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\n\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n\nchannels:\n  - fm:\n      id: ch1\n      name: \"Test Channel\"\n      rxFrequency: 144.8 MHz \n      txFrequency: 144.8 MHz\n"
  },
  {
    "path": "test/data/roaming_channel_test.yaml",
    "content": "---\nversion: 0.9.0\nsettings:\n  introLine1: DM3MAT\n  introLine2: qDMR\n  micLevel: 3\n  speech: false\n  power: High\n  squelch: 1\n  vox: 0\n  tot: 0\n  defaultID: id1\nradioIDs:\n  - dmr: {id: id1, name: DM3MAT, number: 2621370}\ncontacts:\n  - dmr: {id: cont1, name: Local, ring: false, type: GroupCall, number: 9}\n  - dmr: {id: cont2, name: Regional, ring: false, type: GroupCall, number: 8}\n  - dmr: {id: cont3, name: BB, ring: false, type: GroupCall, number: 2621}\n  - dmr: {id: cont4, name: DL, ring: false, type: GroupCall, number: 262}\n  - dmr: {id: cont5, name: BM APRS, ring: false, type: PrivateCall, number: 262999}\n  - dmr: {id: cont6, name: HamRadioVillage, ring: false, type: GroupCall, number: 3177826}\ngroupLists:\n  - {id: grp1, name: Local, contacts: [cont1, cont2, cont3]}\n  - {id: grp2, name: DL, contacts: [cont4]}\n  - {id: grp3, name: HamRadioVillage, contacts: [cont6]}\nchannels:\n  - dmr:\n      id: ch1\n      name: L9 DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch2\n      name: BB DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS2\n      groupList: grp1\n      contact: cont3\n      aprs: aprs1\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch3\n      name: DL DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp2\n      contact: cont4\n      power: High\n      timeout: 0\n      vox: 0\n  - dmr:\n      id: ch4\n      name: HRV DB0LDS\n      rxFrequency: 439.5625\n      txFrequency: 431.9625\n      rxOnly: false\n      admit: Always\n      colorCode: 1\n      timeSlot: TS1\n      groupList: grp3\n      contact: cont6\n      power: High\n      timeout: 0\n      vox: 0\nzones:\n  - id: zone1\n    name: Zu Hause\n    A: [ch1, ch2, ch4]\n    B: [ch3]\nroamingChannels:\n  - id: rc1\n    name: R DB0LDS\n    rxFrequency: 439.5625\n    txFrequency: 431.96249999999998\n    colorCode: 1\n  - id: rc2\n    name: R DM0TZN\n    rxFrequency: 438.82499999999999\n    txFrequency: 431.22500000000002\n    colorCode: 1\n  - id: rc3\n    name: R DM0TT\n    rxFrequency: 439.08749999999998\n    txFrequency: 431.48750000000001\n    colorCode: 1\nroamingZones:\n  - id: roam1\n    name: RZ1\n    channels: [rc1, rc2]\n  - id: roam2\n    name: RZ2\n    channels: [rc1, rc3]\npositioning:\n  - dmr:\n      id: aprs1\n      name: GPS System\n      period: 300\n      contact: cont5\n...\n"
  },
  {
    "path": "test/dm1701_test.cc",
    "content": "#include \"dm1701_test.hh\"\n#include \"config.hh\"\n#include \"dm1701_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nDM1701Test::DM1701Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nDM1701Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  DM1701Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH MD1701: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDM1701Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  DM1701Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM1701: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM1701: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDM1701Test::testChannelFrequency() {\n  ErrorStack err;\n  DM1701Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM1701: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM1701: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nQTEST_GUILESS_MAIN(DM1701Test)\n\n"
  },
  {
    "path": "test/dm1701_test.hh",
    "content": "#ifndef DM1701TEST_HH\n#define DM1701TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass DM1701Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit DM1701Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n};\n\n#endif // DM1701TEST_HH\n"
  },
  {
    "path": "test/dm32uv_test.cc",
    "content": "#include \"dm32uv_test.hh\"\n#include \"config.hh\"\n#include \"dm32uv_codeplug.hh\"\n#include \"errorstack.hh\"\n#include \"logger.hh\"\n#include <QTest>\n\n\nDM32UVTest::DM32UVTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n}\n\nvoid\nDM32UVTest::testBasicConfigEncoding() {\n  ErrorStack err;\n  DM32UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDM32UVTest::testBasicConfigDecoding() {\n  ErrorStack err;\n  DM32UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nvoid\nDM32UVTest::testBasicConfigReencoding() {\n  ErrorStack err;\n  DM32UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config originalConfig;\n  if (! codeplug.decode(&originalConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  // original Config now contains all default values and extensions\n\n  // re-encode it\n  if (! codeplug.encode(&originalConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Decode complete config\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM32UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // compare\n  if (0 != originalConfig.compare(testConfig)) {\n    QString orig, test;\n    QTextStream origStream(&orig), testStream(&test);\n    originalConfig.toYAML(origStream);\n    testConfig.toYAML(testStream);\n    QFAIL(QString(\"Decoded config of BTECH DM32UV does not match source:\\n%1\\n%2\").arg(orig).arg(test).toStdString().c_str());\n  }\n}\n\nvoid\nDM32UVTest::testProstProcessingOfEmptyCodeplug() {\n  Config config;\n  DM32UVCodeplug codeplug;\n  ErrorStack err;\n\n  config.clear();\n  QVERIFY(codeplug.postprocess(&config, err));\n}\n\n\nvoid\nDM32UVTest::testAMChannelReencoding() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/am_channel_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  DM32UVCodeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DM32UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(testConfig.channelList()->count(), config.channelList()->count());\n  QVERIFY(testConfig.channelList()->channel(0)->is<AMChannel>());\n  QCOMPARE(testConfig.channelList()->channel(0)->rxFrequency(), Frequency::fromMHz(121.13));\n  QVERIFY(testConfig.channelList()->channel(0)->txFrequency().isZero());\n}\n\n\nvoid\nDM32UVTest::testChannelBankEncoding() {\n  static const uint32_t bank0=0x12000, bank1 = 0x13000, bank2 = 0x14000;\n  Config config;\n\n  config.radioIDs()->add(new DMRRadioID(\"test\", 123456));\n  // Add 84 channels to fill bank 0\n  for (int i=0; i<84; i++) {\n    auto ch = new FMChannel();\n    ch->setName(QString(\"Channel %1\").arg(i));\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.0));\n    config.channelList()->add(ch);\n  }\n\n  {\n    DM32UVCodeplug codeplug; ErrorStack err;\n    if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n      QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n              .arg(err.format()).toStdString().c_str());\n    }\n    QVERIFY(codeplug.isAllocated(bank0));\n    QVERIFY(!codeplug.isAllocated(bank1));\n    QVERIFY(!codeplug.isAllocated(bank2));\n  }\n\n  // Add another 85 channel to fill bank 1\n  for (int i=0; i<85; i++) {\n    auto ch = new FMChannel();\n    ch->setName(QString(\"Channel %1\").arg(i));\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.0));\n    config.channelList()->add(ch);\n  }\n\n  {\n    DM32UVCodeplug codeplug; ErrorStack err;\n    if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n      QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n              .arg(err.format()).toStdString().c_str());\n    }\n    QVERIFY(codeplug.isAllocated(bank0));\n    QVERIFY(codeplug.isAllocated(bank1));\n    QVERIFY(!codeplug.isAllocated(bank2));\n  }\n\n  // Add yet another channel to allocate bank 2\n  auto ch = new FMChannel();\n  ch->setName(QString(\"Channel %1\").arg(config.channelList()->count()));\n  ch->setRXFrequency(Frequency::fromMHz(144.0));\n  ch->setTXFrequency(Frequency::fromMHz(144.0));\n  config.channelList()->add(ch);\n\n  {\n    DM32UVCodeplug codeplug; ErrorStack err;\n    if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n      QFAIL(QString(\"Cannot encode codeplug for BTECH DM32UV: %1\")\n              .arg(err.format()).toStdString().c_str());\n    }\n    QVERIFY(codeplug.isAllocated(bank0));\n    QVERIFY(codeplug.isAllocated(bank1));\n    QVERIFY(codeplug.isAllocated(bank2));\n  }\n}\n\n\nQTEST_GUILESS_MAIN(DM32UVTest)\n\n\n"
  },
  {
    "path": "test/dm32uv_test.hh",
    "content": "#ifndef DR1801TEST_HH\n#define DR1801TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass DM32UVTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit DM32UVTest(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testBasicConfigReencoding();\n  void testProstProcessingOfEmptyCodeplug();\n  void testAMChannelReencoding();\n  /** Regression test for #873 */\n  void testChannelBankEncoding();\n};\n\n#endif // DR1801TEST_HH\n"
  },
  {
    "path": "test/dmr6x2uv2_test.cc",
    "content": "#include \"dmr6x2uv2_test.hh\"\n#include <QTest>\n\n\nDMR6X2UV2Test::DMR6X2UV2Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\n\n\nQTEST_GUILESS_MAIN(DMR6X2UV2Test)\n\n"
  },
  {
    "path": "test/dmr6x2uv2_test.hh",
    "content": "#ifndef DMR6X2UV2TEST_HH\n#define DMR6X2UV2TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass DMR6X2UV2Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit DMR6X2UV2Test(QObject *parent = nullptr);\n\nprivate slots:\n};\n\n#endif // DMR6X2UV2TEST_HH\n"
  },
  {
    "path": "test/dmr6x2uv_test.cc",
    "content": "#include \"dmr6x2uv_test.hh\"\n#include \"config.hh\"\n#include \"dmr6x2uv_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n\nDMR6X2UVTest::DMR6X2UVTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nDMR6X2UVTest::encodeDecode(Config &input, Config &output) {\n  ErrorStack err;\n  D878UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&input, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DMR-6X2UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n  if (! codeplug.decode(&output, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DMR-6X2UV: %1\")\n            .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nvoid\nDMR6X2UVTest::testBasicConfigEncoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  DMR6X2UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDMR6X2UVTest::testBasicConfigDecoding() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  DMR6X2UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BETCH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDMR6X2UVTest::testChannelFrequency() {\n  ErrorStack err;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  D868UVCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DMR-6X2UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DMR-6X2UV: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nDMR6X2UVTest::testFMAPRSSettings() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\\n\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QCOMPARE(config.posSystems()->count(), 1);\n  QVERIFY(config.posSystems()->get(0)->is<FMAPRSSystem>());\n\n  FMAPRSSystem *aprs = config.posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(aprs->source(), \"DM3MAT\"); QCOMPARE(aprs->srcSSID(), 7);\n  QCOMPARE(aprs->destination(), \"APAT81\"); QCOMPARE(aprs->destSSID(), 0);\n  QCOMPARE(aprs->path(), \"WIDE1-1,WIDE2-1\");\n  QCOMPARE(aprs->period(), Interval::fromMinutes(5));\n\n  QCOMPARE(config.channelList()->count(), 2);\n  QVERIFY(config.channelList()->channel(0)->is<FMChannel>());\n  QVERIFY(config.channelList()->channel(1)->is<FMChannel>());\n  QVERIFY(! config.channelList()->channel(1)->as<FMChannel>()->aprsRef()->isNull());\n\n  // Add extensions\n  auto ch_ext = new AnytoneFMChannelExtension();\n  ch_ext->setAPRSPTT(AnytoneChannelExtension::APRSPTT::Start);\n  config.channelList()->channel(1)->as<FMChannel>()->setAnytoneChannelExtension(ch_ext);\n\n  // test extension settings\n  auto ext = new AnytoneFMAPRSSettingsExtension();\n  ext->setPreWaveDelay(Interval::fromMilliseconds(100));\n  ext->setTXDelay(Interval::fromMilliseconds(200));\n  aprs->setAnytoneExtension(ext);\n\n  // Encode\n  DMR6X2UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  Config *intermediate = codeplug.preprocess(&config, err);\n  if (nullptr == intermediate) {\n    QFAIL(QString(\"Cannot prepare codeplug for BTECH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  if (! codeplug.encode(intermediate, flags, err)) {\n    delete intermediate;\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  delete intermediate;\n\n  // Decode\n  Config comp_config;\n  if (! codeplug.decode(&comp_config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DMR-6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Check config\n  QCOMPARE(comp_config.posSystems()->count(), 1);\n  QVERIFY(comp_config.posSystems()->get(0)->is<FMAPRSSystem>());\n\n  FMAPRSSystem *comp_aprs = comp_config.posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(comp_aprs->source(), aprs->source()); QCOMPARE(comp_aprs->srcSSID(), aprs->srcSSID());\n  QCOMPARE(comp_aprs->destination(), aprs->destination()); QCOMPARE(comp_aprs->destSSID(), aprs->destSSID());\n  QCOMPARE(comp_aprs->path(), aprs->path());\n  QCOMPARE(comp_aprs->period(), aprs->period());\n\n  // check extension settings\n  QVERIFY(nullptr != comp_aprs->anytoneExtension());\n  QCOMPARE(comp_aprs->anytoneExtension()->preWaveDelay().milliseconds(),\n           100);\n  QCOMPARE(comp_aprs->anytoneExtension()->txDelay().milliseconds(),\n           200);\n\n  // Check revert channel\n  QCOMPARE(comp_config.channelList()->count(), 2);\n  QVERIFY(comp_config.channelList()->channel(0)->is<FMChannel>());\n  QCOMPARE(comp_config.channelList()->channel(0)->rxFrequency(),\n           config.channelList()->channel(0)->rxFrequency());\n  QCOMPARE(comp_config.channelList()->channel(0)->txFrequency(),\n           config.channelList()->channel(0)->txFrequency());\n\n  // Check channel extension properties\n  QVERIFY(nullptr != comp_config.channelList()->channel(1)->as<FMChannel>()->anytoneChannelExtension());\n  auto comp_ch_ext = comp_config.channelList()->channel(1)->as<FMChannel>()->anytoneChannelExtension();\n  QCOMPARE(comp_ch_ext->aprsPTT(), ch_ext->aprsPTT());\n}\n\n\nvoid\nDMR6X2UVTest::testAESEncryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/aes_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n          .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  // Check parser\n  QVERIFY(nullptr != config.commercialExtension());\n  QCOMPARE(config.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(config.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"11223344556677889900AABBCCDDEEFF\"));\n  QVERIFY(nullptr != config.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(config.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           config.commercialExtension()->encryptionKeys()->key(0));\n\n  DMR6X2UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BETCH DMR6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BETCH DMR6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"11223344556677889900AABBCCDDEEFF\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nDMR6X2UVTest::testARC4Encryption() {\n  ErrorStack err;\n\n  // Load config from file\n  Config config;\n  if (! config.readYAML(\":/data/arc4_encryption.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file:\\n%1\")\n          .arg(err.format(\" \")).toStdString().c_str());\n  }\n\n  DMR6X2UVCodeplug codeplug;\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  if (! codeplug.encode(&config, flags, err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BETCH DMR6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BETCH DMR6X2UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Verify existence of key\n  QVERIFY(nullptr != decoded.commercialExtension());\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->count(), 1);\n  QCOMPARE(decoded.commercialExtension()->encryptionKeys()->key(0)->key(), QByteArray::fromHex(\"1122334455\"));\n\n  // Verify link to channel\n  QVERIFY(nullptr != decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey(),\n           decoded.commercialExtension()->encryptionKeys()->key(0));\n}\n\n\nvoid\nDMR6X2UVTest::testMicGain() {\n  ErrorStack err;\n  Config copy, config; config.copy(_basicConfig);\n  QList<QPair<unsigned int,unsigned int>> pairs = {{1,1}, {2,1}, {3,1}, {4,3}, {5,3}, {6,5}, {7,5}, {8,7}, {9,7}, {10,10}};\n  for (auto pair: pairs) {\n    config.settings()->audio()->setMicGain(Level::fromValue(pair.first));\n    encodeDecode(config, copy);\n    QCOMPARE(copy.settings()->audio()->micGain(), Level::fromValue(pair.second));\n  }\n}\n\n\n\nQTEST_GUILESS_MAIN(DMR6X2UVTest)\n\n"
  },
  {
    "path": "test/dmr6x2uv_test.hh",
    "content": "#ifndef DMR6X2UVTEST_HH\n#define DMR6X2UVTEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass DMR6X2UVTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit DMR6X2UVTest(QObject *parent = nullptr);\n\nprotected:\n  void encodeDecode(Config &input, Config &output);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n\n  void testFMAPRSSettings();\n\n  void testAESEncryption();\n  void testARC4Encryption();\n  void testMicGain();\n};\n\n#endif // DMR6X2UVTEST_HH\n"
  },
  {
    "path": "test/dr1801_test.cc",
    "content": "#include \"dr1801_test.hh\"\n#include \"config.hh\"\n#include \"dr1801uv_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nDR1801Test::DR1801Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nDR1801Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  DR1801UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nDR1801Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  DR1801UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nvoid\nDR1801Test::testBasicConfigReencoding() {\n  ErrorStack err;\n  DR1801UVCodeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config originalConfig;\n  if (! codeplug.decode(&originalConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  // original Config now contains all default values and extensions\n\n  // re-encode it\n  if (! codeplug.encode(&originalConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // Decode complete config\n  Config testConfig;\n  if (! codeplug.decode(&testConfig, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for BTECH DR1801UV: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  // compare\n  if (0 != originalConfig.compare(testConfig)) {\n    QFAIL(\"Decoded config of BTECH DR1801UV does not match source.\");\n  }\n}\n\nvoid\nDR1801Test::testProstProcessingOfEmptyCodeplug() {\n  Config config;\n  DR1801UVCodeplug codeplug;\n  ErrorStack err;\n\n  config.clear();\n  QVERIFY(codeplug.postprocess(&config, err));\n}\n\n\nQTEST_GUILESS_MAIN(DR1801Test)\n\n\n"
  },
  {
    "path": "test/dr1801_test.hh",
    "content": "#ifndef DR1801TEST_HH\n#define DR1801TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass DR1801Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit DR1801Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testBasicConfigReencoding();\n  void testProstProcessingOfEmptyCodeplug();\n};\n\n#endif // DR1801TEST_HH\n"
  },
  {
    "path": "test/gd73_test.cc",
    "content": "#include \"gd73_test.hh\"\n#include \"config.hh\"\n#include \"gd73.hh\"\n#include \"gd73_codeplug.hh\"\n#include \"gd73_limits.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nGD73Test::GD73Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nGD73Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  GD73Codeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nGD73Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  GD73Codeplug codeplug;\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nGD73Test::testChannelFrequency() {\n  ErrorStack err;\n  GD73Codeplug codeplug;\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\nvoid\nGD73Test::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  GD73Codeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\nvoid\nGD73Test::testFMSignaling() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 1\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall(67.0));\n    ch->setTXTone(SelectiveCall());\n    config.channelList()->add(ch);\n  }\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 2\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall());\n    ch->setTXTone(SelectiveCall(67.0));\n    config.channelList()->add(ch);\n  }\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 3\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall(23,false));\n    ch->setTXTone(SelectiveCall());\n    config.channelList()->add(ch);\n  }\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 4\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall());\n    ch->setTXTone(SelectiveCall(23, false));\n    config.channelList()->add(ch);\n  }\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 5\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall(23, true));\n    ch->setTXTone(SelectiveCall());\n    config.channelList()->add(ch);\n  }\n\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 6\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    ch->setRXTone(SelectiveCall());\n    ch->setTXTone(SelectiveCall(23, true));\n    config.channelList()->add(ch);\n  }\n\n  GD73Codeplug codeplug;\n  ErrorStack err;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config compare;\n  if (! codeplug.decode(&compare, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(compare.channelList()->count(), 6);\n  for (int i=0; i<compare.channelList()->count(); i++) {\n    QVERIFY(compare.channelList()->channel(i)->is<FMChannel>());\n    QCOMPARE(compare.channelList()->channel(i)->as<FMChannel>()->rxTone(),\n             config.channelList()->channel(i)->as<FMChannel>()->rxTone());\n    QCOMPARE(compare.channelList()->channel(i)->as<FMChannel>()->txTone(),\n             config.channelList()->channel(i)->as<FMChannel>()->txTone());\n  }\n}\n\nvoid\nGD73Test::testEncryption() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n\n  DMRContact *cnt = new DMRContact(DMRContact::AllCall, \"All Call\", 0, false);\n  config.contacts()->add(cnt);\n\n  BasicEncryptionKey *key = new BasicEncryptionKey();\n  key->setName(\"Key 1\");\n  key->fromHex(\"1234\");\n  config.commercialExtension()->encryptionKeys()->add(key);\n  QCOMPARE(key->toHex(), \"1234\");\n\n  auto *ext = new CommercialChannelExtension();\n  ext->setEncryptionKey(key);\n\n  DMRChannel *ch = new DMRChannel();\n  ch->setName(\"Ch\");\n  ch->setContact(cnt);\n  ch->setTXFrequency(Frequency::fromMHz(440));\n  ch->setRXFrequency(Frequency::fromMHz(440));\n  ch->setCommercialExtension(ext);\n  config.channelList()->add(ch);\n\n  GD73Codeplug codeplug;\n  ErrorStack err;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config compare;\n  if (! codeplug.decode(&compare, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(compare.channelList()->count(), 1);\n  QVERIFY(compare.channelList()->channel(0)->is<DMRChannel>());\n  QVERIFY(compare.channelList()->channel(0)->as<DMRChannel>()->commercialExtension());\n  QCOMPARE(compare.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey()->key(),\n           config.channelList()->channel(0)->as<DMRChannel>()->commercialExtension()->encryptionKey()->key());\n}\n\n\nvoid\nGD73Test::testEncryptionLimits() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n\n  DMRContact *cnt = new DMRContact(DMRContact::GroupCall, \"GroupCall\", 1234, false);\n  config.contacts()->add(cnt);\n\n  RXGroupList *grpLst = new RXGroupList(\"GrpLst\");\n  grpLst->addContact(cnt);\n  config.rxGroupLists()->add(grpLst);\n\n  BasicEncryptionKey *key = new BasicEncryptionKey();\n  key->setName(\"Key 1\");\n  key->fromHex(\"1234\");\n  config.commercialExtension()->encryptionKeys()->add(key);\n  QCOMPARE(key->toHex(), \"1234\");\n\n  auto *ext = new CommercialChannelExtension();\n  ext->setEncryptionKey(key);\n\n  DMRChannel *ch = new DMRChannel();\n  ch->setName(\"Ch\");\n  ch->setContact(cnt);\n  ch->setGroupList(grpLst);\n  ch->setTXFrequency(Frequency::fromMHz(440));\n  ch->setRXFrequency(Frequency::fromMHz(440));\n  ch->setCommercialExtension(ext);\n  config.channelList()->add(ch);\n\n  Zone *zone = new Zone(\"Zone\");\n  zone->A()->add(ch);\n  config.zones()->add(zone);\n\n  {\n    RadioLimitContext issues;\n    QVERIFY(GD73Limits().verifyConfig(&config, issues));\n    QStringList status;\n    for (int i=0; i<issues.count(); i++)\n      if (RadioLimitIssue::Severity::Critical == issues.message(i).severity())\n        status.append(issues.message(i).format());\n    QCOMPARE(status.count(), 0);\n  }\n\n  // Add wrong key type\n  AESEncryptionKey *key2 = new AESEncryptionKey();\n  key2->setName(\"Key 2\");\n  key2->fromHex(\"1234567890abcdef1234567890abcdef\");\n  config.commercialExtension()->encryptionKeys()->add(key2);\n\n  {\n    RadioLimitContext issues;\n    GD73Limits().verifyConfig(&config, issues);\n    QStringList status;\n    for (int i=0; i<issues.count(); i++)\n      if (RadioLimitIssue::Severity::Critical == issues.message(i).severity())\n        status.append(issues.message(i).format());\n    QVERIFY2(1 == status.count(), status.join(\"\\n\").toLocal8Bit().constData());\n  }\n\n  // add a larger key\n  config.commercialExtension()->encryptionKeys()->del(key2);\n  BasicEncryptionKey *key3 = new BasicEncryptionKey();\n  key3->setName(\"Key 2\");\n  key3->fromHex(\"1234567890\");\n  config.commercialExtension()->encryptionKeys()->add(key3);\n  QCOMPARE(key3->toHex(), \"1234567890\");\n\n  {\n    RadioLimitContext issues;\n    GD73Limits().verifyConfig(&config, issues);\n    QStringList status;\n    for (int i=0; i<issues.count(); i++)\n      if (RadioLimitIssue::Severity::Critical == issues.message(i).severity())\n        status.append(issues.message(i).format());\n    QVERIFY2(1 == status.count(), status.join(\"\\n\").toLocal8Bit().constData());\n  }\n}\n\n\nvoid\nGD73Test::testChannelTypeEcoding() {\n  Config config, new_config;\n  GD73Codeplug codeplug;\n  ErrorStack err;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n\n  {\n    DMRChannel *ch = new DMRChannel();\n    ch->setName(\"Channel 1\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    config.channelList()->add(ch);\n  }\n\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  new_config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  {\n    FMChannel *ch = new FMChannel();\n    ch->setName(\"Channel 1\");\n    ch->setRXFrequency(Frequency::fromMHz(144.0));\n    ch->setTXFrequency(Frequency::fromMHz(144.6));\n    new_config.channelList()->add(ch);\n  }\n\n  // Re-encode in the same binary codeplug\n  if (! codeplug.encode(&new_config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config compare;\n  if (! codeplug.decode(&compare, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(compare.channelList()->count(), 1);\n  QVERIFY(compare.channelList()->channel(0)->is<FMChannel>());\n}\n\n\nvoid\nGD73Test::testPowerEcoding() {\n  Config config;\n  GD73Codeplug codeplug;\n  ErrorStack err;\n\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  DMRChannel *ch = new DMRChannel();\n  ch->setName(\"Channel 1\");\n  ch->setRXFrequency(Frequency::fromMHz(144.0));\n  ch->setTXFrequency(Frequency::fromMHz(144.6));\n  ch->setPower(Channel::Power::High);\n  config.channelList()->add(ch);\n\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config compare;\n  if (! codeplug.decode(&compare, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(compare.channelList()->count(), 1);\n  QCOMPARE(compare.channelList()->channel(0)->power(), Channel::Power::High);\n}\n\nvoid\nGD73Test::testSquelchEcoding() {\n  Config config;\n  GD73Codeplug codeplug;\n  ErrorStack err;\n\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  config.settings()->audio()->setSquelch(Level::fromValue(1));\n\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config compare;\n  if (! codeplug.decode(&compare, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(compare.settings()->audio()->squelch().value(), 1);\n}\n\n\nQTEST_GUILESS_MAIN(GD73Test)\n\n"
  },
  {
    "path": "test/gd73_test.hh",
    "content": "#ifndef GD73TEST_HH\n#define GD73TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass GD73Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit GD73Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testSMSTemplates();\n  void testFMSignaling();\n  void testEncryption();\n  void testEncryptionLimits();\n  void testChannelTypeEcoding(); /// regression test #672\n  void testPowerEcoding();       /// regression test #672\n  void testSquelchEcoding();     /// regression test #676\n};\n\n#endif // GD73TEST_HH\n"
  },
  {
    "path": "test/gd77_test.cc",
    "content": "#include \"gd77_test.hh\"\n#include \"config.hh\"\n#include \"gd77.hh\"\n#include \"gd77_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nGD77Test::GD77Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nGD77Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  GD77Codeplug codeplug;\n  codeplug.clear();\n\n  auto intermediate = codeplug.preprocess(&_basicConfig, err);\n  if (nullptr == intermediate)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  if (! codeplug.encode(intermediate, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  delete intermediate;\n}\n\nvoid\nGD77Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  GD77Codeplug codeplug;\n  codeplug.clear();\n\n  auto intermediate = codeplug.preprocess(&_basicConfig, err);\n  if (nullptr == intermediate)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  if (! codeplug.encode(intermediate, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  delete intermediate;\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  codeplug.clear();\n}\n\nvoid\nGD77Test::testChannelFrequency() {\n  ErrorStack err;\n  GD77Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nGD77Test::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  GD77Codeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\nQTEST_GUILESS_MAIN(GD77Test)\n\n"
  },
  {
    "path": "test/gd77_test.hh",
    "content": "#ifndef GD77TEST_HH\n#define GD77TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass GD77Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit GD77Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testSMSTemplates();\n};\n\n#endif // GD77TEST_HH\n"
  },
  {
    "path": "test/labeltest.cc",
    "content": "#include \"labeltest.hh\"\n#include \"configlabelingvisitor.hh\"\n\n\nLabelTest::LabelTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nLabelTest::testLabelVisitor() {\n  Config::Context ctx;\n  ConfigLabelingVisitor labelPrinter(ctx);\n\n  ErrorStack err;\n  if (! labelPrinter.processItem(&_basicConfig, err)) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n\n  YAML::Node node = _basicConfig.serialize(ctx, err);\n  if (node.IsNull()) {\n    QFAIL(err.format().toLocal8Bit().constData());\n  }\n}\n\n\n\nQTEST_GUILESS_MAIN(LabelTest)\n"
  },
  {
    "path": "test/labeltest.hh",
    "content": "#ifndef LABELTEST_HH\n#define LABELTEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\nclass LabelTest: public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit LabelTest(QObject *parent=nullptr);\n\nprivate slots:\n  void testLabelVisitor();\n};\n\n#endif // LABETEST_HH\n"
  },
  {
    "path": "test/libdmrconfigtest.cc",
    "content": "#include \"libdmrconfigtest.hh\"\n\nUnitTestBase::UnitTestBase(QObject *parent)\n  : QObject(parent)\n{\n  // pass...\n}\n\nvoid\nUnitTestBase::initTestCase() {\n  ErrorStack err;\n  if (! _basicConfig.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  if (! _channelFrequencyConfig.readYAML(\":/data/channel_frequency_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n  if (! _roamingConfig.readYAML(\":/data/roaming_channel_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n}\n\nvoid\nUnitTestBase::cleanupTestCase() {\n  // clear codeplug\n  _basicConfig.clear();\n  _channelFrequencyConfig.clear();\n}\n\n\n"
  },
  {
    "path": "test/libdmrconfigtest.hh",
    "content": "#ifndef LIBDMRCONFIGTEST_HH\n#define LIBDMRCONFIGTEST_HH\n\n#include <QObject>\n#include \"config.hh\"\n#include <QTest>\n\nclass UnitTestBase : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit UnitTestBase(QObject *parent = nullptr);\n\nprotected slots:\n  virtual void initTestCase();\n  virtual void cleanupTestCase();\n\nprotected:\n  Config _basicConfig;\n  Config _channelFrequencyConfig;\n  Config _roamingConfig;\n};\n\n#endif // LIBDMRCONFIGTEST_HH\n"
  },
  {
    "path": "test/md2017_test.cc",
    "content": "#include \"md2017_test.hh\"\n#include \"config.hh\"\n#include \"md2017_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nMD2017Test::MD2017Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nMD2017Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  MD2017Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD2017: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nMD2017Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  MD2017Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD2017: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD2017: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nMD2017Test::testChannelFrequency() {\n  ErrorStack err;\n  MD2017Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD2017: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD2017: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\nQTEST_GUILESS_MAIN(MD2017Test)\n\n"
  },
  {
    "path": "test/md2017_test.hh",
    "content": "#ifndef MD2017TEST_HH\n#define MD2017TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass MD2017Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit MD2017Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n};\n\n#endif // MD2017TEST_HH\n"
  },
  {
    "path": "test/md390_test.cc",
    "content": "#include \"md390_test.hh\"\n#include \"config.hh\"\n#include \"md390_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nMD390Test::MD390Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nMD390Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  MD390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nMD390Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  MD390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nMD390Test::testChannelFrequency() {\n  ErrorStack err;\n  MD390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nMD390Test::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  MD390Codeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity GD73: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\n\nQTEST_GUILESS_MAIN(MD390Test)\n\n"
  },
  {
    "path": "test/md390_test.hh",
    "content": "#ifndef MD390TEST_HH\n#define MD390TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass MD390Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit MD390Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testSMSTemplates();\n};\n\n#endif // MD390TEST_HH\n"
  },
  {
    "path": "test/mergetest.cc",
    "content": "#include \"mergetest.hh\"\n\n#include <QTest>\n#include \"config.hh\"\n#include \"configmergevisitor.hh\"\n\n\nMergeTest::MergeTest(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\nvoid\nMergeTest::testMergeRadioIds() {\n  Config *base = new Config(), *merging = new Config();\n\n  base->radioIDs()->add(new DMRRadioID(\"ID 0\", 1234));\n  base->radioIDs()->add(new DMRRadioID(\"ID 1\", 1234));\n\n  merging->radioIDs()->add(new DMRRadioID(\"ID 1\", 2345));\n  merging->radioIDs()->add(new DMRRadioID(\"ID 2\", 2345));\n\n  ErrorStack err;\n  Config *merged = ConfigMerge::merge(base, merging,\n                                      ConfigMergeVisitor::ItemStrategy::Ignore,\n                                      ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->radioIDs()->count(), 3);\n  QCOMPARE(merged->radioIDs()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->radioIDs()->get(0)->as<DMRRadioID>()->number(), 1234);\n  QCOMPARE(merged->radioIDs()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->radioIDs()->get(1)->as<DMRRadioID>()->number(), 1234);\n  QCOMPARE(merged->radioIDs()->get(2)->name(), \"ID 2\");\n  QCOMPARE(merged->radioIDs()->get(2)->as<DMRRadioID>()->number(), 2345);\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Override,\n                              ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->radioIDs()->count(), 3);\n  QCOMPARE(merged->radioIDs()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->radioIDs()->get(0)->as<DMRRadioID>()->number(), 1234);\n  QCOMPARE(merged->radioIDs()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->radioIDs()->get(1)->as<DMRRadioID>()->number(), 2345);\n  QCOMPARE(merged->radioIDs()->get(2)->name(), \"ID 2\");\n  QCOMPARE(merged->radioIDs()->get(2)->as<DMRRadioID>()->number(), 2345);\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Duplicate,\n                              ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->radioIDs()->count(), 4);\n  QCOMPARE(merged->radioIDs()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->radioIDs()->get(0)->as<DMRRadioID>()->number(), 1234);\n  QCOMPARE(merged->radioIDs()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->radioIDs()->get(1)->as<DMRRadioID>()->number(), 1234);\n  QCOMPARE(merged->radioIDs()->get(2)->name(), \"ID 1 (copy)\");\n  QCOMPARE(merged->radioIDs()->get(2)->as<DMRRadioID>()->number(), 2345);\n  QCOMPARE(merged->radioIDs()->get(3)->name(), \"ID 2\");\n  QCOMPARE(merged->radioIDs()->get(3)->as<DMRRadioID>()->number(), 2345);\n}\n\n\nvoid\nMergeTest::testMergeContacts() {\n  Config *base = new Config(), *merging = new Config();\n\n  base->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 0\", 1234));\n  base->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 1\", 1234));\n\n  merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 1\", 2345));\n  merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 2\", 2345));\n\n  ErrorStack err;\n  Config *merged = ConfigMerge::merge(base, merging,\n                                      ConfigMergeVisitor::ItemStrategy::Ignore,\n                                      ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->contacts()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->contacts()->get(0)->as<DMRContact>()->number(), 1234);\n  QCOMPARE(merged->contacts()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->contacts()->get(1)->as<DMRContact>()->number(), 1234);\n  QCOMPARE(merged->contacts()->get(2)->name(), \"ID 2\");\n  QCOMPARE(merged->contacts()->get(2)->as<DMRContact>()->number(), 2345);\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Override,\n                              ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->contacts()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->contacts()->get(0)->as<DMRContact>()->number(), 1234);\n  QCOMPARE(merged->contacts()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->contacts()->get(1)->as<DMRContact>()->number(), 2345);\n  QCOMPARE(merged->contacts()->get(2)->name(), \"ID 2\");\n  QCOMPARE(merged->contacts()->get(2)->as<DMRContact>()->number(), 2345);\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Duplicate,\n                              ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 4);\n  QCOMPARE(merged->contacts()->get(0)->name(), \"ID 0\");\n  QCOMPARE(merged->contacts()->get(0)->as<DMRContact>()->number(), 1234);\n  QCOMPARE(merged->contacts()->get(1)->name(), \"ID 1\");\n  QCOMPARE(merged->contacts()->get(1)->as<DMRContact>()->number(), 1234);\n  QCOMPARE(merged->contacts()->get(2)->name(), \"ID 1 (copy)\");\n  QCOMPARE(merged->contacts()->get(2)->as<DMRContact>()->number(), 2345);\n  QCOMPARE(merged->contacts()->get(3)->name(), \"ID 2\");\n  QCOMPARE(merged->contacts()->get(3)->as<DMRContact>()->number(), 2345);\n}\n\n\nvoid\nMergeTest::testMergeGroupLists() {\n  Config *base = new Config(), *merging = new Config();\n\n  base->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 0\", 1234));\n  base->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 1\", 1234));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(base->contacts()->contact(0)->as<DMRContact>());\n    lst->addContact(base->contacts()->contact(1)->as<DMRContact>());\n    base->rxGroupLists()->add(lst);\n  }\n\n  merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 1\", 2345));\n  merging->contacts()->add(new DMRContact(DMRContact::PrivateCall, \"ID 2\", 2345));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(merging->contacts()->contact(0)->as<DMRContact>());\n    lst->addContact(merging->contacts()->contact(1)->as<DMRContact>());\n    merging->rxGroupLists()->add(lst);\n  }\n\n\n  ErrorStack err;\n  Config *merged = ConfigMerge::merge(base, merging,\n                                      ConfigMergeVisitor::ItemStrategy::Ignore,\n                                      ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->rxGroupLists()->list(0)->count(), 2);\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1));\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Ignore,\n                              ConfigMergeVisitor::SetStrategy::Merge, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->rxGroupLists()->list(0)->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2));\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Override,\n                              ConfigMergeVisitor::SetStrategy::Merge, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->rxGroupLists()->list(0)->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2));\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Ignore,\n                              ConfigMergeVisitor::SetStrategy::Override, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 3);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->rxGroupLists()->list(0)->count(), 2);\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(1));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(2));\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Duplicate,\n                              ConfigMergeVisitor::SetStrategy::Merge, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 4);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->rxGroupLists()->list(0)->count(), 4);\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(0), merged->contacts()->contact(0));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(1), merged->contacts()->contact(1));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(2), merged->contacts()->contact(2));\n  QCOMPARE(merged->rxGroupLists()->list(0)->contact(3), merged->contacts()->contact(3));\n}\n\n\nvoid\nMergeTest::testMergeChannels() {\n  Config *base = new Config(), *merging = new Config();\n\n  base->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 0\", 1234));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(base->contacts()->contact(0)->as<DMRContact>());\n    base->rxGroupLists()->add(lst);\n\n    FMChannel *ach = new FMChannel();\n    ach->setName(\"FM 0\");\n    ach->setRXFrequency(Frequency::fromMHz(144.0));\n    ach->setTXFrequency(Frequency::fromMHz(144.0));\n    base->channelList()->add(ach);\n\n    DMRChannel *dch = new DMRChannel();\n    dch->setName(\"DMR 0\");\n    dch->setRXFrequency(Frequency::fromMHz(144.0));\n    dch->setTXFrequency(Frequency::fromMHz(144.0));\n    dch->setContact(base->contacts()->contact(0)->as<DMRContact>());\n    dch->setGroupList(lst);\n    base->channelList()->add(dch);\n  }\n\n  merging->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 0\", 2345));\n  merging->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 1\", 3456));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(merging->contacts()->contact(0)->as<DMRContact>());\n    merging->rxGroupLists()->add(lst);\n\n    FMChannel *ach = new FMChannel();\n    ach->setName(\"FM 0\");\n    ach->setRXFrequency(Frequency::fromMHz(145.0));\n    ach->setTXFrequency(Frequency::fromMHz(145.0));\n    merging->channelList()->add(ach);\n\n    DMRChannel *dch = new DMRChannel();\n    dch->setName(\"DMR 0\");\n    dch->setRXFrequency(Frequency::fromMHz(145.0));\n    dch->setTXFrequency(Frequency::fromMHz(145.0));\n    dch->setContact(merging->contacts()->contact(0)->as<DMRContact>());\n    dch->setGroupList(lst);\n    merging->channelList()->add(dch);\n  }\n\n\n  ErrorStack err;\n  Config *merged = ConfigMerge::merge(base, merging,\n                                      ConfigMergeVisitor::ItemStrategy::Ignore,\n                                      ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 2);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->channelList()->count(), 2);\n\n  QVERIFY(merged->channelList()->channel(0)->is<FMChannel>());\n  QCOMPARE(merged->channelList()->channel(0)->rxFrequency().inMHz(), 144.0);\n\n  QVERIFY(merged->channelList()->channel(1)->is<DMRChannel>());\n  QCOMPARE(merged->channelList()->channel(1)->rxFrequency().inMHz(), 144.0);\n  QCOMPARE(merged->channelList()->channel(1)->as<DMRChannel>()->contact(), merged->contacts()->contact(0)->as<DMRContact>());\n  QCOMPARE(merged->channelList()->channel(1)->as<DMRChannel>()->groupList(), merged->rxGroupLists()->list(0));\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Override,\n                              ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->contacts()->count(), 2);\n  QCOMPARE(merged->rxGroupLists()->count(), 1);\n  QCOMPARE(merged->channelList()->count(), 2);\n\n  QVERIFY(merged->channelList()->channel(0)->is<FMChannel>());\n  QCOMPARE(merged->channelList()->channel(0)->rxFrequency().inMHz(), 145.0);\n\n  QVERIFY(merged->channelList()->channel(1)->is<DMRChannel>());\n  QCOMPARE(merged->channelList()->channel(1)->rxFrequency().inMHz(), 145.0);\n  QCOMPARE(merged->channelList()->channel(1)->as<DMRChannel>()->contact(), merged->contacts()->contact(0)->as<DMRContact>());\n  QCOMPARE(merged->channelList()->channel(1)->as<DMRChannel>()->groupList(), merged->rxGroupLists()->list(0));\n}\n\n\nvoid\nMergeTest::testMergeZones() {\n  Config *base = new Config(), *merging = new Config();\n\n  base->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 0\", 1234));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(base->contacts()->contact(0)->as<DMRContact>());\n    base->rxGroupLists()->add(lst);\n\n    FMChannel *ach = new FMChannel();\n    ach->setName(\"FM 0\");\n    ach->setRXFrequency(Frequency::fromMHz(144.0));\n    ach->setTXFrequency(Frequency::fromMHz(144.0));\n    base->channelList()->add(ach);\n\n    DMRChannel *dch = new DMRChannel();\n    dch->setName(\"DMR 0\");\n    dch->setRXFrequency(Frequency::fromMHz(144.0));\n    dch->setTXFrequency(Frequency::fromMHz(144.0));\n    dch->setContact(base->contacts()->contact(0)->as<DMRContact>());\n    dch->setGroupList(lst);\n    base->channelList()->add(dch);\n\n    Zone *zone = new Zone(\"Zone 0\");\n    zone->A()->add(ach);\n    zone->A()->add(dch);\n    base->zones()->add(zone);\n  }\n\n  merging->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 0\", 2345));\n  merging->contacts()->add(new DMRContact(DMRContact::GroupCall, \"ID 1\", 3456));\n\n  {\n    RXGroupList *lst = new RXGroupList(\"List 0\");\n    lst->addContact(merging->contacts()->contact(0)->as<DMRContact>());\n    merging->rxGroupLists()->add(lst);\n\n    FMChannel *ach = new FMChannel();\n    ach->setName(\"FM 0\");\n    ach->setRXFrequency(Frequency::fromMHz(145.0));\n    ach->setTXFrequency(Frequency::fromMHz(145.0));\n    merging->channelList()->add(ach);\n\n    DMRChannel *dch = new DMRChannel();\n    dch->setName(\"DMR 1\");\n    dch->setRXFrequency(Frequency::fromMHz(145.0));\n    dch->setTXFrequency(Frequency::fromMHz(145.0));\n    dch->setContact(merging->contacts()->contact(0)->as<DMRContact>());\n    dch->setGroupList(lst);\n    merging->channelList()->add(dch);\n\n    Zone *zone = new Zone(\"Zone 0\");\n    zone->A()->add(ach);\n    zone->A()->add(dch);\n    merging->zones()->add(zone);\n  }\n\n\n  ErrorStack err;\n  Config *merged = ConfigMerge::merge(base, merging,\n                                      ConfigMergeVisitor::ItemStrategy::Ignore,\n                                      ConfigMergeVisitor::SetStrategy::Ignore, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->channelList()->count(), 3);\n  QCOMPARE(merged->zones()->count(), 1);\n  QCOMPARE(merged->zones()->zone(0)->A()->count(), 2);\n  QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as<Channel>(),\n           merged->channelList()->channel(0));\n  QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as<Channel>(),\n           merged->channelList()->channel(1));\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Ignore,\n                              ConfigMergeVisitor::SetStrategy::Override, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->channelList()->count(), 3);\n  QCOMPARE(merged->zones()->count(), 1);\n  QCOMPARE(merged->zones()->zone(0)->A()->count(), 2);\n  QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as<Channel>(),\n           merged->channelList()->channel(0));\n  QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as<Channel>(),\n           merged->channelList()->channel(2));\n\n\n  merged = ConfigMerge::merge(base, merging,\n                              ConfigMergeVisitor::ItemStrategy::Ignore,\n                              ConfigMergeVisitor::SetStrategy::Merge, err);\n  if (nullptr == merged)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(merged->channelList()->count(), 3);\n  QCOMPARE(merged->zones()->count(), 1);\n  QCOMPARE(merged->zones()->zone(0)->A()->count(), 3);\n  QCOMPARE(merged->zones()->zone(0)->A()->get(0)->as<Channel>(),\n           merged->channelList()->channel(0));\n  QCOMPARE(merged->zones()->zone(0)->A()->get(1)->as<Channel>(),\n           merged->channelList()->channel(1));\n  QCOMPARE(merged->zones()->zone(0)->A()->get(2)->as<Channel>(),\n           merged->channelList()->channel(2));\n}\n\n\nQTEST_GUILESS_MAIN(MergeTest)\n"
  },
  {
    "path": "test/mergetest.hh",
    "content": "#ifndef MERGETEST_HH\n#define MERGETEST_HH\n\n#include <QObject>\n\nclass MergeTest : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit MergeTest(QObject *parent=nullptr);\n\nprivate slots:\n  void testMergeRadioIds();\n  void testMergeContacts();\n  void testMergeGroupLists();\n  void testMergeChannels();\n  void testMergeZones();\n};\n\n#endif // MERGETEST_HH\n"
  },
  {
    "path": "test/opengd77_test.cc",
    "content": "#include \"opengd77_test.hh\"\n#include \"opengd77_limits.hh\"\n#include \"config.hh\"\n#include \"opengd77_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n#include <QtEndian>\n#include \"logger.hh\"\n\n\nOpenGD77Test::OpenGD77Test(QObject *parent)\n  : UnitTestBase(parent), _stderr(stderr)\n{\n  Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG));\n}\n\n\nvoid\nOpenGD77Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  OpenGD77Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for OpenGD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\n\nbool\nOpenGD77Test::encodeDecode(Config &config, Config &decoded, const ErrorStack &err) {\n  OpenGD77Codeplug codeplug;\n  codeplug.clear();\n\n  Codeplug::Flags flags; flags.setUpdateCodeplug(false);\n  Config *intermediate = codeplug.preprocess(&config, err);\n  if (nullptr == intermediate) {\n    errMsg(err) << \"Cannot encode codeplug for OpenGD77.\";\n    return false;\n  }\n\n  if (! codeplug.encode(intermediate, flags, err)) {\n    errMsg(err) << \"Cannot encode codeplug for OpenGD77.\";\n    return false;\n  }\n  delete intermediate;\n\n  if (! codeplug.decode(&decoded, err)) {\n    errMsg(err) << \"Cannot decode codeplug for OpenGD77.\";\n    return false;\n  }\n\n  if (! codeplug.postprocess(&decoded, err)) {\n    errMsg(err) << \"Cannot decode codeplug for OpenGD77.\";\n    return false;\n  }\n\n  return true;\n}\n\n\nvoid\nOpenGD77Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  Config decoded;\n  if (! encodeDecode(_basicConfig, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n}\n\n\nvoid\nOpenGD77Test::testChannelFrequency() {\n  ErrorStack err;\n  Config config;\n\n  if (! encodeDecode(_channelFrequencyConfig, config, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\nvoid\nOpenGD77Test::testChannelGroupList() {\n  ErrorStack err;\n\n  Config config, decoded;\n  if (! config.readYAML(\":/data/config_test.yaml\", err))\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n\n  config.channelList()->channel(0)->as<DMRChannel>()->setGroupList(nullptr);\n  config.channelList()->channel(0)->as<DMRChannel>()->setContact(nullptr);\n  config.channelList()->channel(1)->as<DMRChannel>()->setContact(nullptr);\n  config.channelList()->channel(2)->as<DMRChannel>()->setGroupList(nullptr);\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QVERIFY(decoded.channelList()->channel(0)->as<DMRChannel>()->groupListRef()->isNull());\n  QVERIFY(decoded.channelList()->channel(0)->as<DMRChannel>()->contactRef()->isNull());\n\n  QVERIFY(! decoded.channelList()->channel(1)->as<DMRChannel>()->groupListRef()->isNull());\n  QVERIFY(decoded.channelList()->channel(1)->as<DMRChannel>()->contactRef()->isNull());\n\n  QVERIFY(decoded.channelList()->channel(2)->as<DMRChannel>()->groupListRef()->isNull());\n  QVERIFY(! decoded.channelList()->channel(2)->as<DMRChannel>()->contactRef()->isNull());\n\n  QVERIFY(! decoded.channelList()->channel(3)->as<DMRChannel>()->groupListRef()->isNull());\n  QVERIFY(decoded.channelList()->channel(3)->as<DMRChannel>()->contactRef()->isNull());\n}\n\n\nvoid\nOpenGD77Test::testChannelPowerSettings() {\n  ErrorStack err;\n\n  Config config, decoded;\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  config.channelList()->channel(0)->setDefaultPower();\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), true);\n\n  config.channelList()->channel(0)->setPower(Channel::Power::Low);\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), false);\n  QCOMPARE(decoded.channelList()->channel(0)->power(), Channel::Power::Low);\n\n  config.channelList()->channel(0)->setPower(Channel::Power::High);\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  QCOMPARE(decoded.channelList()->channel(0)->defaultPower(), false);\n  QCOMPARE(decoded.channelList()->channel(0)->power(), Channel::Power::High);\n}\n\n\nvoid\nOpenGD77Test::testOverrideChannelRadioId() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/config_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  // add another DMR ID and assign it to two channels\n  auto id2 = new DMRRadioID(\"Test\", 1234567);\n  config.radioIDs()->add(id2);\n  config.channelList()->channel(0)->as<DMRChannel>()->setRadioId(id2);\n  config.channelList()->channel(1)->as<DMRChannel>()->setRadioId(id2);\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.radioIDs()->count(), 2);\n  QVERIFY(! decoded.channelList()->channel(0)->as<DMRChannel>()->radioIdRef()->isNull());\n  QCOMPARE(decoded.channelList()->channel(0)->as<DMRChannel>()->radioId()->number(), 1234567);\n  QVERIFY(! decoded.channelList()->channel(1)->as<DMRChannel>()->radioIdRef()->isNull());\n  QCOMPARE(decoded.channelList()->channel(1)->as<DMRChannel>()->radioId()->number(), 1234567);\n}\n\n\nvoid\nOpenGD77Test::testChannelSubTones() {\n  ErrorStack err;\n  Config config, decoded;\n\n  char enc[] = {0x00, 0x10};\n  uint16_t dec = *(quint16 *)enc;\n  SelectiveCall decTone = OpenGD77BaseCodeplug::decodeSelectiveCall(\n        qFromLittleEndian(dec));\n  QVERIFY(decTone.isValid());\n  QVERIFY(decTone.isCTCSS());\n  QCOMPARE(decTone.Hz(), 100.0);\n\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  config.channelList()->channel(0)->as<FMChannel>()->setTXTone(SelectiveCall(67.0));\n  config.channelList()->channel(0)->as<FMChannel>()->setRXTone(SelectiveCall(123.0));\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  SelectiveCall txTone = decoded.channelList()->channel(0)->as<FMChannel>()->txTone(),\n      rxTone = decoded.channelList()->channel(0)->as<FMChannel>()->rxTone();\n\n  QVERIFY(txTone.isValid());\n  QVERIFY(txTone.isCTCSS());\n  QCOMPARE(txTone.Hz(), 67.0);\n\n  QVERIFY(rxTone.isValid());\n  QVERIFY(rxTone.isCTCSS());\n  QCOMPARE(rxTone.Hz(), 123.0);\n}\n\n\nvoid\nOpenGD77Test::testChannelFixedLocation() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  auto ext = new OpenGD77ChannelExtension();\n  ext->setLocator(\"JO62jl24\");\n  config.channelList()->channel(0)->setOpenGD77ChannelExtension(ext);\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QVERIFY(decoded.channelList()->channel(0)->openGD77ChannelExtension());\n  QCOMPARE(decoded.channelList()->channel(0)->openGD77ChannelExtension()->locator(), \"JO62jl24\");\n\n  ext->setLocator(\"JO59gw73\");\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  QCOMPARE(decoded.channelList()->channel(0)->openGD77ChannelExtension()->locator(), \"JO59gw73\");\n}\n\n\nvoid\nOpenGD77Test::testAPRSSourceCall() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.posSystems()->count(), 1);\n  auto sys = decoded.posSystems()->get(0)->as<FMAPRSSystem>();\n  QCOMPARE(sys->source(), \"DM3MAT\");\n\n  FMChannel *channel = decoded.posSystems()->get(0)->as<FMAPRSSystem>()\n                         ->revertChannelRef()->as<FMChannel>();\n  QVERIFY(channel->name() == \"2m APRS\");\n}\n\nvoid\nOpenGD77Test::testDTMFContacts() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/dtmf_contact.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.contacts()->count(), 1);\n  QVERIFY(decoded.contacts()->contact(0)->is<DTMFContact>());\n  auto cont = decoded.contacts()->contact(0)->as<DTMFContact>();\n  QCOMPARE(cont->name(), \"DTMF\");\n  QCOMPARE(cont->number(), \"*ABC123#\");\n}\n\n\nvoid\nOpenGD77Test::testChannelTransmitTimeout() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/fm_aprs_test.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  config.channelList()->channel(0)->setTimeout(Interval::fromSeconds(60));\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.channelList()->count(), 2);\n  QVERIFY(! decoded.channelList()->channel(0)->timeoutDisabled());\n  QCOMPARE(decoded.channelList()->channel(0)->timeout(), Interval::fromSeconds(60));\n  QVERIFY(decoded.channelList()->channel(1)->timeoutDisabled());\n}\n\n\nvoid\nOpenGD77Test::testConfigVerification() {\n  ErrorStack err;\n  Config config;\n\n  if (! config.readYAML(\":/data/opengd77_simple_config.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  RadioLimitContext ctx;\n  OpenGD77Limits().verifyConfig(&config, ctx);\n\n  QStringList messages;\n  for (int i=0; i<ctx.count(); i++)\n    messages.append(ctx.message(i).format());\n\n  if (RadioLimitIssue::Severity::Warning <= ctx.maxSeverity())\n    QFAIL(messages.join(\"; \").toLatin1().constData());\n}\n\n\nvoid\nOpenGD77Test::testBootMelody() {\n  uint8_t note[2];\n  OpenGD77BaseCodeplug::NoteElement elm(note);\n  elm.setPause(); QCOMPARE(note[0], 0u);\n  elm.setFrequency(110); QCOMPARE(note[0],1u);\n  elm.setFrequency(440); QCOMPARE(note[0],25u);\n  note[0] = 13; QCOMPARE(elm.frequency(), 220);\n  note[0] = 16; QCOMPARE(elm.frequency(), 261.62);\n  note[0] = 17; QCOMPARE(elm.frequency(), 277.18);\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/opengd77_boot_melody.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  QCOMPARE(config.settings()->tone()->bootMelody()->toLilypond(), \"c4 e g c e g c e g c e g c d e g c1\");\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.settings()->tone()->bootMelody()->toLilypond(), \"c4 e g c e g c e g c e g c d e g c1\");\n}\n\n\nvoid\nOpenGD77Test::testBootMelodyPauses() {\n  ErrorStack err;\n  Config config, decoded;\n\n  if (! config.readYAML(\":/data/opengd77_boot_melody.yaml\", err)) {\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toLocal8Bit().constData());\n  }\n\n  config.settings()->tone()->bootMelody()->fromLilypond(\"c4 r16 c4 g4 e4 r4 c16 g16 e16 r16 c4\");\n\n  if (! encodeDecode(config, decoded, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(decoded.settings()->tone()->bootMelody()->toLilypond(), \"c4 r16 c4 g e r c16 g e r c4\");\n}\n\n\nQTEST_GUILESS_MAIN(OpenGD77Test)\n\n"
  },
  {
    "path": "test/opengd77_test.hh",
    "content": "#ifndef OPENGD77TEST_HH\n#define OPENGD77TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n#include <QObject>\n#include <QTextStream>\n#include \"config.hh\"\n#include \"errorstack.hh\"\n\n\nclass OpenGD77Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit OpenGD77Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n\n  /** Regression test for #539. */\n  void testChannelGroupList();\n  /** Regression test for #507 */\n  void testChannelPowerSettings();\n  /** Regression test for #541. */\n  void testOverrideChannelRadioId();\n  /** Regression test for #554. */\n  void testAPRSSourceCall();\n  /** Regression test for #549. */\n  void testChannelSubTones();\n  /** Regression test for #556. */\n  void testChannelFixedLocation();\n  /** Regression test for #655. */\n  void testDTMFContacts();\n  /** Regression test for #697. */\n  void testChannelTransmitTimeout();\n  /** Regression test for #856. */\n  void testConfigVerification();\n  void testBootMelody();\n  /** Regression tests for #878. */\n  void testBootMelodyPauses();\n\nprotected:\n  static bool encodeDecode(Config &config, Config &decoded, const ErrorStack &err=ErrorStack());\n\nprotected:\n  QTextStream _stderr;\n};\n\n#endif // OPENGD77TEST_HH\n"
  },
  {
    "path": "test/openuv380_test.cc",
    "content": "#include \"openuv380_test.hh\"\n#include \"config.hh\"\n#include \"openuv380_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <QTest>\n#include \"logger.hh\"\n\n\nOpenUV380Test::OpenUV380Test(QObject *parent)\n  : UnitTestBase(parent), _stderr(stderr)\n{\n  Logger::get().addHandler(new StreamLogHandler(_stderr, LogMessage::DEBUG));\n}\n\nvoid\nOpenUV380Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  OpenUV380Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for OpenGD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nOpenUV380Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  OpenUV380Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for OpenGD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for OpenGD77: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nOpenUV380Test::testChannelFrequency() {\n  ErrorStack err;\n  OpenUV380Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for OpenGD77: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity RD5R: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\n\n\nQTEST_GUILESS_MAIN(OpenUV380Test)\n\n"
  },
  {
    "path": "test/openuv380_test.hh",
    "content": "#ifndef OPENUV380TEST_HH\n#define OPENUV380TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n#include <QObject>\n#include <QTextStream>\n#include \"config.hh\"\n\nclass OpenUV380Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit OpenUV380Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n\nprotected:\n  QTextStream _stderr;\n};\n\n#endif // OPENUV380TEST_HH\n"
  },
  {
    "path": "test/rd5r_test.cc",
    "content": "#include \"rd5r_test.hh\"\n#include \"config.hh\"\n#include \"rd5r.hh\"\n#include \"rd5r_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nRD5RTest::RD5RTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nRD5RTest::testBasicConfigEncoding() {\n  ErrorStack err;\n  RD5RCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nRD5RTest::testBasicConfigDecoding() {\n  ErrorStack err;\n  RD5RCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nRD5RTest::testChannelFrequency() {\n  ErrorStack err;\n  RD5RCodeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  /*QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           123456780ULL);\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           1234567890ULL);*/\n}\n\nvoid\nRD5RTest::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  RD5RCodeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for Radioddity RD5R: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\n\nQTEST_GUILESS_MAIN(RD5RTest)\n\n"
  },
  {
    "path": "test/rd5r_test.hh",
    "content": "#ifndef RD5RTEST_HH\n#define RD5RTEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\nclass RD5RTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit RD5RTest(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testSMSTemplates();\n};\n\n#endif // RD5RTEST_HH\n"
  },
  {
    "path": "test/resources.qrc",
    "content": "<RCC>\n    <qresource prefix=\"/\">\n        <file>data/config_test.yaml</file>\n        <file>data/channel_frequency_test.yaml</file>\n        <file>data/anytone_auto_repeater_extension.yaml</file>\n        <file>data/audio_settings_extension.yaml</file>\n        <file>data/roaming_channel_test.yaml</file>\n        <file>data/anytone_call_hangtime.yaml</file>\n        <file>data/multiple_radio_ids.yaml</file>\n        <file>data/ctcss_copy_test.yaml</file>\n        <file>data/anytone_key_function.yaml</file>\n        <file>data/chirp_simple.csv</file>\n        <file>data/chirp_cross.csv</file>\n        <file>data/chirp_ctcss.csv</file>\n        <file>data/chirp_dcs.csv</file>\n        <file>data/fm_aprs_test.yaml</file>\n        <file>data/ctcss_null_test.yaml</file>\n        <file>data/chirp_bandwidth.csv</file>\n        <file>data/basic_encryption.yaml</file>\n        <file>data/aes_encryption.yaml</file>\n        <file>data/arc4_encryption.yaml</file>\n        <file>data/dtmf_contact.yaml</file>\n        <file>data/am_channel_test.yaml</file>\n        <file>data/anytone_channel_data_ack.yaml</file>\n        <file>data/anytone_settings_display.yaml</file>\n        <file>data/anytone_settings_roaming.yaml</file>\n        <file>data/opengd77_simple_config.yaml</file>\n        <file>data/opengd77_boot_melody.yaml</file>\n    </qresource>\n</RCC>\n"
  },
  {
    "path": "test/smstemplatetest.cc",
    "content": "#include \"smstemplatetest.hh\"\n\nSMSTemplateTest::SMSTemplateTest(QObject *parent)\n  : UnitTestBase{parent}\n{\n  // pass...\n}\n\nvoid\nSMSTemplateTest::serializationTest() {\n  Config config;\n\n  auto msg1 = new SMSTemplate();\n  msg1->setName(\"Message 1\");\n  msg1->setMessage(\"Some message\");\n  config.smsExtension()->smsTemplates()->add(msg1);\n\n  ErrorStack err;\n  QString buffer;\n  QTextStream stream(&buffer);\n  if (! config.toYAML(stream, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  Config comp_config;\n  Config::Context ctx;\n  YAML::Node doc = YAML::Load(buffer.toStdString());\n  if (! comp_config.parse(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  if (! comp_config.link(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(comp_config.smsExtension()->smsTemplates()->count(),\n           config.smsExtension()->smsTemplates()->count());\n  QCOMPARE(comp_config.smsExtension()->smsTemplates()->message(0)->name(),\n           config.smsExtension()->smsTemplates()->message(0)->name());\n  QCOMPARE(comp_config.smsExtension()->smsTemplates()->message(0)->message(),\n           config.smsExtension()->smsTemplates()->message(0)->message());\n}\n\n\nvoid\nSMSTemplateTest::testMessageDuplication() {\n  Config config;\n\n  {\n    auto msg1 = new SMSTemplate();\n    msg1->setName(\"Message 1\");\n    msg1->setMessage(\"Some message\");\n    config.smsExtension()->smsTemplates()->add(msg1);\n  }\n\n  ErrorStack err;\n  QString buffer;\n  QTextStream stream(&buffer);\n  if (! config.toYAML(stream, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  config.clear();\n  Config::Context ctx;\n  YAML::Node doc = YAML::Load(buffer.toStdString());\n  if (! config.parse(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n  if (! config.link(doc, ctx, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(config.smsExtension()->smsTemplates()->count(), 1);\n  QCOMPARE(config.smsExtension()->smsTemplates()->message(0)->name(), \"Message 1\");\n  QCOMPARE(config.smsExtension()->smsTemplates()->message(0)->message(), \"Some message\");\n}\n\nQTEST_GUILESS_MAIN(SMSTemplateTest)\n"
  },
  {
    "path": "test/smstemplatetest.hh",
    "content": "#ifndef SMSTEMPLATETEST_HH\n#define SMSTEMPLATETEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\nclass SMSTemplateTest : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit SMSTemplateTest(QObject *parent=nullptr);\n\nprivate slots:\n  void serializationTest();\n\n  /** Regression test for #511. */\n  void testMessageDuplication();\n\n};\n\n#endif // SMSTEMPLATETEST_HH\n"
  },
  {
    "path": "test/tableformattest.cc",
    "content": "#include \"tableformattest.hh\"\n#include \"config.hh\"\n#include \"csvreader.hh\"\n#include <QTest>\n\n\nTableFormatTest::TableFormatTest(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\nvoid\nTableFormatTest::testFrequencyParser() {\n  Config config;\n\n  QString data;\n  QTextStream stream(&data);\n\n  stream << \"ID: 2621370\" << Qt::endl\n         << \"Name: \\\"DM3MAT\\\"\" << Qt::endl\n         << \"Digital Name       Receive    Transmit   Power Scan TOT RO Admit  CC TS RxGL TxC GPS Roam ID\" << Qt::endl\n         << \"1       \\\"test 0\\\" 439.56250  -7.60000   High  -    -   -  Color  1  1  -    -   -   +    -  # Local\" << Qt::endl\n         << \"2       \\\"test 1\\\" 439.56250  +7.60000   High  -    -   -  Color  1  1  -    -   -   +    -  # Sa/Th\" << Qt::endl\n         << \"3       \\\"test 2\\\" 439.56250 439.56250   High  -    -   -  Color  1  2  -    -   -   +    -  # Regional\" << Qt::endl;\n\n  QString errMsg;\n  QVERIFY2(CSVReader::read(&config, stream, errMsg), errMsg.toStdString().c_str());\n\n  QCOMPARE(config.channelList()->count(), 3);\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(), Frequency::fromMHz(439.56250));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(), Frequency::fromMHz(431.96250));\n  QCOMPARE(config.channelList()->channel(1)->txFrequency(), Frequency::fromMHz(447.16250));\n  QCOMPARE(config.channelList()->channel(2)->txFrequency(), Frequency::fromMHz(439.56250));\n}\n\nQTEST_GUILESS_MAIN(TableFormatTest)\n\n\n"
  },
  {
    "path": "test/tableformattest.hh",
    "content": "#ifndef TABLEFORMATTEST_HH\n#define TABLEFORMATTEST_HH\n\n#include <QObject>\n\n\nclass TableFormatTest : public QObject\n{\n  Q_OBJECT\n\npublic:\n  explicit TableFormatTest(QObject *parent = nullptr);\n\nprivate slots:\n  void testFrequencyParser();\n};\n\n\n#endif // TABLEFORMATTEST_HH\n"
  },
  {
    "path": "test/trafotest.cc",
    "content": "#include \"trafotest.hh\"\n#include \"intermediaterepresentation.hh\"\n#include \"configcopyvisitor.hh\"\n\nTrafoTest::TrafoTest(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nTrafoTest::testZoneSplitVisitor() {\n  ErrorStack err;\n  Config *copy = ConfigCopy::copy(&_basicConfig, err)->as<Config>();\n  if (nullptr == copy)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(copy, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(copy->zones()->count(), 4);\n  QVERIFY(copy->zones()->get(0)->name().endsWith(\" A\"));\n  QVERIFY(copy->zones()->get(1)->name().endsWith(\" B\"));\n  QCOMPARE(copy->zones()->get(0)->name().chopped(2),\n           copy->zones()->get(1)->name().chopped(2));\n  QCOMPARE(copy->zones()->get(0)->as<Zone>()->A()->count(), 3);\n  QCOMPARE(copy->zones()->get(1)->as<Zone>()->A()->count(), 1);\n}\n\n\nvoid\nTrafoTest::testZoneMergeVisitor() {\n  ErrorStack err;\n  Config *copy = ConfigCopy::copy(&_basicConfig, err)->as<Config>();\n  if (nullptr == copy)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  ZoneSplitVisitor splitter;\n  if (! splitter.process(copy, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  ZoneMergeVisitor merger;\n  if (! merger.process(copy, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  QCOMPARE(_basicConfig.compare(*copy), 0);\n}\n\n\nvoid\nTrafoTest::testListElementRemoval() {\n  ErrorStack err;\n  Config *copy = ConfigCopy::copy(&_basicConfig, err)->as<Config>();\n  if (nullptr == copy)\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  ObjectFilterVisitor filter({DMRAPRSSystem::staticMetaObject});\n  if (! filter.process(copy, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  // Check filter\n  QCOMPARE(copy->posSystems()->count(), 0);\n  // Check references to deleted objects\n  QCOMPARE(copy->channelList()->channel(1)->as<DMRChannel>()->aprs(), nullptr);\n}\n\nvoid\nTrafoTest::testPropertyRemoval() {\n  ErrorStack err;\n  Config config;\n  if (! config.readYAML(\":/data/anytone_call_hangtime.yaml\", err))\n    QFAIL(QString(\"Cannot open codeplug file: %1\")\n          .arg(err.format()).toStdString().c_str());\n  QVERIFY2(config.settings()->anytoneExtension(), \"Expected AnyTone settings extension.\");\n\n  ObjectFilterVisitor filter({AnytoneSettingsExtension::staticMetaObject});\n  if (! filter.process(&config, err))\n    QFAIL(err.format().toLocal8Bit().constData());\n\n  // Check filter\n  auto settings = config.settings();\n  auto anytoneExt = settings->anytoneExtension();\n  QVERIFY(anytoneExt == nullptr);\n}\n\nQTEST_GUILESS_MAIN(TrafoTest)\n"
  },
  {
    "path": "test/trafotest.hh",
    "content": "#ifndef TRAFOTEST_HH\n#define TRAFOTEST_HH\n\n#include \"libdmrconfigtest.hh\"\n\nclass TrafoTest: public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit TrafoTest(QObject *parent=nullptr);\n\nprivate slots:\n  void testZoneSplitVisitor();\n  void testZoneMergeVisitor();\n  void testListElementRemoval();\n  void testPropertyRemoval();\n};\n\n#endif // TRAFOTEST_HH\n"
  },
  {
    "path": "test/utilstest.cc",
    "content": "#include \"utilstest.hh\"\n\n#include <QTest>\n#include <QtEndian>\n#include \"utils.hh\"\n#include \"frequency.hh\"\n#include \"chirpformat.hh\"\n#include \"config.hh\"\n\n\nUtilsTest::UtilsTest(QObject *parent)\n  : QObject{parent}\n{\n  // pass...\n}\n\nvoid\nUtilsTest::initTestCase() {\n}\n\nvoid\nUtilsTest::testDecodeUnicode() {\n  QString testString(\"abc123äöü\");\n  QCOMPARE(decode_unicode((uint16_t *)testString.data(), testString.size()), testString);\n}\n\nvoid\nUtilsTest::testEncodeUnicode() {\n  QString testString(\"abc123äöü\");\n  QByteArray bufferTrue(32, 0x00), bufferTest(32, 0xff);\n  memcpy(bufferTrue.data(), testString.data(), 18);\n  encode_unicode((uint16_t *)bufferTest.data(), testString, 16);\n  QCOMPARE(bufferTest, bufferTrue);\n}\n\nvoid\nUtilsTest::testEncodeASCII() {\n  const char *testString = \"abc\";\n  QByteArray bufferTrue(16, 0xff), bufferTest(16,0x00);\n  memcpy(bufferTrue.data(), testString, 3);\n  encode_ascii((uint8_t *)bufferTest.data(), testString, 16, 0xff);\n  QCOMPARE(bufferTest, bufferTrue);\n}\n\nvoid\nUtilsTest::testDecodeASCII() {\n  const char *testString = \"abc\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\";\n  QString res = decode_ascii((const uint8_t *)testString, 16, 0xff);\n  QCOMPARE(res, QString(\"abc\"));\n}\n\nvoid\nUtilsTest::testDecodeFrequency() {\n  uint32_t bcd = 0x12345678U;\n  double freq  = 123.45678;\n  QCOMPARE(decode_frequency(bcd), freq);\n}\n\nvoid\nUtilsTest::testEncodeFrequency() {\n  uint32_t bcd = 0x12345678U;\n  double freq  = 123.45678;\n  QCOMPARE(encode_frequency(freq), bcd);\n\n  QCOMPARE(decode_frequency(encode_frequency(439.5630)), 439.5630);\n}\n\nvoid\nUtilsTest::testDecodeDMRID_bcd() {\n  uint8_t bcd[4] = {0x12, 0x34, 0x56, 0x78};\n  uint32_t num = 12345678U;\n  QCOMPARE(decode_dmr_id_bcd((uint8_t *)&bcd), num);\n}\n\nvoid\nUtilsTest::testEncodeDMRID_bcd() {\n  const char bcd[4] = {0x12, 0x34, 0x56, 0x78};\n  uint32_t num = 12345678U;\n  QByteArray res(4, 0);\n  encode_dmr_id_bcd((uint8_t *)res.data(), num);\n  QCOMPARE(res, QByteArray(bcd, 4));\n}\n\nvoid\nUtilsTest::testFrequencyParser() {\n  QCOMPARE(Frequency::fromString(\"100Hz\").inHz(), 100ULL);\n  QCOMPARE(Frequency::fromString(\"100 Hz\").inHz(), 100ULL);\n  QCOMPARE(Frequency::fromString(\"100kHz\").inHz(), 100000ULL);\n  QCOMPARE(Frequency::fromString(\"100 kHz\").inHz(), 100000ULL);\n  QCOMPARE(Frequency::fromString(\"100MHz\").inHz(), 100000000ULL);\n  QCOMPARE(Frequency::fromString(\"100 MHz\").inHz(), 100000000ULL);\n  QCOMPARE(Frequency::fromString(\"100GHz\").inHz(), 100000000000ULL);\n  QCOMPARE(Frequency::fromString(\"100 GHz\").inHz(), 100000000000ULL);\n\n  QCOMPARE(Frequency::fromString(\"100\").inHz(), 100000000ULL);\n  QCOMPARE(Frequency::fromString(\"100.0\").inHz(), 100000000ULL);\n}\n\n\nvoid\nUtilsTest::testLocator() {\n  QGeoCoordinate coor;\n\n  coor = loc2deg(\"JO62\");\n  QVERIFY(coor.isValid());\n  QCOMPARE(deg2loc(coor, 4), \"JO62\");\n\n  coor = loc2deg(\"JO62jl\");\n  QVERIFY(coor.isValid());\n  QCOMPARE(deg2loc(coor, 6), \"JO62jl\");\n\n  coor = loc2deg(\"JO62jl55\");\n  QVERIFY(coor.isValid());\n  QCOMPARE(deg2loc(coor, 8), \"JO62jl55\");\n\n  coor = loc2deg(\"JO62jl55jj\");\n  QVERIFY(coor.isValid());\n  QCOMPARE(deg2loc(coor, 10), \"JO62jl55jj\");\n}\n\n\nvoid\nUtilsTest::testEndianess() {\n  char val1le[] = {0x34, 0x12}, val1be[] = {0x12,0x34};\n\n  QCOMPARE(qFromLittleEndian(*(quint16 *)val1le), 0x1234);\n  QCOMPARE(qFromBigEndian(*(quint16 *)val1be), 0x1234);\n\n  char val2le[] = {0x78, 0x56, 0x34, 0x12}, val2be[] = {0x12, 0x34, 0x56, 0x78};\n\n  QCOMPARE(qFromLittleEndian(*(quint32 *)val2le), 0x12345678);\n  QCOMPARE(qFromBigEndian(*(quint32 *)val2be), 0x12345678);\n}\n\n\nvoid\nUtilsTest::testFrequencyNearestMap() {\n  auto map = Frequency::MapNearest<unsigned int>(\n        {\n          {Frequency::fromHz(100), 0},\n          {Frequency::fromHz(200), 1},\n          {Frequency::fromHz(300), 2},\n          {Frequency::fromHz(400), 3},\n        });\n\n  QCOMPARE(map.key(Frequency::fromHz(10)),  Frequency::fromHz(100));\n  QCOMPARE(map.key(Frequency::fromHz(100)), Frequency::fromHz(100));\n  QCOMPARE(map.key(Frequency::fromHz(110)), Frequency::fromHz(100));\n  QCOMPARE(map.key(Frequency::fromHz(160)), Frequency::fromHz(200));\n  QCOMPARE(map.key(Frequency::fromHz(200)), Frequency::fromHz(200));\n  QCOMPARE(map.key(Frequency::fromHz(210)), Frequency::fromHz(200));\n  QCOMPARE(map.key(Frequency::fromHz(360)), Frequency::fromHz(400));\n  QCOMPARE(map.key(Frequency::fromHz(400)), Frequency::fromHz(400));\n  QCOMPARE(map.key(Frequency::fromHz(410)), Frequency::fromHz(400));\n\n  QCOMPARE(map.value(Frequency::fromHz(10)),  0);\n  QCOMPARE(map.value(Frequency::fromHz(100)), 0);\n  QCOMPARE(map.value(Frequency::fromHz(110)), 0);\n  QCOMPARE(map.value(Frequency::fromHz(160)), 1);\n  QCOMPARE(map.value(Frequency::fromHz(200)), 1);\n  QCOMPARE(map.value(Frequency::fromHz(210)), 1);\n  QCOMPARE(map.value(Frequency::fromHz(360)), 3);\n  QCOMPARE(map.value(Frequency::fromHz(400)), 3);\n  QCOMPARE(map.value(Frequency::fromHz(410)), 3);\n}\n\n\nQTEST_GUILESS_MAIN(UtilsTest)\n"
  },
  {
    "path": "test/utilstest.hh",
    "content": "#ifndef UTILSTEST_HH\n#define UTILSTEST_HH\n\n#include <QObject>\n\nclass UtilsTest : public QObject\n{\n  Q_OBJECT\npublic:\n  explicit UtilsTest(QObject *parent = nullptr);\n\nprivate slots:\n  void initTestCase();\n  void testDecodeUnicode();\n  void testEncodeUnicode();\n  void testDecodeASCII();\n  void testEncodeASCII();\n  void testDecodeFrequency();\n  void testEncodeFrequency();\n  void testDecodeDMRID_bcd();\n  void testEncodeDMRID_bcd();\n  void testFrequencyParser();\n  void testLocator();\n  void testEndianess();\n  void testFrequencyNearestMap();\n};\n\n#endif // UTILSTEST_HH\n"
  },
  {
    "path": "test/uv390_test.cc",
    "content": "#include \"uv390_test.hh\"\n#include \"config.hh\"\n#include \"uv390_codeplug.hh\"\n#include \"errorstack.hh\"\n#include <iostream>\n#include <QTest>\n\nUV390Test::UV390Test(QObject *parent)\n  : UnitTestBase(parent)\n{\n  // pass...\n}\n\nvoid\nUV390Test::testBasicConfigEncoding() {\n  ErrorStack err;\n  UV390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT UV390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nUV390Test::testBasicConfigDecoding() {\n  ErrorStack err;\n  UV390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_basicConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT UV390: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT UV390: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n}\n\nvoid\nUV390Test::testChannelFrequency() {\n  ErrorStack err;\n  UV390Codeplug codeplug;\n  codeplug.clear();\n  if (! codeplug.encode(&_channelFrequencyConfig, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD-UV390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config config;\n  if (! codeplug.decode(&config, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD-UV390: {}\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(config.channelList()->channel(0)->rxFrequency(),\n           Frequency::fromHz(123456780ULL));\n  QCOMPARE(config.channelList()->channel(0)->txFrequency(),\n           Frequency::fromHz(999999990ULL));\n}\n\nvoid\nUV390Test::testSMSTemplates() {\n  Config config;\n  config.radioIDs()->add(new DMRRadioID(\"ID\", 1234567));\n  SMSTemplate *sms0 = new SMSTemplate(); sms0->setName(\"SMS0\"); sms0->setMessage(\"ABC\");\n  SMSTemplate *sms1 = new SMSTemplate(); sms1->setName(\"SMS1\"); sms1->setMessage(\"XYZ\");\n  config.smsExtension()->smsTemplates()->add(sms0);\n  config.smsExtension()->smsTemplates()->add(sms1);\n\n  ErrorStack err;\n  UV390Codeplug codeplug;\n  if (! codeplug.encode(&config, Codeplug::Flags(), err)) {\n    QFAIL(QString(\"Cannot encode codeplug for TyT MD-UV390: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  Config decoded;\n  if (! codeplug.decode(&decoded, err)) {\n    QFAIL(QString(\"Cannot decode codeplug for TyT MD-UV390: %1\")\n          .arg(err.format()).toStdString().c_str());\n  }\n\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->count(), 2);\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(0)->name(), \"SMS0\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(0)->message(), \"ABC\");\n  //QCOMPARE_NE(decoded.smsExtension()->smsTemplates()->message(1)->name(), \"SMS1\");\n  QCOMPARE(decoded.smsExtension()->smsTemplates()->message(1)->message(), \"XYZ\");\n}\n\n\nQTEST_GUILESS_MAIN(UV390Test)\n\n"
  },
  {
    "path": "test/uv390_test.hh",
    "content": "#ifndef UV390TEST_HH\n#define UV390TEST_HH\n\n\n#include \"libdmrconfigtest.hh\"\n\nclass UV390Test : public UnitTestBase\n{\n  Q_OBJECT\n\npublic:\n  explicit UV390Test(QObject *parent = nullptr);\n\nprivate slots:\n  void testBasicConfigEncoding();\n  void testBasicConfigDecoding();\n  void testChannelFrequency();\n  void testSMSTemplates();\n};\n\n#endif // UV390TEST_HH\n"
  }
]